@erclx/aitk 0.104.1 → 0.106.0

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.
Files changed (69) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/claude-address-review/SKILL.md +4 -3
  3. package/claude/skills/claude-design-extract/SKILL.md +3 -3
  4. package/claude/skills/claude-docs/SKILL.md +1 -1
  5. package/claude/skills/claude-groundwork/SKILL.md +3 -2
  6. package/claude/skills/claude-memory-capture/SKILL.md +2 -2
  7. package/claude/skills/claude-pr-review/SKILL.md +1 -1
  8. package/claude/skills/claude-standards-audit/SKILL.md +8 -6
  9. package/claude/skills/claude-worktree/SKILL.md +6 -1
  10. package/claude/skills/create-skill/SKILL.md +2 -2
  11. package/claude/skills/create-snippet/SKILL.md +2 -2
  12. package/claude/skills/create-snippet/references/snippets.md +2 -2
  13. package/claude/skills/create-standard/SKILL.md +2 -2
  14. package/claude/skills/docs-sync/SKILL.md +2 -2
  15. package/claude/skills/git-issue/SKILL.md +2 -2
  16. package/claude/skills/git-issue/references/issue.md +2 -2
  17. package/claude/skills/git-pr/SKILL.md +2 -2
  18. package/claude/skills/git-pr/references/pr.md +2 -2
  19. package/claude/skills/git-split/references/pr.md +2 -2
  20. package/claude/skills/git-stage/SKILL.md +2 -2
  21. package/claude/skills/migration-standards/SKILL.md +1 -1
  22. package/claude/skills/setup-indexes/SKILL.md +1 -1
  23. package/claude/skills/write-human/REQUIREMENT.md +46 -0
  24. package/claude/skills/write-human/SKILL.md +68 -0
  25. package/claude/skills/write-human/references/density.md +38 -0
  26. package/claude/skills/write-human/references/machine-tells.md +107 -0
  27. package/claude/skills/write-human/references/source-material.md +37 -0
  28. package/docs/agents/commands.md +2 -1
  29. package/docs/agents/index.md +1 -0
  30. package/docs/agents/markdown-audit.md +7 -7
  31. package/docs/agents/test-order.md +56 -0
  32. package/docs/ai-workflow.md +3 -0
  33. package/docs/target-projects.md +1 -1
  34. package/governance/rules/claude/500-prose.md +7 -5
  35. package/governance/rules/claude/501-markdown.md +5 -4
  36. package/governance/rules/core/070-planning.md +1 -0
  37. package/package.json +1 -1
  38. package/scripts/core/install-check.sh +1 -1
  39. package/src/commands/gov.ts +135 -0
  40. package/src/commands/markdown.ts +2 -2
  41. package/src/comments/vocabulary.ts +1 -1
  42. package/src/gov/test-order.ts +383 -0
  43. package/src/markdown/bans.ts +2 -2
  44. package/src/standards/closure.ts +1 -1
  45. package/standards/bundled/issue.md +2 -2
  46. package/standards/bundled/pr.md +2 -2
  47. package/standards/bundled/snippets.md +2 -2
  48. package/standards/diagrams.md +5 -5
  49. package/standards/glossary.md +2 -2
  50. package/standards/groundwork.md +6 -2
  51. package/standards/index.md +1 -2
  52. package/standards/intake.md +2 -2
  53. package/standards/markdown.md +55 -6
  54. package/standards/memory.md +2 -2
  55. package/standards/plan.md +2 -2
  56. package/standards/publish.md +2 -2
  57. package/standards/readme.md +4 -4
  58. package/standards/skill.md +2 -2
  59. package/standards/standard.md +2 -2
  60. package/standards/teach.md +2 -2
  61. package/standards/versioning.md +2 -2
  62. package/standards/wireframes.md +3 -3
  63. package/tooling/astro/manifest.toml +5 -5
  64. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +2 -2
  65. package/tooling/vite-react/manifest.toml +4 -4
  66. package/tooling/web/configs/scripts/worktree-port.sh +44 -6
  67. package/tooling/web/manifest.toml +3 -3
  68. package/tooling/web/reference.md +4 -1
  69. package/standards/prose.md +0 -89
@@ -0,0 +1,383 @@
1
+ import { execaSync } from 'execa'
2
+ import { gitEnv } from '@/git-env'
3
+
4
+ /**
5
+ * Preferred first, matching `src/tasks/trunk.ts`. A clone with no remote still
6
+ * answers off its local trunk, and a local `main` trailing the remote widens
7
+ * the range rather than narrowing it, which over-reports instead of hiding.
8
+ */
9
+ const TRUNK_REFS = ['origin/main', 'main'] as const
10
+
11
+ /**
12
+ * The extensions this check can pair. A test sits beside its subject under one
13
+ * name throughout this corpus, so the weakest assumption available is also the
14
+ * one the corpus already honors. Every other extension is read past and named,
15
+ * since a bash script and its test are related by nothing a filename carries.
16
+ */
17
+ export const SOURCE_EXTENSIONS = ['.ts', '.tsx'] as const
18
+
19
+ /** The suffix removed to derive the subject a test covers. */
20
+ export const TEST_SUFFIXES = ['.test.ts', '.test.tsx'] as const
21
+
22
+ /** A `%H` line, which is 40 hex characters under sha1 and 64 under sha256. */
23
+ const COMMIT = /^[0-9a-f]{40,64}$/
24
+
25
+ /** A `--name-status` line: a status letter, a tab, then one path. */
26
+ const NAME_STATUS = /^([A-Z])\d*\t(.+)$/
27
+
28
+ export type Verdict = 'satisfied' | 'implementation-first' | 'unclassified'
29
+
30
+ export interface PairRecord {
31
+ /** The implementation path, which is what a subject is named by. */
32
+ readonly subject: string
33
+ /** The test paired to it, or `null` when the pairing found no partner. */
34
+ readonly test: string | null
35
+ readonly verdict: Verdict
36
+ readonly implementationCommit: string | null
37
+ readonly testCommit: string | null
38
+ /** Why the verdict reads the way it does, in the report's own words. */
39
+ readonly reason: string
40
+ }
41
+
42
+ export interface Scope {
43
+ readonly extensions: readonly string[]
44
+ readonly testSuffixes: readonly string[]
45
+ }
46
+
47
+ export type TestOrderReport =
48
+ | {
49
+ readonly kind: 'measured'
50
+ readonly base: string
51
+ readonly head: string
52
+ readonly scope: Scope
53
+ readonly satisfied: readonly PairRecord[]
54
+ readonly findings: readonly PairRecord[]
55
+ readonly unclassified: readonly PairRecord[]
56
+ /** Changed paths outside the pairing's reach, named rather than counted. */
57
+ readonly ignored: readonly string[]
58
+ }
59
+ | { readonly kind: 'unreadable'; readonly reason: string }
60
+
61
+ export interface TestOrderOptions {
62
+ /** The far side of the range, defaulting to the merge base against the trunk. */
63
+ readonly base?: string
64
+ }
65
+
66
+ /** One path as one commit in the range touched it. */
67
+ interface Change {
68
+ readonly path: string
69
+ readonly status: string
70
+ readonly commit: string
71
+ /** Position in the range, oldest first, which is what orders a pair. */
72
+ readonly order: number
73
+ }
74
+
75
+ /**
76
+ * Reads `--name-status` log output, oldest commit first, into one entry per
77
+ * path per commit.
78
+ *
79
+ * The order field rather than the commit is what a comparison reads, since two
80
+ * commits carry no ordering a caller can derive from their hashes alone.
81
+ */
82
+ function parseChanges(output: string): Change[] {
83
+ const changes: Change[] = []
84
+ let commit = ''
85
+ let order = -1
86
+
87
+ for (const line of output.split('\n')) {
88
+ const trimmed = line.trimEnd()
89
+ if (trimmed === '') continue
90
+
91
+ if (COMMIT.test(trimmed)) {
92
+ commit = trimmed
93
+ order += 1
94
+ continue
95
+ }
96
+
97
+ const match = NAME_STATUS.exec(trimmed)
98
+ if (match === null || commit === '') continue
99
+
100
+ changes.push({ path: match[2], status: match[1], commit, order })
101
+ }
102
+
103
+ return changes
104
+ }
105
+
106
+ /** Whether a path is a test this check can derive a subject from. */
107
+ function testSuffix(path: string): string | undefined {
108
+ return TEST_SUFFIXES.find((suffix) => path.endsWith(suffix))
109
+ }
110
+
111
+ /**
112
+ * The implementation a test covers, derived by removing the test suffix.
113
+ *
114
+ * A behavior split across two modules pairs wrongly or not at all under this,
115
+ * which is what the unclassified bucket exists to catch rather than hide.
116
+ */
117
+ function subjectOf(path: string): string {
118
+ const suffix = testSuffix(path)
119
+ if (suffix === undefined) return path
120
+ return `${path.slice(0, -suffix.length)}${suffix.replace('.test', '')}`
121
+ }
122
+
123
+ /** The one test path that would cover `subject` under the beside convention. */
124
+ function testPathFor(subject: string): string {
125
+ return subject.replace(/\.(tsx?)$/, '.test.$1')
126
+ }
127
+
128
+ /**
129
+ * Whether a path is an implementation this check can pair. A declaration file
130
+ * carries no behavior to test, so it is read past rather than reported as a
131
+ * module nothing covers.
132
+ */
133
+ function isImplementation(path: string): boolean {
134
+ if (path.endsWith('.d.ts')) return false
135
+ if (testSuffix(path) !== undefined) return false
136
+ return SOURCE_EXTENSIONS.some((extension) => path.endsWith(extension))
137
+ }
138
+
139
+ /** The first commit in the range that added `path`, or `undefined`. */
140
+ function introduction(
141
+ changes: readonly Change[],
142
+ path: string,
143
+ ): Change | undefined {
144
+ return changes.find((change) => change.path === path && change.status === 'A')
145
+ }
146
+
147
+ function classify(
148
+ changes: readonly Change[],
149
+ atBase: ReadonlySet<string>,
150
+ ): {
151
+ satisfied: PairRecord[]
152
+ findings: PairRecord[]
153
+ unclassified: PairRecord[]
154
+ } {
155
+ const satisfied: PairRecord[] = []
156
+ const findings: PairRecord[] = []
157
+ const unclassified: PairRecord[] = []
158
+
159
+ // Keyed by subject so a module whose implementation and test both moved is
160
+ // one record rather than two, and so a test with no partner still reports
161
+ // under the implementation path a reader would go looking for.
162
+ const subjects = new Set<string>()
163
+ for (const change of changes) {
164
+ if (isImplementation(change.path)) subjects.add(change.path)
165
+ else if (testSuffix(change.path) !== undefined) {
166
+ subjects.add(subjectOf(change.path))
167
+ }
168
+ }
169
+
170
+ for (const subject of [...subjects].sort()) {
171
+ const candidate = testPathFor(subject)
172
+ const testChange = introduction(changes, candidate)
173
+ const testAtBase = atBase.has(candidate)
174
+ const test = testAtBase || testChange !== undefined ? candidate : undefined
175
+
176
+ const implementation = introduction(changes, subject)
177
+
178
+ if (implementation === undefined) {
179
+ unclassified.push({
180
+ subject,
181
+ test: test ?? null,
182
+ verdict: 'unclassified',
183
+ implementationCommit: null,
184
+ testCommit: null,
185
+ reason: atBase.has(subject)
186
+ ? 'the implementation predates the range, so a change to it cannot be separated from a refactor'
187
+ : 'no implementation reached the range beside this test',
188
+ })
189
+ continue
190
+ }
191
+
192
+ if (test === undefined) {
193
+ unclassified.push({
194
+ subject,
195
+ test: null,
196
+ verdict: 'unclassified',
197
+ implementationCommit: implementation.commit,
198
+ testCommit: null,
199
+ reason: 'no test names this module, so the ordering has no second side',
200
+ })
201
+ continue
202
+ }
203
+
204
+ // A paired test the range never added is one that already sat at the base
205
+ // commit, since those are the only two ways `test` gets a value at all.
206
+ if (testChange === undefined) {
207
+ satisfied.push({
208
+ subject,
209
+ test,
210
+ verdict: 'satisfied',
211
+ implementationCommit: implementation.commit,
212
+ testCommit: null,
213
+ reason: 'the test predates the range',
214
+ })
215
+ continue
216
+ }
217
+
218
+ // One commit carrying both sides counts as satisfied. The rule asks that
219
+ // the test not come after, and a single commit is the shape a small change
220
+ // takes here, so reporting it would flag most of the corpus.
221
+ const record: PairRecord = {
222
+ subject,
223
+ test,
224
+ verdict:
225
+ testChange.order <= implementation.order
226
+ ? 'satisfied'
227
+ : 'implementation-first',
228
+ implementationCommit: implementation.commit,
229
+ testCommit: testChange.commit,
230
+ reason:
231
+ testChange.order <= implementation.order
232
+ ? 'the test reached history no later than the implementation'
233
+ : 'the implementation reached history before the test covering it',
234
+ }
235
+
236
+ if (record.verdict === 'satisfied') satisfied.push(record)
237
+ else findings.push(record)
238
+ }
239
+
240
+ return { satisfied, findings, unclassified }
241
+ }
242
+
243
+ /**
244
+ * Where an implementation reached a commit ahead of the test covering it,
245
+ * between `base` and the current `HEAD` of the tree at `root`.
246
+ *
247
+ * This reports and never gates. Pairing a test to an implementation is a
248
+ * judgment, so a change the pairing cannot read lands in `unclassified` with
249
+ * its reason stated rather than being counted as a pass. Coverage is narrower
250
+ * than the rule the check answers to, and `scope` and `ignored` are what say so
251
+ * on every run.
252
+ */
253
+ export function readTestOrder(
254
+ root: string,
255
+ options: TestOrderOptions = {},
256
+ ): TestOrderReport {
257
+ const head = revParse(root, 'HEAD')
258
+ if (head === undefined) {
259
+ return {
260
+ kind: 'unreadable',
261
+ reason: `No git history under ${root}. History is the only surface carrying the ordering, so there is nothing to read.`,
262
+ }
263
+ }
264
+
265
+ const base = resolveBase(root, options.base, head)
266
+ if (typeof base !== 'string') return base
267
+
268
+ const log = git(root, [
269
+ 'log',
270
+ '--reverse',
271
+ '--name-status',
272
+ '--no-renames',
273
+ '--format=%H',
274
+ `${base}..${head}`,
275
+ ])
276
+
277
+ if (log === undefined) {
278
+ return {
279
+ kind: 'unreadable',
280
+ reason: `Reading history between ${base} and HEAD failed under ${root}.`,
281
+ }
282
+ }
283
+
284
+ const tree = git(root, ['ls-tree', '-r', '--name-only', base])
285
+ if (tree === undefined) {
286
+ return {
287
+ kind: 'unreadable',
288
+ reason: `Reading the tree at ${base} failed under ${root}. Without it a test written before the range reads as absent.`,
289
+ }
290
+ }
291
+
292
+ const changes = parseChanges(log)
293
+ const atBase = new Set(tree.split('\n').filter((line) => line !== ''))
294
+
295
+ const ignored = [
296
+ ...new Set(
297
+ changes
298
+ .map((change) => change.path)
299
+ .filter(
300
+ (path) => !isImplementation(path) && testSuffix(path) === undefined,
301
+ ),
302
+ ),
303
+ ].sort()
304
+
305
+ return {
306
+ kind: 'measured',
307
+ base,
308
+ head,
309
+ scope: { extensions: SOURCE_EXTENSIONS, testSuffixes: TEST_SUFFIXES },
310
+ ...classify(changes, atBase),
311
+ ignored,
312
+ }
313
+ }
314
+
315
+ /**
316
+ * The far side of the range. A ref the caller named has to resolve, since
317
+ * falling back to the trunk there would measure a range nobody asked for. With
318
+ * no ref named, the merge base against the trunk scopes the run to the branch,
319
+ * and a repository carrying no trunk falls back to the root commit rather than
320
+ * refusing.
321
+ */
322
+ function resolveBase(
323
+ root: string,
324
+ ref: string | undefined,
325
+ head: string,
326
+ ): string | { kind: 'unreadable'; reason: string } {
327
+ if (ref !== undefined) {
328
+ const resolved = revParse(root, ref)
329
+ if (resolved === undefined) {
330
+ return {
331
+ kind: 'unreadable',
332
+ reason: `Ref ${ref} resolves to no commit in ${root}. Pass a commit this tree carries.`,
333
+ }
334
+ }
335
+ return resolved
336
+ }
337
+
338
+ for (const trunk of TRUNK_REFS) {
339
+ if (revParse(root, trunk) === undefined) continue
340
+ const merged = git(root, ['merge-base', head, trunk])
341
+ if (merged !== undefined && merged !== '') return merged
342
+ }
343
+
344
+ const rootCommits = git(root, ['rev-list', '--max-parents=0', head])
345
+ if (rootCommits === undefined || rootCommits === '') {
346
+ return {
347
+ kind: 'unreadable',
348
+ reason: `No base resolves against ${root}. Fetch origin or pass --base.`,
349
+ }
350
+ }
351
+
352
+ return rootCommits.split('\n')[0]
353
+ }
354
+
355
+ /**
356
+ * `execaSync` with git's repository-resolution variables stripped, so `-C`
357
+ * resolves against `root` and not against whatever repository a hook exported.
358
+ * `undefined` is the refusal, which every caller turns into its own reason.
359
+ */
360
+ function git(root: string, args: readonly string[]): string | undefined {
361
+ const result = execaSync('git', ['-C', root, ...args], {
362
+ reject: false,
363
+ env: gitEnv(),
364
+ extendEnv: false,
365
+ })
366
+
367
+ return result.exitCode === 0 ? result.stdout.trimEnd() : undefined
368
+ }
369
+
370
+ /**
371
+ * The commit a ref names. `^{commit}` is what turns a tag or a tree into the
372
+ * commit behind it, so a caller never compares a ref against another type.
373
+ */
374
+ function revParse(root: string, ref: string): string | undefined {
375
+ const resolved = git(root, [
376
+ 'rev-parse',
377
+ '--verify',
378
+ '--quiet',
379
+ `${ref}^{commit}`,
380
+ ])
381
+
382
+ return resolved === undefined || resolved === '' ? undefined : resolved.trim()
383
+ }
@@ -10,7 +10,7 @@ import type { BanSets } from '@/markdown/scan'
10
10
  const CHARACTERS = ['—', ';'] as const
11
11
 
12
12
  /**
13
- * Single lowercase words `prose.md` bans under `## Language`.
13
+ * Single lowercase words `markdown.md` bans under `## Language`.
14
14
  *
15
15
  * A multi-word ban is absent by the same test the character set applies. The
16
16
  * standard bans a pattern like `It's not X, it's Y` with a placeholder standing
@@ -34,7 +34,7 @@ const WORDS = [
34
34
  ] as const
35
35
 
36
36
  /**
37
- * British spellings of the American examples `prose.md` lists.
37
+ * British spellings of the American examples `markdown.md` lists.
38
38
  *
39
39
  * The set is carried whole rather than derived from a suffix rule, because a
40
40
  * suffix pattern run over prose produced 46 of the 58 false positives measured
@@ -29,7 +29,7 @@ const CITATION = /`([^`\n]+?\.md)`/g
29
29
  const DELEGATION_START = /^Does not govern:/
30
30
  const HEADING = /^#{1,6}\s/
31
31
 
32
- /** Accepts `prose` and `prose.md` alike, since the catalog lists both spellings. */
32
+ /** Accepts `skill` and `skill.md` alike, since the catalog lists both spellings. */
33
33
  export function normalizeName(raw: string): string {
34
34
  const name = raw.trim()
35
35
  return name.endsWith('.md') ? name : `${name}.md`
@@ -14,8 +14,8 @@ Does not govern:
14
14
 
15
15
  - Pull request title and body: `pr.md`
16
16
  - Whether a phase label may appear in issue text: `versioning.md`
17
- - Voice and banned words in issue prose: `prose.md`
18
- - Punctuation and formatting in issue prose: `markdown.md`
17
+ - Voice, rhythm, and sentence construction in issue prose: the `write-human` skill
18
+ - Punctuation, formatting, and banned words in issue prose: `markdown.md`
19
19
 
20
20
  ## Title
21
21
 
@@ -15,8 +15,8 @@ Does not govern:
15
15
  - Commit subject format, which shares the title form: `commit.md`
16
16
  - Branch naming: `branch.md`
17
17
  - Whether a phase label or a semver tag may appear in a title or body: `versioning.md`
18
- - Voice and banned words in pull request prose: `prose.md`
19
- - Punctuation and formatting in pull request prose: `markdown.md`
18
+ - Voice, rhythm, and sentence construction in pull request prose: the `write-human` skill
19
+ - Punctuation, formatting, and banned words in pull request prose: `markdown.md`
20
20
 
21
21
  ## Title
22
22
 
@@ -13,8 +13,8 @@ Governs a snippet file: what one is for, whether a prompt qualifies as one, how
13
13
  Does not govern:
14
14
 
15
15
  - Skill folders, which carry frontmatter, references, and scripts a snippet has none of: `skill.md`
16
- - Voice and word choice in snippet prose: `prose.md`
17
- - Punctuation and formatting in snippet prose: `markdown.md`
16
+ - Voice, rhythm, and sentence construction in snippet prose: the `write-human` skill
17
+ - Punctuation, formatting, and word choice in snippet prose: `markdown.md`
18
18
 
19
19
  ## What a snippet is
20
20
 
@@ -11,12 +11,12 @@ A diagram entry answers one question about the system with one or more Mermaid d
11
11
 
12
12
  ## Scope
13
13
 
14
- Governs per-kind diagram entries under `.claude/diagrams/`: which question each answers, the Mermaid source, the accessibility fields, and the explanation prose beneath. It states the voice for that prose, which is the yield `prose.md` grants a surface whose own standard sets one.
14
+ Governs per-kind diagram entries under `.claude/diagrams/`: which question each answers, the Mermaid source, the accessibility fields, and the explanation prose beneath. It states the voice for that prose, which is the yield the `write-human` skill grants a surface whose own standard sets one.
15
15
 
16
16
  Does not govern:
17
17
 
18
- - Language and word choice in explanation prose and node labels: `prose.md`, whose bans the yield does not lift
19
- - Punctuation and formatting in explanation prose: `markdown.md`, which the yield does not reach
18
+ - Rhythm and sentence construction in explanation prose: the `write-human` skill, whose rules the yield does not lift beyond voice
19
+ - Language, word choice, punctuation, and formatting in explanation prose and node labels: `markdown.md`, which the yield does not reach
20
20
  - The mechanism behind any component a diagram draws: `context.md`
21
21
  - UI layout, on-screen copy, and interaction intent: `wireframes.md`
22
22
  - The decision record a components diagram is drawn from: `architecture.md`
@@ -94,7 +94,7 @@ A second entry for one kind takes a suffixed name (`request-flow-admin.md`) and
94
94
  - Do not duplicate prose across entries. An entry that restates its neighbor has taken the neighbor's job.
95
95
  - The audience is mixed, so vocabulary runs as a gradient across the set. `System context` assumes no knowledge of the repository. `Deployment` may assume the reader has read the others.
96
96
 
97
- This section states the voice for the surface, which is what claims the yield `prose.md` grants to a surface whose own standard sets it. Explanation prose is pedagogical here and the default developer-facing voice does not apply. The yield covers voice alone. The language bans in `prose.md` stay in force, as do the punctuation and formatting rules in `markdown.md`, which grants no yield at all.
97
+ This section states the voice for the surface, which is what claims the yield the `write-human` skill grants to a surface whose own standard sets it. Explanation prose is pedagogical here and the default developer-facing voice does not apply. The yield covers voice alone. The rhythm and density rules that skill carries stay in force, as do the language bans, punctuation, and formatting in `markdown.md`, which grants no yield at all.
98
98
 
99
99
  ## Verification
100
100
 
@@ -121,7 +121,7 @@ Reference the context entry by path when a reader needs the mechanism. The diagr
121
121
  - `System context` has no named source signal beyond `.claude/REQUIREMENTS.md`, so nothing tells a session it went stale. Re-read it when the boundary or the set of external dependencies moves.
122
122
  - The `claude-docs` sweep watches two things and writes frontmatter only. It appends `stale` when a path an entry cites leaves the tree, and it stubs a kind when a diff adds the source signal that kind is drawn from. Diagram bodies and explanation paragraphs are off limits to it, because a change that removes a module does not carry the new correct shape of the picture.
123
123
  - That watch samples thinly. It sees the one or two paths an entry happened to cite and nothing else, so a change elsewhere leaves the entry looking current. `verified` is what covers the gap, and an entry whose date sits far behind the branch is due a read whether or not anything flagged it.
124
- - The explanation paragraphs around a Mermaid block are prose and follow `prose.md` and `markdown.md`. The fenced block itself is not, which is why a check scoped to prose is the wrong thing to rely on for what sits inside it.
124
+ - The explanation paragraphs around a Mermaid block are prose and follow `markdown.md` and the `write-human` skill. The fenced block itself is not, which is why a check scoped to prose is the wrong thing to rely on for what sits inside it.
125
125
  - The punctuation bans still apply to node and subgraph labels, and nothing checks them there. An em dash in a label passes every gate the repository has, so read the labels before shipping the entry.
126
126
 
127
127
  ## Template
@@ -15,8 +15,8 @@ Does not govern:
15
15
 
16
16
  - The folder a learning workspace lays out around its glossary, and the other files in it: `teach.md`
17
17
  - Which surface a glossary moves to once it leaves the material that produced it, which belongs to the surface driving that move
18
- - Voice and word choice: `prose.md`
19
- - Headings, punctuation, and file references: `markdown.md`
18
+ - Voice, rhythm, and sentence construction: the `write-human` skill
19
+ - Headings, punctuation, word choice, and file references: `markdown.md`
20
20
 
21
21
  ## What a working glossary looks like
22
22
 
@@ -18,8 +18,8 @@ Does not govern:
18
18
  - A dump of many findings filed by domain, each carrying its own verdict: `intake.md`
19
19
  - The feature plan a closed track feeds, and the contract its answer slots keep: `plan.md`
20
20
  - The task file a closing track writes, and the origin line pointing back at the folder: `tasks.md`
21
- - Voice and word choice: `prose.md`
22
- - Headings, punctuation, and file references: `markdown.md`
21
+ - Voice, rhythm, and sentence construction: the `write-human` skill
22
+ - Headings, punctuation, word choice, and file references: `markdown.md`
23
23
  - When a project opens a track at all, and the procedure that runs one, which belong to the surface driving it
24
24
 
25
25
  ## What a working track looks like
@@ -122,6 +122,10 @@ Each spike carries four things:
122
122
 
123
123
  Cost is a report rather than a limit, and it is what makes the next spike estimable before anyone commits to it. Record it even when it comes to a single read.
124
124
 
125
+ A spike also leaves files behind, and they split on whether the track cites them rather than on whether they are markdown. An input the run reads, being a fixture page, an arm script, or a copied asset, is re-runnable and cited by nothing, so it stays outside the track wherever the surface driving the spike puts it. Evidence the record cites, being a recording, a render, or a frame pulled from one, is what a later reader opens to check a claim, so it lives in `evidence/` inside the track beside the file citing it. Name the split rather than the file types, which is what keeps this from going stale on the next kind of artifact a spike produces.
126
+
127
+ `evidence/` takes no number, since numbering is the read order over the files a reader opens in sequence and an artifact is reached from the claim that cites it instead.
128
+
125
129
  Reach for a test harness the project already carries before building one. A track needing an experiment no existing harness can express has found a finding, and it belongs in the folder rather than in a new abstraction.
126
130
 
127
131
  One method error is worth naming, because it is made rather than imagined. Counting matches in a transcript overstates whether a file was read, since an instruction naming a path puts that path in the transcript whether or not anything opened it. The check is the tool call.
@@ -14,10 +14,9 @@ Reference docs for consistent authoring across the toolkit and target projects.
14
14
  - [Glossary reference](glossary.md): Frontmatter, entry shape, ordering, and the rules deciding which terms a glossary carries
15
15
  - [Groundwork reference](groundwork.md): Folder layout, reserved numbering, frontmatter and dating, required file contents, and conventions for a measurement track
16
16
  - [Intake reference](intake.md): Folder layout, reserved index number, frontmatter and dating, the item template, the answer contract, and retrieval
17
- - [Markdown reference](markdown.md): Headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, and file references
17
+ - [Markdown reference](markdown.md): Headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, file references, banned words, and frontmatter wording
18
18
  - [Memory reference](memory.md): Filename and type prefix, frontmatter, the body shape per type, links between entries, and the lifecycle from write to retire
19
19
  - [Plan reference](plan.md): Filename and slug, required sections, the suggested-and-answer contract, and the lifecycle from the live folder to the archive
20
- - [Prose reference](prose.md): Voice, language, what prose may claim, and frontmatter wording for reference markdown
21
20
  - [Publish reference](publish.md): Scan an author runs against finished text, the cross-reference form each destination takes, and the response to an unreadable source
22
21
  - [Readme reference](readme.md): Readme voice, structure, and content conventions
23
22
  - [Requirements reference](requirements.md): Shape and content rules for .claude/REQUIREMENTS.md
@@ -18,8 +18,8 @@ Does not govern:
18
18
  - One question measured in depth before anyone can plan against it: `groundwork.md`
19
19
  - The feature plan a promoted item feeds, and the inverted answer contract it keeps: `plan.md`
20
20
  - The task file promoting an item onto the board, and the origin line pointing back at the folder: `tasks.md`
21
- - Voice and word choice: `prose.md`
22
- - Headings, punctuation, and file references: `markdown.md`
21
+ - Voice, rhythm, and sentence construction: the `write-human` skill
22
+ - Headings, punctuation, word choice, and file references: `markdown.md`
23
23
  - Which findings belong in a dump at all, and the procedure that files one, which belong to the surface driving it
24
24
 
25
25
  ## What a working intake looks like
@@ -1,22 +1,28 @@
1
1
  ---
2
2
  title: Markdown reference
3
- description: Headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, and file references
3
+ description: Headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, file references, banned words, and frontmatter wording
4
4
  ---
5
5
 
6
6
  # Markdown reference
7
7
 
8
- Applies to markdown reference docs, READMEs, and inline documentation in repos. These are mechanics rather than voice, so no surface yields them. A surface stating its own voice claims that yield from `prose.md` and formats by this file regardless.
8
+ Applies to markdown reference docs, READMEs, and inline documentation in repos. Every rule here is a fact a scan can settle rather than a judgment, so no surface yields any of them. A surface stating its own voice claims that yield from the voice guidance and formats and spells by this file regardless.
9
9
 
10
10
  ## Scope
11
11
 
12
- Governs the markdown mechanics of every markdown file: headings, paragraph and list structure, code spans and fences, the form a date takes, punctuation, emphasis, and file references. It is an attribute standard rather than a document-type one, so it applies over documents whose shape another standard sets, and it carries no template because mechanics are written across every document and have no shape of their own.
12
+ Governs what a check can decide about a markdown file: headings, paragraph and list structure, code spans and fences, the form a date takes, punctuation, emphasis, file references, the closed-set word and spelling bans, what prose may claim about its sources, and the wording of a catalog title and description. It is an attribute standard rather than a document-type one, so it applies over documents whose shape another standard sets, and it carries no template because these rules are written across every document and have no shape of their own.
13
+
14
+ The two frontmatter fields it reaches are `title` and `description`. They are named here rather than in the statement above, since a backticked token in a scope statement's first sentence is published as the standard's jurisdiction.
15
+
16
+ The split with the voice guidance is what reads the rule rather than what the rule covers. Every ban below ships as data `aitk markdown audit` and the installed audit hook both read, so a violation is measured on every run. Cadence, rhythm, and information density are judgments a reader settles, and those travel in the `write-human` skill, which a markdown edit routes to.
13
17
 
14
18
  Does not govern:
15
19
 
16
- - Voice, word choice, what prose may claim, and the wording of a `title` or `description`: `prose.md`
20
+ - Voice, rhythm, sentence construction, and information density, which the `write-human` skill carries
17
21
  - What sections a document has, or what belongs in each: the standard for that document type
22
+ - Which frontmatter fields a document carries, which is that standard's own subject. This file governs the wording of a `title` and a `description` and nothing else about them.
18
23
  - The text inside a fenced block, which follows the conventions of its own language rather than these
19
- - The scan that applies the punctuation bans to finished text on its way out: `publish.md`
24
+ - Phase-label and semver discipline: `versioning.md`
25
+ - The scan that applies these bans to finished text on its way out: `publish.md`
20
26
  - Whether a pull request or issue number is backticked, which turns on where the text is published rather than on the text: `publish.md`
21
27
 
22
28
  ## Headings
@@ -58,7 +64,30 @@ Does not govern:
58
64
  - Do not use em dashes (`—`) or semicolons (`;`). Rewrite or restructure the sentence to avoid them.
59
65
  - Do not use parenthetical asides in prose (`the config (which is optional) controls...`). Split into its own sentence or drop it. Parentheses in rule definitions for grouping examples are fine.
60
66
 
61
- The closed-set word bans sit in `prose.md` under `## Language` rather than here, because a banned word is a word-choice rule and these are character rules. A surface applying both reads both files.
67
+ ## Language
68
+
69
+ - Use American English spelling. Prefer `-ize` over `-ise`, `-or` over `-our`, `-er` over `-re` (`organize`, `analyze`, `summarize`, `recognize`, `behavior`, `color`, `center`)
70
+ - Do not use marketing buzzwords (`seamless`, `robust`, `powerful`, `revolutionary`, `enhanced`, `allows`, `leverage`)
71
+ - Do not use vague qualifiers (`simply`, `just`, `easily`, `quickly`, `very`, `really`)
72
+ - Open a sentence with its subject and action, not filler (`Note that`, `Basically`), a hollow connective (`That being said`, `It's worth noting`), or a gerund windup (`Leveraging the API...`). Substantive transitions that carry a real relationship are fine.
73
+ - Do not use the negative parallelism pattern (`It's not X, it's Y`, `not because X, but because Y`)
74
+ - Do not pad verb phrases or delay the action. Write the shortest form (`in order to` → `to`, `ensure that X is set` → `set X`, `By doing X, you can Y` → state Y directly).
75
+ - Do not address the reader as a participant (`Let's`, `Here's`, `Here are`). State the content directly.
76
+ - Commit to a position. Do not hedge in clusters (`It might be worth considering`) or use false balance (`While X is true, Y is also important`). Recommend, or state the tradeoff.
77
+ - Do not inflate significance. State what a thing does rather than calling it `a major milestone` or `a turning point for the field`.
78
+ - Do not name a person, company, or product to borrow its authority. Name a source only where the claim turns on who made it.
79
+ - Do not attribute a claim to an unnamed authority (`experts say`, `studies show`, `it is widely believed`). Name the source or cut the claim.
80
+ - Do not introduce a fact, name, date, or citation the source does not carry when rewriting existing text. A rewrite changes wording and never claims.
81
+
82
+ The word bans and the character bans sit under one file because one command reads both. `aitk markdown audit` ships them as package data, so a project that installed no standards is measured the same as one that did, and this section states them for a reader rather than for the parser.
83
+
84
+ ## Frontmatter descriptions
85
+
86
+ When frontmatter carries a short `title` or `description` used for catalog display:
87
+
88
+ - `title`: sentence case, identifies the file uniquely against its siblings in the same catalog. Proper nouns retain their casing. No trailing period.
89
+ - `description`: sentence case, names the specific topics covered so a reader can decide whether to open the file. Lead with concrete subjects, strip filler like "guide to", "overview of", or "documentation about". No trailing period, no leading article (`the`, `a`).
90
+ - Do not mechanically reuse the H1 as the description.
62
91
 
63
92
  ## Emphasis and dividers
64
93
 
@@ -83,3 +112,23 @@ Good: See `.claude/context/retrieval.md` for the retrieval flow.
83
112
  Bad: Read [docs/development.md](docs/development.md) before contributing.
84
113
  Good: Read the [development guide](docs/development.md) before contributing.
85
114
  ```
115
+
116
+ ```markdown
117
+ Bad: In order to configure the server, you'll need to ensure that the port is set.
118
+ Good: Set `port` in the server config.
119
+ ```
120
+
121
+ ```markdown
122
+ Bad: It's not just a cache. It's a system for intelligent memory management.
123
+ Good: The cache is an LRU store. It evicts the least-recently-used entry when full.
124
+ ```
125
+
126
+ ```markdown
127
+ Bad: Leveraging the retry mechanism, developers can build more resilient integrations.
128
+ Good: Use the `retry` option for failed webhooks. Set `maxRetries` to 3.
129
+ ```
130
+
131
+ ```markdown
132
+ Bad: It might be worth considering whether to enable caching.
133
+ Good: Enable caching for read-heavy endpoints. Skip it for writes.
134
+ ```
@@ -18,8 +18,8 @@ Does not govern:
18
18
  - The per-domain entry a domain fact is routed to instead of memory: `context.md`
19
19
  - The feature plan a session executes, and its answer contract: `plan.md`
20
20
  - The task file tracking what is being built: `tasks.md`
21
- - Voice and word choice: `prose.md`
22
- - Headings, punctuation, and file references: `markdown.md`
21
+ - Voice, rhythm, and sentence construction: the `write-human` skill
22
+ - Headings, punctuation, word choice, and file references: `markdown.md`
23
23
  - Which facts a project captures at all, and where the folder sits, which are project policy
24
24
 
25
25
  ## What a working entry looks like
package/standards/plan.md CHANGED
@@ -19,8 +19,8 @@ Does not govern:
19
19
  - A dump of many findings filed by domain, each carrying its own verdict: `intake.md`
20
20
  - The task file a plan is linked from, and the origin line pointing back at it: `tasks.md`
21
21
  - The transform from a branch name to a slug: `slug.md`
22
- - Voice and word choice: `prose.md`
23
- - Headings, punctuation, and file references: `markdown.md`
22
+ - Voice, rhythm, and sentence construction: the `write-human` skill
23
+ - Headings, punctuation, word choice, and file references: `markdown.md`
24
24
  - When a plan is written at all, and whether a short one goes to a file or to the conversation, which belong to the surface driving it
25
25
 
26
26
  ## What a working plan looks like