@erclx/canon 5.21.0 → 5.22.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 (74) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/auto-ship/SKILL.md +1 -1
  3. package/claude/skills/auto-ship/references/draft-mark.md +8 -6
  4. package/claude/skills/draft-doc/REQUIREMENT.md +45 -0
  5. package/claude/skills/draft-doc/SKILL.md +77 -0
  6. package/claude/skills/draft-doc/references/context.md +31 -0
  7. package/claude/skills/draft-doc/references/docs.md +33 -0
  8. package/claude/skills/draft-doc/references/readme.md +58 -0
  9. package/claude/skills/draft-doc/references/wiki.md +53 -0
  10. package/claude/skills/draft-doc/references/wireframes.md +48 -0
  11. package/claude/skills/draft-figure/REQUIREMENT.md +1 -1
  12. package/claude/skills/draft-figure/SKILL.md +1 -1
  13. package/claude/skills/git-pr/SKILL.md +5 -46
  14. package/claude/skills/git-pr/references/final-command.md +75 -0
  15. package/claude/skills/git-pr/references/labels.md +2 -2
  16. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +76 -0
  17. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +11 -1
  18. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +5 -1
  19. package/claude/skills/search-craft/REQUIREMENT.md +4 -4
  20. package/claude/skills/search-craft/SKILL.md +1 -1
  21. package/docs/agents/commands.md +1 -1
  22. package/docs/agents/drift-surfaces.md +20 -9
  23. package/docs/agents/index.md +2 -2
  24. package/docs/agents/install-and-sync.md +3 -3
  25. package/docs/agents/migrate.md +1 -1
  26. package/docs/agents/rule-citations.md +1 -1
  27. package/docs/agents/slides.md +47 -3
  28. package/docs/agents/tasks.md +1 -1
  29. package/docs/target/migrations.md +3 -1
  30. package/docs/target/sync.md +1 -1
  31. package/docs/workflow/skill-map.md +14 -18
  32. package/governance/rules/canon/604-scratch.md +0 -1
  33. package/governance/rules/claude/575-hooks.md +1 -2
  34. package/governance/rules/claude/576-settings.md +1 -2
  35. package/governance/rules/standards/architecture.md +0 -1
  36. package/governance/rules/standards/context.md +0 -1
  37. package/governance/rules/standards/design.md +0 -1
  38. package/governance/rules/standards/diagrams.md +0 -3
  39. package/governance/rules/standards/requirements.md +0 -1
  40. package/governance/rules/standards/wireframes.md +0 -1
  41. package/package.json +1 -1
  42. package/scripts/manage-sandbox.sh +7 -8
  43. package/src/claude/cases/authoring.ts +5 -5
  44. package/src/commands/slides.ts +14 -4
  45. package/src/commands/sync.ts +10 -0
  46. package/src/gov/citations.ts +1 -2
  47. package/src/migrate/skill-names.ts +12 -0
  48. package/src/slides/convert/deck.ts +83 -3
  49. package/src/slides/convert/export.ts +67 -19
  50. package/src/slides/convert/shapes.ts +43 -3
  51. package/src/slides/convert/walk.ts +43 -1
  52. package/src/slides/package/fonts.ts +297 -0
  53. package/src/slides/package/motion.ts +316 -0
  54. package/src/slides/package/test-face.ts +113 -0
  55. package/src/sync/check.ts +9 -0
  56. package/src/sync/layout.ts +27 -1
  57. package/standards/architecture.md +0 -1
  58. package/standards/context.md +0 -1
  59. package/standards/design.md +0 -1
  60. package/standards/diagrams.md +0 -3
  61. package/standards/requirements.md +0 -1
  62. package/standards/rule.md +1 -0
  63. package/standards/wireframes.md +0 -1
  64. package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +8 -3
  65. package/claude/skills/draft-context/REQUIREMENT.md +0 -37
  66. package/claude/skills/draft-context/SKILL.md +0 -61
  67. package/claude/skills/draft-docs/REQUIREMENT.md +0 -35
  68. package/claude/skills/draft-docs/SKILL.md +0 -59
  69. package/claude/skills/draft-readme/REQUIREMENT.md +0 -38
  70. package/claude/skills/draft-readme/SKILL.md +0 -73
  71. package/claude/skills/draft-wiki/REQUIREMENT.md +0 -42
  72. package/claude/skills/draft-wiki/SKILL.md +0 -83
  73. package/claude/skills/draft-wireframes/REQUIREMENT.md +0 -38
  74. package/claude/skills/draft-wireframes/SKILL.md +0 -69
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Builds the smallest sfnt the font step reads, holding only the `OS/2`,
3
+ * `head`, and `name` tables an EOT header is built from, plus `fvar` when the
4
+ * face should read as variable. It carries no glyphs, so it embeds and never
5
+ * renders. Tests use it in place of a real face, whose license the repository
6
+ * would then have to carry.
7
+ */
8
+
9
+ export interface TestFaceOptions {
10
+ readonly family?: string
11
+ readonly subfamily?: string
12
+ readonly weight?: number
13
+ readonly isItalic?: boolean
14
+ readonly fsType?: number
15
+ readonly isVariable?: boolean
16
+ /** The first four bytes, `0x00010000` for TrueType. */
17
+ readonly version?: number
18
+ }
19
+
20
+ /** Values distinct enough that a header copying the wrong field shows it. */
21
+ export const TEST_FACE = {
22
+ panose: [2, 11, 5, 3, 3, 4, 3, 2, 2, 4],
23
+ unicodeRanges: [0xe0000aff, 0x5000e5ff, 0x00000009, 0x00000001],
24
+ codePages: [0x0000019f, 0x01020000],
25
+ checkSumAdjustment: 0x1234abcd,
26
+ } as const
27
+
28
+ function os2(options: TestFaceOptions): Buffer {
29
+ const table = Buffer.alloc(96)
30
+ table.writeUInt16BE(4, 0)
31
+ table.writeUInt16BE(options.weight ?? 400, 4)
32
+ table.writeUInt16BE(options.fsType ?? 0, 8)
33
+ Buffer.from(TEST_FACE.panose).copy(table, 32)
34
+ TEST_FACE.unicodeRanges.forEach((range, index) => {
35
+ table.writeUInt32BE(range, 42 + index * 4)
36
+ })
37
+ table.write('TEST', 58, 'latin1')
38
+ table.writeUInt16BE(options.isItalic ? 0x0001 : 0x0040, 62)
39
+ TEST_FACE.codePages.forEach((page, index) => {
40
+ table.writeUInt32BE(page, 78 + index * 4)
41
+ })
42
+ return table
43
+ }
44
+
45
+ function head(): Buffer {
46
+ const table = Buffer.alloc(54)
47
+ table.writeUInt32BE(0x00010000, 0)
48
+ table.writeUInt32BE(TEST_FACE.checkSumAdjustment, 8)
49
+ table.writeUInt32BE(0x5f0f3cf5, 12)
50
+ return table
51
+ }
52
+
53
+ function name(strings: ReadonlyMap<number, string>): Buffer {
54
+ const encoded = [...strings].map(([id, text]) => ({
55
+ id,
56
+ bytes: Buffer.from(text, 'utf16le').swap16(),
57
+ }))
58
+ const header = Buffer.alloc(6 + encoded.length * 12)
59
+ header.writeUInt16BE(0, 0)
60
+ header.writeUInt16BE(encoded.length, 2)
61
+ header.writeUInt16BE(header.length, 4)
62
+ let offset = 0
63
+ encoded.forEach(({ id, bytes }, index) => {
64
+ const at = 6 + index * 12
65
+ header.writeUInt16BE(3, at)
66
+ header.writeUInt16BE(1, at + 2)
67
+ header.writeUInt16BE(0x0409, at + 4)
68
+ header.writeUInt16BE(id, at + 6)
69
+ header.writeUInt16BE(bytes.length, at + 8)
70
+ header.writeUInt16BE(offset, at + 10)
71
+ offset += bytes.length
72
+ })
73
+ return Buffer.concat([header, ...encoded.map(({ bytes }) => bytes)])
74
+ }
75
+
76
+ export function testFace(options: TestFaceOptions = {}): Buffer {
77
+ const family = options.family ?? 'Fixture Sans'
78
+ const subfamily = options.subfamily ?? 'Regular'
79
+ const tables = new Map<string, Buffer>([
80
+ ['OS/2', os2(options)],
81
+ ['head', head()],
82
+ [
83
+ 'name',
84
+ name(
85
+ new Map([
86
+ [1, family],
87
+ [2, subfamily],
88
+ [4, `${family} ${subfamily}`],
89
+ [5, 'Version 1.000'],
90
+ ]),
91
+ ),
92
+ ],
93
+ ])
94
+ if (options.isVariable) tables.set('fvar', Buffer.alloc(16))
95
+
96
+ const entries = [...tables].sort(([a], [b]) => a.localeCompare(b))
97
+ const directory = Buffer.alloc(12 + entries.length * 16)
98
+ directory.writeUInt32BE(options.version ?? 0x00010000, 0)
99
+ directory.writeUInt16BE(entries.length, 4)
100
+ let offset = directory.length
101
+ const bodies: Buffer[] = []
102
+ entries.forEach(([tag, table], index) => {
103
+ const at = 12 + index * 16
104
+ directory.write(tag, at, 'latin1')
105
+ directory.writeUInt32BE(offset, at + 8)
106
+ directory.writeUInt32BE(table.length, at + 12)
107
+ const padded = Buffer.alloc(Math.ceil(table.length / 4) * 4)
108
+ table.copy(padded)
109
+ bodies.push(padded)
110
+ offset += padded.length
111
+ })
112
+ return Buffer.concat([directory, ...bodies])
113
+ }
package/src/sync/check.ts CHANGED
@@ -10,8 +10,10 @@ import { planSync, type ScanEntry, type SyncAdapter } from '@/sync/engine'
10
10
  import {
11
11
  collectSuperseded,
12
12
  detectUnmigrated,
13
+ detectUnmovedSurfaces,
13
14
  type SupersededEntry,
14
15
  type UnmigratedDomain,
16
+ type UnmovedSurface,
15
17
  } from '@/sync/layout'
16
18
  import {
17
19
  buildReverseReport,
@@ -178,6 +180,11 @@ export interface CheckReport {
178
180
  readonly seeds: SeedsReport
179
181
  readonly superseded: readonly SupersededEntry[]
180
182
  readonly unmigrated: readonly UnmigratedDomain[]
183
+ /**
184
+ * Report-only like `superseded`, since a target's CI gating on `--exit-code`
185
+ * would otherwise start failing on a layout nobody asked it to move yet.
186
+ */
187
+ readonly unmovedSurfaces: readonly UnmovedSurface[]
181
188
  readonly newSkills: readonly string[]
182
189
  /**
183
190
  * Rules the toolkit authored after this target's governance anchor, filtered
@@ -440,6 +447,7 @@ export async function buildCheckReport(
440
447
  seeds: { entries: [], historyUnavailable: false },
441
448
  superseded: [],
442
449
  unmigrated: [],
450
+ unmovedSurfaces: [],
443
451
  newSkills: [],
444
452
  newRules: [],
445
453
  reverse: emptyReverseReport(),
@@ -456,6 +464,7 @@ export async function buildCheckReport(
456
464
  seeds: buildSeedsReport(toolkitRoot, target),
457
465
  superseded: collectSuperseded(target),
458
466
  unmigrated,
467
+ unmovedSurfaces: detectUnmovedSurfaces(target),
459
468
  newSkills: await readNewSkills(toolkitRoot, anchors),
460
469
  newRules: await readNewRules(toolkitRoot, target, stamp),
461
470
  reverse: buildReverseReport(toolkitRoot, target),
@@ -2,7 +2,7 @@ import { existsSync, statSync } from 'node:fs'
2
2
  import { basename, join, relative } from 'node:path'
3
3
  import { SUBDIRS } from '@/claude/seeds'
4
4
  import { creationRel, isRecordEntry } from '@/record-root'
5
- import { SURFACE_ENTRIES, surfaceDir } from '@/surface-root'
5
+ import { SURFACE_ENTRIES, spell, surfaceDir } from '@/surface-root'
6
6
  import type { StampDomain } from '@/sync/stamp'
7
7
 
8
8
  const CLAUDE_DIR = '.claude'
@@ -122,6 +122,32 @@ export function detectUnmigrated(
122
122
  return found
123
123
  }
124
124
 
125
+ /**
126
+ * A tracked surface the target still reads at its `.claude/` spelling, with
127
+ * nothing at the `canon/` one, and where `canon migrate surface-roots` moves it.
128
+ */
129
+ export interface UnmovedSurface {
130
+ readonly rel: string
131
+ readonly movesTo: string
132
+ }
133
+
134
+ /**
135
+ * Surfaces a target holds only at the root the surface move retired. Kept
136
+ * apart from `detectUnmigrated`, which models a domain stranded at the project
137
+ * root under a stamp domain, while a surface carries neither. A rule names the
138
+ * current layout only, so a target here stops loading the rules scoped to these
139
+ * files, and this report is what points it at the verb.
140
+ */
141
+ export function detectUnmovedSurfaces(target: string): UnmovedSurface[] {
142
+ return SURFACE_ENTRIES.filter(
143
+ (entry) =>
144
+ relative(target, surfaceDir(target, entry)) === join(CLAUDE_DIR, entry),
145
+ ).map((entry) => ({
146
+ rel: join(CLAUDE_DIR, entry),
147
+ movesTo: join('canon', spell('canon', entry)),
148
+ }))
149
+ }
150
+
125
151
  /**
126
152
  * Root files whose basename matches something the toolkit ships for this domain.
127
153
  * Basenames rather than relative paths, because the root layout an older toolkit
@@ -3,7 +3,6 @@ title: Architecture reference
3
3
  description: Shape and content rules for canon/ARCHITECTURE.md
4
4
  paths:
5
5
  - 'canon/ARCHITECTURE.md'
6
- - '.claude/ARCHITECTURE.md'
7
6
  rule:
8
7
  - "Add a decision here only when it fills one of the standard's slots, and write any other decision into the context entry for the domain it constrains. At the record's stated cap, merge or retire an entry before adding one."
9
8
  ---
@@ -3,7 +3,6 @@ title: Context entry reference
3
3
  description: Shape and content rules for canon/context/<domain>.md entries
4
4
  paths:
5
5
  - 'canon/context/**'
6
- - '.claude/context/**'
7
6
  rule:
8
7
  - 'Before non-trivial work in a domain, read its `canon/context/<domain>.md` entry. Use `canon/context/index.md` to find it.'
9
8
  - 'Leave the entry conforming when work in its domain changes what it describes.'
@@ -3,7 +3,6 @@ title: Design reference
3
3
  description: Shape and content rules for canon/DESIGN.md
4
4
  paths:
5
5
  - 'canon/DESIGN.md'
6
- - '.claude/DESIGN.md'
7
6
  ---
8
7
 
9
8
  # Design reference
@@ -3,12 +3,9 @@ title: Diagram reference
3
3
  description: Shape and content rules for .canon/diagrams/<kind>.md files
4
4
  paths:
5
5
  - '.canon/diagrams/**'
6
- - '.claude/DIAGRAMS.md'
7
6
  rule:
8
7
  - 'Layout, budgets, accessibility, labels, and render verification inside the fence are a separate topic. The mermaid standard routes them.'
9
8
  - 'A diagram entry carries structure and flow, not implementation. Read the standard before adding or revising a kind.'
10
- - 'Write a new diagram to `.canon/diagrams/<kind>.md`, never to `.claude/DIAGRAMS.md`'
11
- - 'Convert a `.claude/DIAGRAMS.md` left by an older install into per-kind entries before editing it'
12
9
  ---
13
10
 
14
11
  # Diagram reference
@@ -3,7 +3,6 @@ title: Requirements reference
3
3
  description: Shape and content rules for canon/REQUIREMENTS.md
4
4
  paths:
5
5
  - 'canon/REQUIREMENTS.md'
6
- - '.claude/REQUIREMENTS.md'
7
6
  ---
8
7
 
9
8
  # Requirements reference
package/standards/rule.md CHANGED
@@ -89,6 +89,7 @@ paths:
89
89
  - Cut any rule that resists crisp one-line phrasing. Vague guidance is worse than none.
90
90
  - Keep the file to one topic. A second topic is a second rule file.
91
91
  - Do not restate a rule that a sibling rule or `CLAUDE.md` already owns. Point once, never duplicate.
92
+ - Name the current layout only, in a glob and in the body alike. A layout change ships a migration verb, as `canon migrate surface-roots` and `canon migrate records` did, rather than a rule line or a glob that keeps the old spelling loading.
92
93
 
93
94
  ## Examples
94
95
 
@@ -3,7 +3,6 @@ title: Wireframe reference
3
3
  description: Shape and content rules for canon/wireframes/<surface>.md files
4
4
  paths:
5
5
  - 'canon/wireframes/**'
6
- - '.claude/wireframes/**'
7
6
  rule:
8
7
  - 'Read it before adding or revising a surface.'
9
8
  ---
@@ -19,15 +19,20 @@ IFS= read -r -d '' -t 2 input
19
19
  exit 1
20
20
  }
21
21
 
22
+ # Two shapes open a pull request. `gh pr create` names itself in the command.
23
+ # git-pr opens one over REST and prints `opened=true` only on that path, since
24
+ # its edit path prints a URL as well and is not an opening.
22
25
  command=$(printf '%s' "$input" | jq -r '.tool_input.command // empty')
26
+ stdout=$(printf '%s' "$input" | jq -r '.tool_response.stdout // empty')
23
27
  case "$command" in
24
28
  *"gh pr create"*) ;;
25
- *) exit 0 ;;
29
+ *)
30
+ printf '%s\n' "$stdout" | grep -qx 'opened=true' || exit 0
31
+ ;;
26
32
  esac
27
33
 
28
- # gh pr create prints the new pull request's URL to stdout on success, so
34
+ # Either shape prints the new pull request's URL to stdout on success, so
29
35
  # matching it is what tells a creation apart from a failed or refused call.
30
- stdout=$(printf '%s' "$input" | jq -r '.tool_response.stdout // empty')
31
36
  url=$(printf '%s' "$stdout" | grep -Eo 'https://github\.com/[^[:space:]]+/pull/[0-9]+' | tail -1)
32
37
  [ -n "$url" ] || exit 0
33
38
 
@@ -1,37 +0,0 @@
1
- ---
2
- name: draft-context
3
- description: Why a brand-new canon/context/<domain>.md entry needs a catalog collision check and a confirm step, not the refresh path context-fold already owns
4
- ---
5
-
6
- # Context draft requirement
7
-
8
- ## Gap
9
-
10
- Without this skill, a session documenting a domain that has no context entry yet either invents a shape from memory or reaches for the refresh mechanism, which reads an existing entry and a diff and has neither to work from for a domain that never had one. Either way the entry ships with no read of `standards/context.md` and no check against the catalog for a sibling already covering the same ground under a different name.
11
-
12
- ## Must
13
-
14
- - Read `standards/context.md` before drafting, since the frontmatter contract and the three-question test are what make the entry arguable against a sibling
15
- - Check the domain against the existing catalog by slug and by title-and-description, not by slug alone, since a domain can be covered under a name the topic phrase does not guess
16
- - Default a brand-new entry to a flat file, since a fresh domain never holds the three or more sub-areas the standard requires before it earns a folder
17
- - Confirm the resolved path and the full content with the user before writing, since placement is a judgment call with no diff to preview it against
18
- - Run `canon indexes regen` on `canon/context/` after writing, so the catalog picks up the new domain immediately rather than drifting until the next unrelated regen
19
-
20
- ## Must not
21
-
22
- - Refresh or rewrite an existing entry. A domain already covered refuses toward the refresh mechanism.
23
- - Hand-edit `canon/context/index.md`. It regenerates from sibling frontmatter, and a hand edit is overwritten on the next regen.
24
- - Assume this skill's own invocation frequency needs no check. Whether anything reaches for it beyond an author typing its name has no answer at creation time, so a review pass some months in should read that back rather than take the assumption on faith.
25
-
26
- ## Guards
27
-
28
- - No domain given: stop and ask what domain the entry should cover.
29
- - The derived slug already resolves to `canon/context/<slug>.md` or `canon/context/<slug>/index.md`: stop and point at the refresh mechanism instead.
30
- - The catalog already covers the domain under a different name: stop the same way, checked against the titles and descriptions the Placement step already reads.
31
-
32
- ## Out of scope
33
-
34
- - Refreshing an existing `canon/context/<domain>.md` entry against a diff: `context-fold`
35
- - Drafting a `canon/wireframes/<surface>.md` file: `draft-wireframes`
36
- - Drafting a `docs/*.md` page: `draft-docs`
37
- - Drafting a standard or a governance rule: `create-standard`, `create-rule`
@@ -1,61 +0,0 @@
1
- ---
2
- name: draft-context
3
- description: Drafts a brand-new `canon/context/<domain>.md` entry against the context standard, checks the catalog for a name-or-topic collision, decides flat-file placement, confirms with the user, then writes. Use when asked to "write a context entry for X", "document the X domain", "add a context entry for X", or "create a canon/context page for X" where no existing entry covers the domain. Do NOT use to refresh an existing entry against a diff, which is `context-fold`.
4
- ---
5
-
6
- # Context draft
7
-
8
- Drafts one brand-new `canon/context/<domain>.md` entry end to end: read the standard, decide whether the domain is already covered under a different name, confirm the draft with the user, then write.
9
-
10
- A project the surface move has not reached keeps its context folder under `.claude/` rather than `canon/`. Check and write at whichever root already carries the folder, and create it under `canon/` only when neither does, since a new `canon/` folder would hide every entry the old one holds.
11
-
12
- Read these files in parallel:
13
-
14
- - `${CLAUDE_SKILL_DIR}/../../standards/context.md`: the three questions an entry must answer, its frontmatter, required and expected sections, and what stays out of it
15
- - `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: punctuation and formatting for all generated text
16
- - The `write-human` skill: voice, rhythm, and sentence construction for all generated text
17
-
18
- ## Guards
19
-
20
- - If no domain is given, stop: `❌ No domain given. Name the domain this entry should cover.`
21
- - Derive a kebab-case slug from the domain and check whether `canon/context/<slug>.md` or `canon/context/<slug>/index.md` already exists. Either resolving means the domain is already covered under that exact name. Stop: `❌ <slug> already has a context entry. Use context-fold to refresh it instead.`
22
-
23
- ## Placement
24
-
25
- - Read `canon/context/index.md` and check every title and description it lists against the domain. The Guards check above only catches an exact-slug collision, and a domain already covered under a different name still resolves here at no extra cost, since this read already runs.
26
- - Stop the same way on a match: `❌ <path> already covers this domain under a different name. Use context-fold to refresh it instead.`
27
- - Default a brand-new entry to a flat file, `canon/context/<slug>.md`. A domain starts as one page's worth of narrative, and the standard only splits it into a folder once it holds three or more sub-areas, which a fresh domain never does on day one.
28
-
29
- ## Draft
30
-
31
- - Read the domain's own folders and files well enough to answer the standard's three questions: where things live, why they are that way, and how to add one more of what the domain holds.
32
- - Draft `title` and `description` frontmatter, then `## Overview`, `## Layout` (folder ownership lines only, never a file-by-file list), and `## Decisions` or `## Gotchas` wherever the domain's history supplies a non-obvious choice or a workaround worth preserving. Omit an expected section with nothing to put in it rather than padding it.
33
- - Draft against `${CLAUDE_SKILL_DIR}/../../standards/context.md`'s template and ordering: `Overview`, `Layout`, `Decisions`, `Gotchas`, then anything else.
34
-
35
- ## Confirm
36
-
37
- - Show the resolved path and the full drafted content before writing.
38
- - Confirm both with the user. This skill waits for that answer rather than treating the tool permission dialog as the gate, since placement here is a judgment call with no diff to preview it against.
39
-
40
- ## Write
41
-
42
- - Write the file at the confirmed path, creating the folder when it is absent.
43
- - Run `canon markdown audit <path>`.
44
- - Run `canon indexes regen canon/context`, so the domain appears in `canon/context/index.md` immediately rather than drifting until the next unrelated regen.
45
-
46
- ## Response format
47
-
48
- ### Preview
49
-
50
- **Domain:** `<domain>`
51
- **Placement:** `<path>`
52
-
53
- ```markdown
54
- <drafted frontmatter and body>
55
- ```
56
-
57
- ### After confirmation
58
-
59
- ```plaintext
60
- ✅ Drafted: <path>
61
- ```
@@ -1,35 +0,0 @@
1
- ---
2
- name: draft-docs
3
- description: Why a brand-new docs/*.md page needs a placement decision and a confirm step, not the rewrite path docs-sync already owns
4
- ---
5
-
6
- # Docs draft requirement
7
-
8
- ## Gap
9
-
10
- Without this skill, a session drafting documentation for a surface that has no page yet either invents structure from memory or reaches for `docs-sync`, which has nothing to diff a nonexistent page against and reports the topic as unrelated to any change. Either way the page ships with no read of `standards/docs.md` and no catalog placement decided against the folder's actual shelves.
11
-
12
- ## Must
13
-
14
- - Read `standards/docs.md` before drafting, since the frontmatter contract and the four reader questions are what make the page arguable against a sibling
15
- - Decide placement from the existing catalog rather than guessing a folder, reusing a `category` value verbatim when one fits and defaulting to the `docs/` root when none does
16
- - Confirm the resolved path and the full content with the user before writing, since placement is a judgment call with no diff to preview it against
17
- - Run `canon indexes regen` on the containing folder after writing, so the folder's `index.md` picks up the new page immediately rather than drifting until the next unrelated regen
18
-
19
- ## Must not
20
-
21
- - Rewrite an existing page. A topic already covered by a page refuses toward `docs-sync`.
22
- - Hand-edit an `index.md`. It regenerates from sibling frontmatter, and a hand edit is overwritten on the next regen.
23
- - Build a hand-drawn diagram-and-capture authoring loop. `standards/docs.md`'s Mermaid-fence permission is the only path to a diagram this skill takes.
24
-
25
- ## Guards
26
-
27
- - No topic given: stop and ask what surface the page should cover.
28
- - The derived slug already resolves through `canon docs <slug>`: stop and point at `docs-sync` instead. This catches an exact-name collision only.
29
- - A page in the catalog already covers the topic under a different name: stop the same way, checked against the titles and descriptions Placement already reads.
30
-
31
- ## Out of scope
32
-
33
- - Rewriting or syncing an existing `docs/*.md` section against a diff since main: `docs-sync`
34
- - The context entries, requirements, architecture, wireframes, and task board: `context-fold`
35
- - Drafting a standard or a governance rule: `create-standard`, `create-rule`
@@ -1,59 +0,0 @@
1
- ---
2
- name: draft-docs
3
- description: Drafts a brand-new `docs/*.md` page against the docs standard, decides its placement in the existing catalog, confirms with the user, then writes. Use when asked to "add a docs page for X", "write a new doc for X", "document X under docs/", or "create a docs page for X" where no existing page covers the topic. Do NOT use to rewrite or sync an existing `docs/*.md` section against a diff, which is `docs-sync`.
4
- ---
5
-
6
- # Docs draft
7
-
8
- Drafts one brand-new `docs/*.md` page end to end: read the standard, decide where the page belongs in the existing catalog, confirm the draft with the user, then write.
9
-
10
- Read these files in parallel:
11
-
12
- - `${CLAUDE_SKILL_DIR}/../../standards/docs.md`: the reader a page serves, its frontmatter, structure, and what it links out to rather than restates
13
- - `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: punctuation and formatting for all generated text
14
- - The `write-human` skill: voice, rhythm, and sentence construction for all generated text
15
-
16
- ## Guards
17
-
18
- - If no topic is given, stop: `❌ No topic given. Name the surface this page should cover.`
19
- - Derive a kebab-case slug from the topic and run `canon docs <slug>`. A resolved page means the topic is already covered under that exact name. Stop: `❌ <slug> already resolves to an existing page. Run canon:docs-sync instead.` This is a name match rather than a topic match. Placement below checks the wider case.
20
-
21
- ## Placement
22
-
23
- - Read `docs/index.md` and its sub-catalogs for the closest existing `category`. <!-- canon-allow-reference: illustrates the target project's own docs/ tree, not a citation of this repository's own corpus -->
24
- - Check every page title and description this read surfaces against the topic. The Guards check above only catches an exact-slug collision, and a page already covering the same subject under a different slug still resolves here at no extra cost, since this read already runs. Stop the same way on a match: `❌ <path> already covers this topic under a different name. Run canon:docs-sync instead.`
25
- - Reuse a matching `category` value verbatim. A near-miss spelling opens a second shelf holding one page, per the docs standard.
26
- - Default to the `docs/` root with no `category` when nothing fits. A subfolder earns itself only once a shelf of pages already sits there, per the standard's splitting rule.
27
-
28
- ## Draft
29
-
30
- - Draft `title`, `description`, and `category` (where one applies), then the page body, against `${CLAUDE_SKILL_DIR}/../../standards/docs.md`.
31
- - Write for a reader with no source open. The standard's four questions are the test: what the surface is, what to run or write, what it refuses, and where to go for the adjacent surface.
32
-
33
- ## Confirm
34
-
35
- - Show the resolved path and the full drafted content before writing.
36
- - Confirm both with the user. This skill waits for that answer rather than treating the tool permission dialog as the gate, since placement here is a judgment call with no diff to preview it against.
37
-
38
- ## Write
39
-
40
- - Write the file at the confirmed path, creating the folder when it is absent.
41
- - Run `canon markdown audit <path>`.
42
- - Run `canon indexes regen <folder>` on the containing folder, so its `index.md` picks up the new page.
43
-
44
- ## Response format
45
-
46
- ### Preview
47
-
48
- **Topic:** `<topic>`
49
- **Placement:** `<path>` (category: `<category-or-root>`)
50
-
51
- ```markdown
52
- <drafted frontmatter and body>
53
- ```
54
-
55
- ### After confirmation
56
-
57
- ```plaintext
58
- ✅ Drafted: <path>
59
- ```
@@ -1,38 +0,0 @@
1
- ---
2
- name: draft-readme
3
- description: Why a README needs a project-type read and a confirm step, not the rewrite path docs-sync already owns
4
- ---
5
-
6
- # Readme draft requirement
7
-
8
- ## Gap
9
-
10
- Without this skill, a session drafting a README either copies the generic template from memory or reaches for `docs-sync`, which has nothing to diff a nonexistent page against and reports the topic as unrelated to any change. A scaffold-written stub meets the same dead end, since no diff touches it either. Either way the page ships with no read of `standards/readme.md`, no read of what the project actually is, and no badge chosen against its rendered value rather than its service name.
11
-
12
- A README whose real documentation lives in `canon/context/` or `docs/` was a second gap the standard itself carried until this build: nothing named a pointer page as complete, so a session drafting one either padded the page or read the required list as unsatisfied.
13
-
14
- ## Must
15
-
16
- - Read `standards/readme.md` before drafting, since its `## Voice` section is scoped to a repository-root README and yields to `write-human`'s reference voice everywhere else
17
- - Detect every project type the standard's `## Content` list applies to, from the manifest and the tree, rather than drafting against the closest single type
18
- - Treat an existing README with no H1, or with headings that name only the scaffold that wrote it, as unedited generator output to draft over rather than a page to sync section by section
19
- - Read the header block's slots off the repository, being the mark, the claim, the live link, and the product screenshot, and skip the link and the screenshot for a project with no page, so every element traces to a file or field the project holds
20
- - Verify a candidate badge by the value it would render rather than by a fetch's status code, since a badge service answers 200 for a query it cannot satisfy
21
- - Confirm the resolved path, the detected types, the badge candidates, and the full content with the user before writing, since project-type detection and badge selection are judgment calls with no diff to preview them against
22
-
23
- ## Must not
24
-
25
- - Rewrite an authored README. One carrying an H1 that names the project refuses toward `docs-sync`.
26
- - Offer to overwrite an authored README on the refusal path, or propose conforming it to what this skill would have drafted. A target project's own citations into its README are invisible from here, so the refusal reports rather than proposes.
27
- - Invent a screenshot or a mark the project does not hold, or write a placeholder path for one. An omitted slot is a correct page and an invented image is the failure.
28
- - Assume this skill's own invocation frequency needs no check. Whether anything reaches for it beyond an author typing its name has no answer at creation time, so a review pass some months in should read that back rather than take the assumption on faith.
29
-
30
- ## Guards
31
-
32
- - The target already exists and is authored, carrying an H1 that names the project: stop and point at `docs-sync` instead.
33
-
34
- ## Out of scope
35
-
36
- - Rewriting or syncing an existing authored `README.md` against a diff since main: `docs-sync`
37
- - The consumer-facing reference under `docs/`: `draft-docs`
38
- - Product scope and goals, and every other surface `standards/readme.md` already scopes out of its own governance
@@ -1,73 +0,0 @@
1
- ---
2
- name: draft-readme
3
- description: Drafts a project's README.md against the readme standard, detecting project type and badge candidates, and confirming with the user before write. Use when asked to "write a README", "draft a README for this project", "add a README", or "create a README.md" where none exists yet or the existing one is unedited scaffold output. Do NOT use to rewrite or resync an existing authored README against a diff, which is `docs-sync`.
4
- ---
5
-
6
- # Readme draft
7
-
8
- Drafts a project's `README.md` end to end: read the standard, detect the project's shape, confirm the draft with the user, then write.
9
-
10
- Read these files in parallel:
11
-
12
- - `${CLAUDE_SKILL_DIR}/../../standards/readme.md`: voice, structure, required and optional sections, badge selection, and what to link out to rather than carry. Voice is claimed here for a repository-root README, and `write-human` yields it there while keeping rhythm, density, and the machine-tell catalog everywhere, root README included.
13
- - `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: punctuation and formatting for all generated text
14
- - The `write-human` skill: rhythm, density, and sentence construction for all generated text
15
-
16
- ## Guards
17
-
18
- - Default the target to `README.md` at the repository root when no path is given. A caller naming a path under a folder, a harness, or an internal tool is drafting a nested README instead, which keeps the reference voice per the standard's `## Voice` section rather than the root voice below.
19
- - Read the target if it exists.
20
- - It carries an H1 naming the project: it is authored. Stop: `❌ <path> already exists and covers the project. Run canon:docs-sync instead.`
21
- - It carries no H1, or its only headings restate the tool that scaffolded it rather than the project: it is unedited generator output. Continue, drafting over it rather than syncing its sections.
22
- - It does not exist: continue.
23
- - A hand-authored README opening with a badge block, or a title in some other form the H1 test misses, falls into the no-H1 branch the same as a scaffold page. The Confirm step below is what catches that case before the write happens, so treat it as load-bearing rather than a courtesy: never skip it on the reasoning that the guard already decided.
24
-
25
- ## Detect
26
-
27
- - Read the project's manifest (`package.json`, `pyproject.toml`, `Cargo.toml`, or equivalent) for a `bin` field or CLI entry point, an installable package name, and its declared dependencies.
28
- - Check for a `claude/skills/` or `.claude/skills/` folder, a `plugin.json`, or a marketplace manifest, each naming an agent-facing or marketplace-distributed surface.
29
- - A project is often several of these at once. Note every type that applies rather than stopping at the first match, since the Draft step covers each one the project actually is.
30
- - Check for a page. A dependency on a site framework or a site config file in the manifest or the tree marks a project as having one. Read a live URL separately, from a `homepage` field or a deploy config, since a project can have a page and no known URL.
31
- - A monorepo can carry a page in one package and a CLI in another, so both signals may fire. Report each rather than picking one.
32
- - Look for the mark and the screenshot among images the project already commits: scan the existing README for image references, then the asset and public folders.
33
-
34
- ## Draft
35
-
36
- - Draft the page against `${CLAUDE_SKILL_DIR}/../../standards/readme.md`: H1, a 2-3 sentence description in plain text, then the required sections, then whichever optional sections and per-type content the Detect step found.
37
- - Open the page with the standard's header block, filling each slot from what Detect found: mark, title, badges, a one-line claim from the manifest description, the live link, then the product screenshot.
38
- - Fill the screenshot slot only with an image the project already commits, referenced with alt text naming what it depicts. This skill cannot capture one. Omit the slot when no such image exists and say so in the preview. Never write a placeholder path.
39
- - Omit the link and the screenshot for a project with no page, and the mark for a project with none, without a note in the drafted page. A project with a page and no known URL gets the screenshot slot, and the Confirm step asks for the link.
40
- - Cover every applicable project type from the standard's `## Content` list rather than picking the closest one.
41
- - Candidate badges: check for a published package (a registry field in the manifest), a CI workflow, and a `LICENSE` file.
42
- - State each candidate's rendered value in the preview rather than trusting a fetch's status code, since a badge service answers 200 for a query it cannot satisfy.
43
- - Pin a status badge to the branch the standard names and confirm the workflow actually triggers on that branch before offering it. Zero badges is a correct answer when nothing passes the test.
44
-
45
- ## Confirm
46
-
47
- - Show the resolved path, the detected project types, the header slots filled or skipped with the reason for each, the badge candidates and what backs each one, and the full drafted content before writing.
48
- - The skill can confirm that an image file exists and is referenced, and it cannot verify the picture shows the product. Ask the user to look at it in the preview.
49
- - Confirm with the user. This skill waits for that answer rather than treating the tool permission dialog as the gate, since project-type detection and badge selection are judgment calls with no diff to preview them against.
50
-
51
- ## Write
52
-
53
- - Write the file at the confirmed path, creating the folder when it is absent.
54
- - Run `canon markdown audit <path>`.
55
-
56
- ## Response format
57
-
58
- ### Preview
59
-
60
- **Target:** `<path>` (root | nested)
61
- **Detected:** `<project types>`
62
- **Header:** `<each slot as filled (source) or skipped (reason)>`
63
- **Badges:** `<candidates, or none>`
64
-
65
- ```markdown
66
- <drafted H1 and body>
67
- ```
68
-
69
- ### After confirmation
70
-
71
- ```plaintext
72
- ✅ Drafted: <path>
73
- ```
@@ -1,42 +0,0 @@
1
- ---
2
- name: draft-wiki
3
- description: Why a brand-new wiki reference page needs both placement tests, a catalog collision check, and a sourced read before anything is written
4
- ---
5
-
6
- # Wiki draft requirement
7
-
8
- ## Gap
9
-
10
- Without this skill, a session asked for a wiki page writes one from training knowledge, which is the single thing the wiki standard forbids outright, and nothing in the resulting file shows that it happened. It also files the page by where the request came from rather than by who owns the subject, so material about the project itself lands in the wiki and material about a third-party tool acquires a folder the standard never sanctioned.
11
-
12
- The standard can state all of this and still be read by nothing, which is the condition this skill was written into in the toolkit. A page written under that condition conforms by the author's memory rather than by a read, and it ships looking identical to one that was checked.
13
-
14
- ## Must
15
-
16
- - Read the wiki standard before drafting, since the placement tests and the sourcing rule are the two things a session gets wrong from memory
17
- - Run both placement tests and refuse on either, since a page passing one and failing the other is still non-conforming
18
- - Check the subject against the catalog by title and description, not by slug alone, since a subject can be covered under a name the slug does not guess
19
- - Build the draft on a fetched read and stop when the fetch fails, since a page written from recall is indistinguishable from a checked one once it ships
20
- - Confirm the path, the source, and the content with the user before writing, since the standard forbids writing a wiki file unasked
21
- - Name the folder by resolving what the standard states rather than by carrying a path in the body, since the body ships to projects whose layout this repository does not set
22
-
23
- ## Must not
24
-
25
- - Widen the folder split to admit a subject the second placement test rejects. That is a change to the standard, argued there.
26
- - Edit or refresh a page that already exists. A collision refuses toward editing the existing page rather than drafting a second one.
27
- - Substitute recall for a failed fetch, even for a subject the session believes it knows.
28
-
29
- ## Guards
30
-
31
- - No subject given: stop and ask what subject the page should cover.
32
- - The wiki root does not resolve: say so and ask before creating it, since a project holding no wiki has never taken the placement rule.
33
- - The slug already resolves to a page: stop and point at editing that page.
34
- - The catalog already covers the subject under a different name: stop the same way, checked against the titles and descriptions the Collision step already reads.
35
-
36
- ## Out of scope
37
-
38
- - Editing or refreshing a wiki page that already exists: a plain edit against the same standard
39
- - Drafting a `docs/*.md` page: `draft-docs`
40
- - Drafting a `canon/context/<domain>.md` entry: `draft-context`
41
- - Voice, rhythm, and sentence construction in the drafted page: the `write-human` skill
42
- - Punctuation, headings, and word choice in the drafted page: the markdown standard