@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.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/SKILL.md +1 -1
- package/claude/skills/auto-ship/references/draft-mark.md +8 -6
- package/claude/skills/draft-doc/REQUIREMENT.md +45 -0
- package/claude/skills/draft-doc/SKILL.md +77 -0
- package/claude/skills/draft-doc/references/context.md +31 -0
- package/claude/skills/draft-doc/references/docs.md +33 -0
- package/claude/skills/draft-doc/references/readme.md +58 -0
- package/claude/skills/draft-doc/references/wiki.md +53 -0
- package/claude/skills/draft-doc/references/wireframes.md +48 -0
- package/claude/skills/draft-figure/REQUIREMENT.md +1 -1
- package/claude/skills/draft-figure/SKILL.md +1 -1
- package/claude/skills/git-pr/SKILL.md +5 -46
- package/claude/skills/git-pr/references/final-command.md +75 -0
- package/claude/skills/git-pr/references/labels.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +76 -0
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +11 -1
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +5 -1
- package/claude/skills/search-craft/REQUIREMENT.md +4 -4
- package/claude/skills/search-craft/SKILL.md +1 -1
- package/docs/agents/commands.md +1 -1
- package/docs/agents/drift-surfaces.md +20 -9
- package/docs/agents/index.md +2 -2
- package/docs/agents/install-and-sync.md +3 -3
- package/docs/agents/migrate.md +1 -1
- package/docs/agents/rule-citations.md +1 -1
- package/docs/agents/slides.md +47 -3
- package/docs/agents/tasks.md +1 -1
- package/docs/target/migrations.md +3 -1
- package/docs/target/sync.md +1 -1
- package/docs/workflow/skill-map.md +14 -18
- package/governance/rules/canon/604-scratch.md +0 -1
- package/governance/rules/claude/575-hooks.md +1 -2
- package/governance/rules/claude/576-settings.md +1 -2
- package/governance/rules/standards/architecture.md +0 -1
- package/governance/rules/standards/context.md +0 -1
- package/governance/rules/standards/design.md +0 -1
- package/governance/rules/standards/diagrams.md +0 -3
- package/governance/rules/standards/requirements.md +0 -1
- package/governance/rules/standards/wireframes.md +0 -1
- package/package.json +1 -1
- package/scripts/manage-sandbox.sh +7 -8
- package/src/claude/cases/authoring.ts +5 -5
- package/src/commands/slides.ts +14 -4
- package/src/commands/sync.ts +10 -0
- package/src/gov/citations.ts +1 -2
- package/src/migrate/skill-names.ts +12 -0
- package/src/slides/convert/deck.ts +83 -3
- package/src/slides/convert/export.ts +67 -19
- package/src/slides/convert/shapes.ts +43 -3
- package/src/slides/convert/walk.ts +43 -1
- package/src/slides/package/fonts.ts +297 -0
- package/src/slides/package/motion.ts +316 -0
- package/src/slides/package/test-face.ts +113 -0
- package/src/sync/check.ts +9 -0
- package/src/sync/layout.ts +27 -1
- package/standards/architecture.md +0 -1
- package/standards/context.md +0 -1
- package/standards/design.md +0 -1
- package/standards/diagrams.md +0 -3
- package/standards/requirements.md +0 -1
- package/standards/rule.md +1 -0
- package/standards/wireframes.md +0 -1
- package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +8 -3
- package/claude/skills/draft-context/REQUIREMENT.md +0 -37
- package/claude/skills/draft-context/SKILL.md +0 -61
- package/claude/skills/draft-docs/REQUIREMENT.md +0 -35
- package/claude/skills/draft-docs/SKILL.md +0 -59
- package/claude/skills/draft-readme/REQUIREMENT.md +0 -38
- package/claude/skills/draft-readme/SKILL.md +0 -73
- package/claude/skills/draft-wiki/REQUIREMENT.md +0 -42
- package/claude/skills/draft-wiki/SKILL.md +0 -83
- package/claude/skills/draft-wireframes/REQUIREMENT.md +0 -38
- 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),
|
package/src/sync/layout.ts
CHANGED
|
@@ -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
|
---
|
package/standards/context.md
CHANGED
|
@@ -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.'
|
package/standards/design.md
CHANGED
package/standards/diagrams.md
CHANGED
|
@@ -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
|
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
|
|
package/standards/wireframes.md
CHANGED
|
@@ -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
|
-
*)
|
|
29
|
+
*)
|
|
30
|
+
printf '%s\n' "$stdout" | grep -qx 'opened=true' || exit 0
|
|
31
|
+
;;
|
|
26
32
|
esac
|
|
27
33
|
|
|
28
|
-
#
|
|
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
|