@erclx/canon 5.19.0 → 5.21.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 (135) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/auto-ship/references/verb-absent.md +1 -1
  3. package/claude/skills/create-skill/SKILL.md +1 -0
  4. package/claude/skills/docs-sync/REQUIREMENT.md +1 -1
  5. package/claude/skills/document-health/REQUIREMENT.md +1 -1
  6. package/claude/skills/draft-ready/REQUIREMENT.md +2 -1
  7. package/claude/skills/draft-ready/SKILL.md +6 -4
  8. package/claude/skills/markdown-propose/REQUIREMENT.md +3 -3
  9. package/claude/skills/markdown-propose/SKILL.md +1 -1
  10. package/claude/skills/plan-feature/SKILL.md +1 -1
  11. package/claude/skills/plan-groundwork/SKILL.md +1 -1
  12. package/claude/skills/record-screencast/REQUIREMENT.md +2 -1
  13. package/claude/skills/record-screencast/SKILL.md +2 -2
  14. package/claude/skills/role-worker/REQUIREMENT.md +3 -1
  15. package/claude/skills/role-worker/SKILL.md +5 -3
  16. package/claude/skills/session-resume/SKILL.md +1 -1
  17. package/claude/skills/task-board/REQUIREMENT.md +1 -0
  18. package/claude/skills/task-board/SKILL.md +6 -0
  19. package/claude/skills/task-board/references/decline.md +2 -0
  20. package/claude/skills/video-craft/REQUIREMENT.md +48 -0
  21. package/claude/skills/video-craft/SKILL.md +80 -0
  22. package/claude/skills/video-craft/references/adopted.md +66 -0
  23. package/docs/agents/canvas.md +4 -0
  24. package/docs/agents/commands.md +1 -0
  25. package/docs/agents/demo.md +1 -1
  26. package/docs/agents/index.md +1 -0
  27. package/docs/agents/install-and-sync.md +17 -3
  28. package/docs/agents/markdown-audit.md +1 -3
  29. package/docs/agents/rule-citations.md +4 -4
  30. package/docs/agents/sandbox.md +8 -7
  31. package/docs/agents/slides.md +120 -0
  32. package/docs/agents/tasks-validate.md +1 -1
  33. package/docs/agents/tasks.md +1 -1
  34. package/docs/target/migrations.md +1 -1
  35. package/docs/workflow/ai-workflow.md +1 -1
  36. package/docs/workflow/operating-model.md +9 -9
  37. package/docs/workflow/parallel-features.md +1 -1
  38. package/docs/workflow/skill-map.md +12 -13
  39. package/governance/renames.toml +24 -0
  40. package/governance/rules/lib/330-testing-py.md +1 -1
  41. package/governance/rules/lib/336-testing-php.md +1 -1
  42. package/governance/rules/{canon/640-architecture.md → standards/architecture.md} +3 -2
  43. package/governance/rules/standards/context.md +15 -0
  44. package/governance/rules/standards/design.md +13 -0
  45. package/governance/rules/standards/diagrams.md +17 -0
  46. package/governance/rules/standards/groundwork.md +13 -0
  47. package/governance/rules/standards/intake.md +12 -0
  48. package/governance/rules/standards/markdown.md +15 -0
  49. package/governance/rules/standards/memory.md +12 -0
  50. package/governance/rules/standards/mermaid.md +15 -0
  51. package/governance/rules/{canon/658-plan.md → standards/plan.md} +5 -9
  52. package/governance/rules/standards/readme.md +12 -0
  53. package/governance/rules/standards/ready.md +13 -0
  54. package/governance/rules/standards/requirements.md +13 -0
  55. package/governance/rules/standards/rule.md +13 -0
  56. package/governance/rules/standards/session.md +13 -0
  57. package/governance/rules/{claude/570-skill.md → standards/skill.md} +6 -10
  58. package/governance/rules/standards/standard.md +13 -0
  59. package/governance/rules/standards/tasks.md +15 -0
  60. package/governance/rules/{canon/661-teach.md → standards/teach.md} +3 -2
  61. package/governance/rules/standards/wiki.md +13 -0
  62. package/governance/rules/standards/wireframes.md +14 -0
  63. package/governance/rules/writing/800-prose.md +1 -1
  64. package/governance/stacks/base.toml +4 -2
  65. package/package.json +1 -1
  66. package/src/canvas/capture.ts +228 -15
  67. package/src/claude/cases/misc.ts +5 -0
  68. package/src/claude/cases/workflow.ts +4 -20
  69. package/src/claude/routing.ts +1 -1
  70. package/src/commands/canvas.ts +76 -2
  71. package/src/commands/gov.ts +19 -4
  72. package/src/commands/sandbox.ts +7 -0
  73. package/src/commands/slides.ts +13 -3
  74. package/src/commands/tasks.ts +46 -2
  75. package/src/gate/stages.ts +7 -1
  76. package/src/gov/citations.ts +1 -1
  77. package/src/gov/standard-rules.ts +179 -0
  78. package/src/migrate/skill-names.ts +0 -7
  79. package/src/sandbox/equivalence.ts +43 -14
  80. package/src/sandbox/stub-remote.ts +140 -0
  81. package/src/slides/convert/chart.ts +165 -0
  82. package/src/slides/convert/deck.ts +164 -0
  83. package/src/slides/convert/export.ts +217 -7
  84. package/src/slides/convert/master.ts +195 -0
  85. package/src/slides/convert/shapes.ts +1 -1
  86. package/src/slides/convert/walk.ts +187 -2
  87. package/src/tasks/record.ts +11 -1
  88. package/src/tasks/validate.ts +14 -0
  89. package/standards/architecture.md +5 -0
  90. package/standards/context.md +6 -0
  91. package/standards/design.md +3 -0
  92. package/standards/diagrams.md +8 -0
  93. package/standards/groundwork-spikes.md +66 -0
  94. package/standards/groundwork.md +5 -25
  95. package/standards/index.md +6 -2
  96. package/standards/intake.md +2 -0
  97. package/standards/markdown.md +6 -0
  98. package/standards/memory.md +2 -0
  99. package/standards/mermaid.md +6 -0
  100. package/standards/plan-lifecycle.md +36 -0
  101. package/standards/plan.md +13 -11
  102. package/standards/readme.md +2 -0
  103. package/standards/ready.md +4 -0
  104. package/standards/requirements.md +3 -0
  105. package/standards/rule.md +6 -1
  106. package/standards/session.md +4 -0
  107. package/standards/skill-paths.md +50 -0
  108. package/standards/skill.md +15 -23
  109. package/standards/standard.md +7 -1
  110. package/standards/tasks-decline.md +38 -0
  111. package/standards/tasks.md +9 -14
  112. package/standards/teach.md +5 -0
  113. package/standards/wiki.md +4 -0
  114. package/standards/wireframes.md +5 -0
  115. package/tooling/astro/configs/card-src/pages/og-card.astro +2 -2
  116. package/claude/skills/docs-fold/REQUIREMENT.md +0 -25
  117. package/claude/skills/docs-fold/SKILL.md +0 -9
  118. package/claude/skills/standards-audit/REQUIREMENT.md +0 -33
  119. package/claude/skills/standards-audit/SKILL.md +0 -99
  120. package/governance/rules/canon/610-context.md +0 -17
  121. package/governance/rules/canon/620-wireframes.md +0 -13
  122. package/governance/rules/canon/630-requirements.md +0 -12
  123. package/governance/rules/canon/650-design.md +0 -12
  124. package/governance/rules/canon/655-tasks.md +0 -13
  125. package/governance/rules/canon/656-groundwork.md +0 -11
  126. package/governance/rules/canon/657-intake.md +0 -11
  127. package/governance/rules/canon/659-memory.md +0 -11
  128. package/governance/rules/canon/660-diagrams.md +0 -19
  129. package/governance/rules/canon/662-session.md +0 -15
  130. package/governance/rules/canon/663-ready.md +0 -15
  131. package/governance/rules/claude/590-rule-authoring.md +0 -12
  132. package/governance/rules/writing/801-markdown.md +0 -14
  133. package/governance/rules/writing/802-mermaid.md +0 -14
  134. package/governance/rules/writing/880-readme.md +0 -11
  135. package/governance/rules/writing/891-standard-authoring.md +0 -12
@@ -1,3 +1,5 @@
1
+ import type { ChartRecord } from '@/slides/convert/chart'
2
+ import type { ThemeReading } from '@/slides/convert/master'
1
3
  import type {
2
4
  BoxStyle,
3
5
  ElementRecord,
@@ -9,10 +11,34 @@ import type {
9
11
  TextRun,
10
12
  } from '@/slides/convert/shapes'
11
13
 
14
+ /** How one slide treats a band the deck master draws. */
15
+ export type BandOverride =
16
+ | { readonly kind: 'master' }
17
+ | { readonly kind: 'off' }
18
+ | {
19
+ readonly kind: 'override'
20
+ readonly left?: string
21
+ readonly center?: string
22
+ readonly right?: string
23
+ }
24
+
25
+ /** What a slide declares about itself on `<body>` and in its notes. */
26
+ export interface SlideMeta {
27
+ readonly notes?: string
28
+ readonly section?: string
29
+ readonly isHidden: boolean
30
+ readonly header: BandOverride
31
+ readonly footer: BandOverride
32
+ /** The `--color-text` the slide's `<body>` computes, as hex. */
33
+ readonly textToken?: string
34
+ }
35
+
12
36
  /** What one laid-out slide hands back across `page.evaluate`. */
13
37
  export interface WalkedSlide {
14
38
  readonly background: Rgba
15
39
  readonly records: ElementRecord[]
40
+ readonly charts: ChartRecord[]
41
+ readonly meta: SlideMeta
16
42
  }
17
43
 
18
44
  /** Set on every walked element, so a screenshot finds the one a record names. */
@@ -27,7 +53,8 @@ export const ID_ATTRIBUTE = 'data-canon-id'
27
53
  * Text gathers along the inline flow. A block holding text takes every run its
28
54
  * inline descendants carry, so `<strong>` inside `<p>` is one shape with two
29
55
  * runs, while a block-level child is walked on its own. Images, SVGs, and
30
- * tables consume their whole subtree.
56
+ * tables consume their whole subtree. Speaker notes in `<aside class="notes">`
57
+ * and a chart's `<figure data-chart>` are read whole and never drawn as shapes.
31
58
  *
32
59
  * Each walked element is tagged with `idAttribute` set to its record id.
33
60
  */
@@ -293,10 +320,41 @@ export function walkSlide(idAttribute: string): WalkedSlide {
293
320
 
294
321
  const SKIPPED = new Set(['script', 'style', 'template', 'noscript', 'link'])
295
322
 
323
+ const cellText = (element: Element): string =>
324
+ (element.textContent ?? '').replace(/\s+/g, ' ').trim()
325
+
326
+ /** One line per block, so a paragraph break survives and no tag does. */
327
+ const notesText = (element: Element): string => {
328
+ const clone = element.cloneNode(true)
329
+ if (!(clone instanceof Element)) return ''
330
+ for (const br of Array.from(clone.querySelectorAll('br'))) {
331
+ br.replaceWith('\n')
332
+ }
333
+ for (const block of Array.from(
334
+ clone.querySelectorAll('p, li, div, blockquote, h1, h2, h3, h4, h5, h6'),
335
+ )) {
336
+ block.append('\n')
337
+ }
338
+ return (clone.textContent ?? '')
339
+ .split('\n')
340
+ .map((line) => line.replace(/\s+/g, ' ').trim())
341
+ .filter(Boolean)
342
+ .join('\n')
343
+ }
344
+
345
+ const notes: string[] = []
346
+ const charts: ChartRecord[] = []
347
+
296
348
  for (const element of Array.from(document.body.querySelectorAll('*'))) {
297
349
  if (consumed.has(element)) continue
298
350
  const tag = element.tagName.toLowerCase()
299
351
  if (SKIPPED.has(tag)) continue
352
+ if (tag === 'aside' && element.classList.contains('notes')) {
353
+ consumeSubtree(element)
354
+ const text = notesText(element)
355
+ if (text) notes.push(text)
356
+ continue
357
+ }
300
358
  if (!element.checkVisibility({ visibilityProperty: true })) continue
301
359
 
302
360
  const id = records.length + 1
@@ -313,6 +371,27 @@ export function walkSlide(idAttribute: string): WalkedSlide {
313
371
  ids.set(element, id)
314
372
  element.setAttribute(idAttribute, String(id))
315
373
 
374
+ if (tag === 'figure' && element.hasAttribute('data-chart')) {
375
+ consumeSubtree(element)
376
+ records.push({ ...base, kind: 'box' })
377
+ const table = element.querySelector('table')
378
+ const caption = element.querySelector('figcaption')
379
+ const title = caption ? cellText(caption) : ''
380
+ charts.push({
381
+ selector: base.selector,
382
+ box: bounds,
383
+ type: element.getAttribute('data-chart') ?? '',
384
+ hasLabels: element.hasAttribute('data-labels'),
385
+ ...(title ? { title } : {}),
386
+ rows: table
387
+ ? Array.from(table.rows).map((row) =>
388
+ Array.from(row.cells).map(cellText),
389
+ )
390
+ : [],
391
+ })
392
+ continue
393
+ }
394
+
316
395
  if (element instanceof HTMLImageElement) {
317
396
  records.push({
318
397
  ...base,
@@ -440,5 +519,111 @@ export function walkSlide(idAttribute: string): WalkedSlide {
440
519
  ? htmlGround
441
520
  : { hex: 'FFFFFF', alpha: 1 }
442
521
 
443
- return { background, records }
522
+ const body = document.body
523
+ const band = (kind: 'header' | 'footer'): BandOverride => {
524
+ if (body.getAttribute(`data-${kind}`) === 'off') return { kind: 'off' }
525
+ const slots: { left?: string; center?: string; right?: string } = {}
526
+ for (const slot of ['left', 'center', 'right'] as const) {
527
+ const value = body.getAttribute(`data-${kind}-${slot}`)
528
+ if (value !== null) slots[slot] = value
529
+ }
530
+ return Object.keys(slots).length > 0
531
+ ? { kind: 'override', ...slots }
532
+ : { kind: 'master' }
533
+ }
534
+ const section = body.getAttribute('data-section')?.trim()
535
+ const textValue = getComputedStyle(body)
536
+ .getPropertyValue('--color-text')
537
+ .trim()
538
+ const meta: SlideMeta = {
539
+ ...(textValue ? { textToken: rgba(textValue).hex } : {}),
540
+ ...(notes.length > 0 ? { notes: notes.join('\n\n') } : {}),
541
+ ...(section ? { section } : {}),
542
+ isHidden:
543
+ body.hasAttribute('data-hidden') &&
544
+ body.getAttribute('data-hidden') !== 'false',
545
+ header: band('header'),
546
+ footer: band('footer'),
547
+ }
548
+
549
+ return { background, records, charts, meta }
550
+ }
551
+
552
+ /**
553
+ * Runs inside the laid-out page and reads the colors and face the deck master
554
+ * takes. Tokens come from `<html>`'s computed style, so a slide that switches
555
+ * theme on its own `<body>`, such as a dark cover, leaves the master on the
556
+ * project's root theme. The body supplies the fallback colors and the face.
557
+ * Serialized to source like `walkSlide`, so its helpers sit in its own body.
558
+ */
559
+ export function readTheme(): ThemeReading {
560
+ const canvas = document.createElement('canvas')
561
+ canvas.width = 1
562
+ canvas.height = 1
563
+ const context = canvas.getContext('2d', { willReadFrequently: true })
564
+ const hex = (value: string): string | undefined => {
565
+ if (!context || !value || value === 'transparent') return undefined
566
+ context.clearRect(0, 0, 1, 1)
567
+ context.fillStyle = '#000000'
568
+ context.fillStyle = value
569
+ context.fillRect(0, 0, 1, 1)
570
+ const [r = 0, g = 0, b = 0, a = 0] = context.getImageData(0, 0, 1, 1).data
571
+ if (a === 0) return undefined
572
+ return [r, g, b]
573
+ .map((channel) => channel.toString(16).padStart(2, '0'))
574
+ .join('')
575
+ .toUpperCase()
576
+ }
577
+
578
+ const style = getComputedStyle(document.body)
579
+ const rootStyle = getComputedStyle(document.documentElement)
580
+ const token = (name: string): string | undefined => {
581
+ const value = rootStyle.getPropertyValue(name).trim()
582
+ return value ? hex(value) : undefined
583
+ }
584
+
585
+ const names: string[] = []
586
+ for (const sheet of Array.from(document.styleSheets)) {
587
+ let rules: CSSRuleList
588
+ try {
589
+ rules = sheet.cssRules
590
+ } catch {
591
+ continue
592
+ }
593
+ for (const rule of Array.from(rules)) {
594
+ if (!(rule instanceof CSSStyleRule)) continue
595
+ for (const name of Array.from(rule.style)) {
596
+ if (name.startsWith('--color-') && !names.includes(name)) {
597
+ names.push(name)
598
+ }
599
+ }
600
+ }
601
+ }
602
+
603
+ const tokens: {
604
+ background?: string
605
+ text?: string
606
+ muted?: string
607
+ accent?: string
608
+ } = {}
609
+ for (const role of ['background', 'text', 'muted', 'accent'] as const) {
610
+ const value = token(`--color-${role}`)
611
+ if (value) tokens[role] = value
612
+ }
613
+
614
+ return {
615
+ tokens,
616
+ body: {
617
+ background:
618
+ hex(style.backgroundColor) ??
619
+ hex(getComputedStyle(document.documentElement).backgroundColor) ??
620
+ 'FFFFFF',
621
+ color: hex(style.color) ?? '000000',
622
+ },
623
+ fontFamily: style.fontFamily,
624
+ roles: names.flatMap((name) => {
625
+ const value = token(name)
626
+ return value ? [{ name, hex: value }] : []
627
+ }),
628
+ }
444
629
  }
@@ -512,12 +512,18 @@ export async function recordPlan(
512
512
  * line, so the merge of an earlier slice cannot archive the task in the window
513
513
  * before this branch's own number is recorded. A rerun that ticks nothing adds
514
514
  * no marker, since its branch has already recorded or is already listed.
515
+ *
516
+ * `branchPulls` holds the pull request numbers the branch's own head carries.
517
+ * One already on the task's `Pull request:` line means the branch recorded its
518
+ * number, so a marker would outlive the branch with nothing left to clear it.
519
+ * An absent or empty list keeps the marker, which is the first slice's case.
515
520
  */
516
521
  export async function closeOutcomes(
517
522
  root: string,
518
523
  selector: RecordSelector,
519
524
  positions: readonly number[],
520
525
  branch?: string,
526
+ branchPulls: readonly number[] = [],
521
527
  ): Promise<CloseOutcome> {
522
528
  const opened = await openTask(root, selector)
523
529
  if ('ok' in opened) return opened
@@ -541,8 +547,12 @@ export async function closeOutcomes(
541
547
  )
542
548
  }
543
549
 
550
+ const recorded = readPullRequest(result.text).some((number) =>
551
+ branchPulls.includes(number),
552
+ )
553
+
544
554
  const text =
545
- result.closed.length > 0 && branch
555
+ result.closed.length > 0 && branch && !recorded
546
556
  ? writePendingBranch(result.text, branch).text
547
557
  : result.text
548
558
 
@@ -6,6 +6,7 @@ import {
6
6
  declinedDir,
7
7
  isReservedStem,
8
8
  readOutcomes,
9
+ readPendingBranches,
9
10
  readPlanTargets,
10
11
  readPullRequest,
11
12
  resolveLivePlan,
@@ -1065,6 +1066,10 @@ function nothing(): CitedResult {
1065
1066
  * about, and a task naming none leaves the row untested rather than settled,
1066
1067
  * because the only local signal left is the checkbox that produced the defect.
1067
1068
  *
1069
+ * A task still carrying a pending branch is untested whatever its recorded
1070
+ * pull request says, since a slice is in flight that has recorded no number yet
1071
+ * and the landed one may belong to an earlier slice.
1072
+ *
1068
1073
  * The outcome list comes off `readOutcomes` rather than a pattern of its own,
1069
1074
  * so this check cannot disagree with the archive and outcome verbs about which
1070
1075
  * checkboxes are outcomes and which sit inside a block a task displays.
@@ -1114,6 +1119,15 @@ async function checkCitedTask(
1114
1119
  const { open, closed } = readOutcomes(text)
1115
1120
  if (open.length > 0 || closed.length === 0) return nothing()
1116
1121
 
1122
+ const pending = readPendingBranches(text)
1123
+ if (pending.length > 0) {
1124
+ return untestedRow(
1125
+ group,
1126
+ subject,
1127
+ `waits on ${cited}, which closed every outcome but holds a pending branch (${pending.join(', ')}) that has yet to record its pull request.`,
1128
+ )
1129
+ }
1130
+
1117
1131
  // The last number is the newest slice, and an earlier one closed without
1118
1132
  // merging never lands, so only the last says whether the work reached trunk.
1119
1133
  const pullRequest = readPullRequest(text).at(-1)
@@ -1,6 +1,11 @@
1
1
  ---
2
2
  title: Architecture reference
3
3
  description: Shape and content rules for canon/ARCHITECTURE.md
4
+ paths:
5
+ - 'canon/ARCHITECTURE.md'
6
+ - '.claude/ARCHITECTURE.md'
7
+ rule:
8
+ - "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."
4
9
  ---
5
10
 
6
11
  # Architecture reference
@@ -1,6 +1,12 @@
1
1
  ---
2
2
  title: Context entry reference
3
3
  description: Shape and content rules for canon/context/<domain>.md entries
4
+ paths:
5
+ - 'canon/context/**'
6
+ - '.claude/context/**'
7
+ rule:
8
+ - 'Before non-trivial work in a domain, read its `canon/context/<domain>.md` entry. Use `canon/context/index.md` to find it.'
9
+ - 'Leave the entry conforming when work in its domain changes what it describes.'
4
10
  ---
5
11
 
6
12
  # Context entry reference
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  title: Design reference
3
3
  description: Shape and content rules for canon/DESIGN.md
4
+ paths:
5
+ - 'canon/DESIGN.md'
6
+ - '.claude/DESIGN.md'
4
7
  ---
5
8
 
6
9
  # Design reference
@@ -1,6 +1,14 @@
1
1
  ---
2
2
  title: Diagram reference
3
3
  description: Shape and content rules for .canon/diagrams/<kind>.md files
4
+ paths:
5
+ - '.canon/diagrams/**'
6
+ - '.claude/DIAGRAMS.md'
7
+ rule:
8
+ - 'Layout, budgets, accessibility, labels, and render verification inside the fence are a separate topic. The mermaid standard routes them.'
9
+ - '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'
4
12
  ---
5
13
 
6
14
  # Diagram reference
@@ -0,0 +1,66 @@
1
+ ---
2
+ title: Groundwork spikes reference
3
+ description: What each spike in a track's 08-spikes.md records, the sample rule, and the three folders that hold what a spike produced
4
+ ---
5
+
6
+ # Groundwork spikes reference
7
+
8
+ Applies to `08-spikes.md` inside a groundwork track at `.canon/groundwork/<nn>-<slug>/`, and to the folders beside it that hold what a spike produced. It records evidence by experiment, beside the evidence by measurement `01-current-state.md` holds.
9
+
10
+ ## Scope
11
+
12
+ Governs `08-spikes.md`, what each spike in it carries, how a spike is sized, and the `evidence/`, `scripts/`, and `clones/` folders in a track.
13
+
14
+ Does not govern:
15
+
16
+ - The track's folder layout, its other files, frontmatter, and numbering: `groundwork.md`
17
+ - Running a spike, which belongs to the surface driving the track
18
+
19
+ ## What a working spike record looks like
20
+
21
+ A spike record works when a later reader can re-run each spike and trust what it settled:
22
+
23
+ - Which open question did each spike answer, and did it close it?
24
+ - Can the method be re-run from the file, against the fixture it names?
25
+ - What did it cost, and what does the result not prove?
26
+
27
+ ## Spikes
28
+
29
+ The file is optional, since measuring what is already there settles most questions.
30
+
31
+ Each spike carries four things:
32
+
33
+ - The open question it answers, named by file and number. A spike attached to no question is a runaway.
34
+ - The method, stated fully enough for a later reader to re-run it. Name the fixture and where it lived, since an arm pointed at a fixture inside the project measured the project, plus the exact command and the repetition count.
35
+ - The result, and which question it closes. Record a spike that settles nothing too, so a later pass does not pay for it twice.
36
+ - The measured cost, even for a single read, and the caveats bounding what the result proves. Cost is a report rather than a limit, and it makes the next spike estimable.
37
+
38
+ Start a spike against a sample bounded on input size, duration, and spend, and scale up only once it shows the method works. Record the sample's bounds beside the figure for the full input.
39
+
40
+ ## What a spike produced
41
+
42
+ A file a reader opens to verify how a result was produced or what it showed lives inside the track, and bulk input such as a large fixture stays outside it. Three unnumbered subfolders carry the first kind, since a reader reaches them from the citing claim rather than in read order:
43
+
44
+ - `evidence/`: what a run produced and the record cites, being a recording, a render, or a frame pulled from one, beside the file citing it.
45
+ - `scripts/`: the arm scripts and harnesses a spike ran, which a reader opens to check the method.
46
+ - `clones/`: checkouts and copies of outside material a claim rests on, kept small enough to hold in the folder. A checkout too large to keep is cited by its address and commit instead.
47
+
48
+ Open no other subfolder: `spikes/`, `web/`, and `research/` fold into these three, with a fetched page in `clones/` and a reference-site screenshot in `evidence/`.
49
+
50
+ Reach for a test harness the project already carries before building one. An experiment no existing harness can express is a finding for the folder, not a new abstraction.
51
+
52
+ Never count transcript matches to show a file was read. An instruction naming a path puts it in the transcript whether or not anything opened it, so the check is the tool call.
53
+
54
+ ## Template
55
+
56
+ ```markdown
57
+ # Spikes
58
+
59
+ ## <nn>. <what the spike tests>
60
+
61
+ - Question: <file and number of the open question>
62
+ - Method: <fixture and where it lived, exact command, repetition count>
63
+ - Result: <what it showed, and which question it closes>
64
+ - Cost: <measured cost>, <sample bounds beside the full-input figure>
65
+ - Caveats: <what the result does not prove>
66
+ ```
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  title: Groundwork reference
3
3
  description: Folder layout, ordinal naming, reserved numbering, frontmatter and dating, required file contents, and conventions for a measurement track
4
+ paths:
5
+ - '.canon/groundwork/**'
6
+ rule:
7
+ - 'Follow the groundwork spikes standard for `08-spikes.md` and the folders holding what a spike produced. Read it with `canon standards groundwork-spikes`.'
4
8
  ---
5
9
 
6
10
  # Groundwork reference
@@ -15,6 +19,7 @@ Governs a groundwork track under `.canon/groundwork/<nn>-<slug>/`: folder layout
15
19
 
16
20
  Does not govern:
17
21
 
22
+ - `08-spikes.md`, what each spike records, and the folders holding what a spike produced: `groundwork-spikes.md`
18
23
  - A dump of many findings filed by domain, each carrying its own verdict: `intake.md`
19
24
  - The feature plan a closed track feeds, and the contract its answer slots keep: `plan.md`
20
25
  - The task file a closing track writes, and the origin line pointing back at the folder: `tasks.md`
@@ -110,31 +115,6 @@ The dropped list is what stops a future session re-proposing something already r
110
115
 
111
116
  Written to survive a compaction that loses the conversation. It repeats facts held elsewhere in the folder rather than pointing at them. That duplication is correct here and wrong everywhere else.
112
117
 
113
- ## 08-spikes.md
114
-
115
- Evidence by experiment, beside the evidence by measurement `01-current-state.md` holds. Optional, since measuring what is already there settles most questions.
116
-
117
- Each spike carries four things:
118
-
119
- - The open question it answers, named by file and number. A spike attached to no question is a runaway.
120
- - The method, stated fully enough for a later reader to re-run it. Name the fixture and where it lived, since an arm pointed at a fixture inside the project measured the project, plus the exact command and the repetition count.
121
- - The result, and which question it closes. Record a spike that settles nothing too, so a later pass does not pay for it twice.
122
- - The measured cost, even for a single read, and the caveats bounding what the result proves. Cost is a report rather than a limit, and it makes the next spike estimable.
123
-
124
- Start a spike against a sample bounded on input size, duration, and spend, and scale up only once it shows the method works. Record the sample's bounds beside the figure for the full input.
125
-
126
- A file a reader opens to verify how a result was produced or what it showed lives inside the track, and bulk input such as a large fixture stays outside it. Three unnumbered subfolders carry the first kind, since a reader reaches them from the citing claim rather than in read order:
127
-
128
- - `evidence/`: what a run produced and the record cites, being a recording, a render, or a frame pulled from one, beside the file citing it.
129
- - `scripts/`: the arm scripts and harnesses a spike ran, which a reader opens to check the method.
130
- - `clones/`: checkouts and copies of outside material a claim rests on, kept small enough to hold in the folder. A checkout too large to keep is cited by its address and commit instead.
131
-
132
- Open no other subfolder: `spikes/`, `web/`, and `research/` fold into these three, with a fetched page in `clones/` and a reference-site screenshot in `evidence/`.
133
-
134
- Reach for a test harness the project already carries before building one. An experiment no existing harness can express is a finding for the folder, not a new abstraction.
135
-
136
- Never count transcript matches to show a file was read. An instruction naming a path puts it in the transcript whether or not anything opened it, so the check is the tool call.
137
-
138
118
  ## Open questions
139
119
 
140
120
  Every open question carries a lean, wherever it appears. A bare numbered list hands the reader a quiz and defers the judgment the track exists to inform.
@@ -17,12 +17,14 @@ Reference docs for consistent authoring across the toolkit and target projects.
17
17
  - [Docs reference](docs.md): Reader and jurisdiction, frontmatter, page structure, what a page links out to, the diagram permission, and when a category earns a subfolder
18
18
  - [Figure reference](figures.md): When a figure earns its place, the render-first policy for a graph-shaped subject, freehand SVG as the escape hatch, and its wrapping, color, and accessibility rules
19
19
  - [Glossary reference](glossary.md): Frontmatter, entry shape, ordering, and the rules deciding which terms a glossary carries
20
+ - [Groundwork spikes reference](groundwork-spikes.md): What each spike in a track's 08-spikes.md records, the sample rule, and the three folders that hold what a spike produced
20
21
  - [Groundwork reference](groundwork.md): Folder layout, ordinal naming, reserved numbering, frontmatter and dating, required file contents, and conventions for a measurement track
21
22
  - [Intake reference](intake.md): Folder layout, ordinal naming, reserved index number, frontmatter and dating, the item template, the answer contract, and retrieval
22
23
  - [Markdown reference](markdown.md): Headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, file references, American spelling, and frontmatter wording
23
24
  - [Memory reference](memory.md): Filename and type prefix, frontmatter, the body shape per type, links between entries, and the lifecycle from write to retire
24
25
  - [Mermaid reference](mermaid.md): Direction and layout, node and edge budgets, accessibility fields, label punctuation, and render verification for a Mermaid diagram
25
- - [Plan reference](plan.md): Filename and slug, required sections, the suggested-and-answer contract, and the lifecycle from the live folder to the archive
26
+ - [Plan lifecycle reference](plan-lifecycle.md): When a feature plan is written, where it lives, how it changes mid-flight, and its move to the archive
27
+ - [Plan reference](plan.md): Filename and slug, the required sections, and the suggested-and-answer contract for a feature plan
26
28
  - [Pull request reference](pr.md): Pull request title and body conventions
27
29
  - [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
28
30
  - [Readme reference](readme.md): Readme voice, structure, and content conventions
@@ -30,11 +32,13 @@ Reference docs for consistent authoring across the toolkit and target projects.
30
32
  - [Requirements reference](requirements.md): Shape and content rules for canon/REQUIREMENTS.md
31
33
  - [Governance rule reference](rule.md): Rule frontmatter, body shape, and voice for .claude/rules files
32
34
  - [Session map reference](session.md): Filename and location, the sections a handoff carries, the write and read procedures, and how a role extends it
35
+ - [Skill path reference](skill-paths.md): Which root a file a skill body names resolves against, and how a body cites a standard
33
36
  - [Practice skill reference](skill-practice.md): Closing sections and source ledger for a skill carrying engineering practice
34
37
  - [Skill requirement reference](skill-requirement.md): Shape and content rules for the REQUIREMENT.md beside a skill
35
- - [Claude skill reference](skill.md): Claude skill structure and authoring rules
38
+ - [Claude skill reference](skill.md): Skill structure and authoring rules for Claude Code
36
39
  - [Slug reference](slug.md): Transform from a git branch name to a slug, and the three responses to an empty result
37
40
  - [Standard reference](standard.md): Shape and content rules for authoring a standard
41
+ - [Task decline reference](tasks-decline.md): Where a task decided against moves, the Declined line it carries, and what happens to its plan and board row
38
42
  - [Tasks reference](tasks.md): Folder layout, filename convention, file format, origin lines, and archiving for task files under .canon/tasks/
39
43
  - [Teach reference](teach.md): Workspace layout, ordinal naming, frontmatter, and the mission and learning-record formats for a learning workspace
40
44
  - [Versioning reference](versioning.md): Phase label vs semver discipline across tasks, PRs, reviews, issues, commits, and tags
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  title: Intake reference
3
3
  description: Folder layout, ordinal naming, reserved index number, frontmatter and dating, the item template, the answer contract, and retrieval
4
+ paths:
5
+ - '.canon/intake/**'
4
6
  ---
5
7
 
6
8
  # Intake reference
@@ -1,6 +1,12 @@
1
1
  ---
2
2
  title: Markdown reference
3
3
  description: Headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, file references, American spelling, and frontmatter wording
4
+ paths:
5
+ - '**/*.md'
6
+ rule:
7
+ - 'Read it before a substantial markdown edit. Do not work the banned characters from memory.'
8
+ - 'Run `canon markdown audit <path>` after the edit, and rewrite the sentence carrying a hit rather than swapping the token for a near-synonym.'
9
+ - 'Voice, rhythm, and sentence construction are a separate topic. `800-prose` routes them.'
4
10
  ---
5
11
 
6
12
  # Markdown reference
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  title: Memory reference
3
3
  description: Filename and type prefix, frontmatter, the body shape per type, links between entries, and the lifecycle from write to retire
4
+ paths:
5
+ - '.canon/memory/**'
4
6
  ---
5
7
 
6
8
  # Memory reference
@@ -1,6 +1,12 @@
1
1
  ---
2
2
  title: Mermaid reference
3
3
  description: Direction and layout, node and edge budgets, accessibility fields, label punctuation, and render verification for a Mermaid diagram
4
+ paths:
5
+ - '**/*.md'
6
+ rule:
7
+ - 'Apply this rule only when the edit drafts or revises a Mermaid fence. Skip it on a markdown edit carrying no diagram.'
8
+ - 'Read it before drafting the fence. Do not work the layout or the budgets from memory.'
9
+ - 'Which diagram a document carries and what question it answers is a separate topic. The diagrams standard routes it.'
4
10
  ---
5
11
 
6
12
  # Mermaid reference
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: Plan lifecycle reference
3
+ description: When a feature plan is written, where it lives, how it changes mid-flight, and its move to the archive
4
+ ---
5
+
6
+ # Plan lifecycle reference
7
+
8
+ Applies to a feature plan at `.canon/plans/feature-<slug>.md` from the session that writes it to the archive. The folder is gitignored, so nothing but this lifecycle keeps a plan's rejected alternatives readable after the work ships.
9
+
10
+ This standard governs when a plan moves and changes rather than what the file holds, so it carries no template. The plan's own template is in `plan.md`.
11
+
12
+ ## Scope
13
+
14
+ Governs when a feature plan is written, where it lives across working trees, how it is amended, and its move to `.canon/plans/archive/`.
15
+
16
+ Does not govern:
17
+
18
+ - The filename, the required sections, and the suggested-and-answer contract: `plan.md`
19
+ - The task file a plan is linked from, and how it is archived: `tasks.md`
20
+
21
+ ## What a working lifecycle looks like
22
+
23
+ A plan's lifecycle works when a later reader can still answer, from the file alone:
24
+
25
+ - What did the work set out to do, and which alternative did it reject?
26
+ - Which answer to each question is current, without reconciling two passages?
27
+
28
+ A plan lost to deletion, or carrying two answers to one question, fails both.
29
+
30
+ ## Lifecycle
31
+
32
+ - Write the plan before implementation starts, and treat it as the scope of the run that executes it. A subject that has to be measured before anyone can plan against it takes a measurement track first.
33
+ - Keep every plan at one root. A plan copied into each parallel working tree forks, and the copies answer the same question differently.
34
+ - Amend the plan in place when a decision changes mid-flight. Do not append a second passage narrating the change, which leaves a reader to work out which of two answers is current. An execution-time deviation from a suggestion is one such amendment, and the suggested-and-answer contract in `plan.md` fixes which line takes it.
35
+ - Move the plan to `.canon/plans/archive/` when the work it describes ships or is declined. Never delete it, because the plan is where the rejected alternative is written down. A flat `.claude/plans-archive/` sibling is the older layout.
36
+ - Write the plan in the same session that opens the task it serves. The session executing it later inherits reasoning it would otherwise re-derive.
package/standards/plan.md CHANGED
@@ -1,20 +1,30 @@
1
1
  ---
2
2
  title: Plan reference
3
- description: Filename and slug, required sections, the suggested-and-answer contract, and the lifecycle from the live folder to the archive
3
+ description: Filename and slug, the required sections, and the suggested-and-answer contract for a feature plan
4
+ paths:
5
+ - '.canon/plans/**'
6
+ rule:
7
+ - 'Never fill an `- Answer:` slot on behalf of the person who owns it. A blank slot accepts the suggestion at execution time.'
8
+ - 'Never ship a question without a `- Suggested:` line. Write `- Suggested: needs your call, <why>` where the answer turns on preference.'
9
+ - "Rewrite the `- Suggested:` line as `overridden at execution to <pick>,` plus the measurement when execution deviates from an unanswered question, leaving the slot blank. Put the same deviation in one line under the open task's `## Findings`."
10
+ - 'Move a shipped plan to `.canon/plans/archive/`. Never delete one.'
11
+ - 'Amend a plan in place when a decision changes. Do not append a second passage narrating the change.'
12
+ - 'Follow the plan lifecycle standard for when a plan is written, how it is amended, and its move to the archive. Read it with `canon standards plan-lifecycle`.'
4
13
  ---
5
14
 
6
15
  # Plan reference
7
16
 
8
17
  Applies to a feature plan at `.canon/plans/feature-<slug>.md`. One file holds one concern, written before implementation starts and read by whatever executes it, so it has to carry the scope without the conversation that produced it.
9
18
 
10
- The folder is gitignored, and backed by `canon records push` wherever a records remote is configured. Nothing backs a plan deleted before a push, which is why the archive step below is a move rather than a cleanup.
19
+ The folder is gitignored, and backed by `canon records push` wherever a records remote is configured. Nothing backs a plan deleted before a push, which is why the archive step is a move rather than a cleanup.
11
20
 
12
21
  ## Scope
13
22
 
14
- Governs a feature plan under `.canon/plans/feature-<slug>.md`: the filename, the required sections, what each holds, the suggested-and-answer contract, and the lifecycle from the live folder to the archive.
23
+ Governs a feature plan under `.canon/plans/feature-<slug>.md`: the filename, the required sections, what each holds, and the suggested-and-answer contract.
15
24
 
16
25
  Does not govern:
17
26
 
27
+ - When a plan is written, how it is amended, and its move to the archive: `plan-lifecycle.md`
18
28
  - One question measured in depth before anyone can plan against it: `groundwork.md`
19
29
  - A dump of many findings filed by domain, each carrying its own verdict: `intake.md`
20
30
  - The task file a plan is linked from, and the origin line pointing back at it: `tasks.md`
@@ -135,14 +145,6 @@ The plan is archived at ship, so the same deviation takes one line in the open t
135
145
 
136
146
  This contract inverts the one an intake folder keeps, where an empty slot means unread and acceptance is typed out. A plan is written and read in one sitting with every question already surfaced, so silence is a usable default here and is not one there.
137
147
 
138
- ## Lifecycle
139
-
140
- - Write the plan before implementation starts, and treat it as the scope of the run that executes it. A subject that has to be measured before anyone can plan against it takes a measurement track first.
141
- - Keep every plan at one root. A plan copied into each parallel working tree forks, and the copies answer the same question differently.
142
- - Amend the plan in place when a decision changes mid-flight. Do not append a second passage narrating the change, which leaves a reader to work out which of two answers is current. An execution-time deviation from a suggestion is one such amendment, and the contract above fixes which line takes it.
143
- - Move the plan to `.canon/plans/archive/` when the work it describes ships or is declined. Never delete it, because the plan is where the rejected alternative is written down. A flat `.claude/plans-archive/` sibling is the older layout.
144
- - Write the plan in the same session that opens the task it serves. The session executing it later inherits reasoning it would otherwise re-derive.
145
-
146
148
  ## Template
147
149
 
148
150
  ```markdown
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  title: Readme reference
3
3
  description: Readme voice, structure, and content conventions
4
+ paths:
5
+ - '**/README.md'
4
6
  ---
5
7
 
6
8
  # Readme reference