@erclx/canon 5.24.0 → 5.26.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/README.md +2 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/backlog-triage/REQUIREMENT.md +2 -2
- package/claude/skills/backlog-triage/SKILL.md +19 -6
- package/claude/skills/canvas/REQUIREMENT.md +49 -0
- package/claude/skills/canvas/SKILL.md +106 -0
- package/claude/skills/context-fold/REQUIREMENT.md +6 -2
- package/claude/skills/context-fold/SKILL.md +8 -6
- package/claude/skills/context-fold/references/anchor-sweep.md +2 -2
- package/claude/skills/design-extract/REQUIREMENT.md +1 -1
- package/claude/skills/design-taste/REQUIREMENT.md +1 -1
- package/claude/skills/design-taste/SKILL.md +4 -2
- package/claude/skills/design-taste/references/adopted.md +40 -0
- package/claude/skills/design-taste/references/kinds.md +1 -1
- package/claude/skills/design-taste/references/systems.md +1 -7
- package/claude/skills/design-taste/references/tells.md +2 -24
- package/claude/skills/docs-sync/REQUIREMENT.md +2 -1
- package/claude/skills/document-health/REQUIREMENT.md +2 -0
- package/claude/skills/document-health/SKILL.md +21 -0
- package/claude/skills/document-health/references/axes.md +12 -0
- package/claude/skills/draft-and-pick/SKILL.md +1 -1
- package/claude/skills/draft-doc/REQUIREMENT.md +1 -1
- package/claude/skills/draft-figure/REQUIREMENT.md +14 -3
- package/claude/skills/draft-figure/SKILL.md +24 -51
- package/claude/skills/draft-figure/references/architecture-set.md +35 -0
- package/claude/skills/draft-figure/references/hand-drawn.md +31 -0
- package/claude/skills/draft-figure/references/mermaid.md +55 -0
- package/claude/skills/draft-identity/SKILL.md +1 -1
- package/claude/skills/draft-screencast/SKILL.md +9 -7
- package/claude/skills/draft-slides/REQUIREMENT.md +17 -12
- package/claude/skills/draft-slides/SKILL.md +31 -20
- package/claude/skills/git-followup/SKILL.md +17 -6
- package/claude/skills/git-issue/references/issue.md +10 -0
- package/claude/skills/git-pr/references/evidence.md +12 -2
- package/claude/skills/git-pr/references/labels.md +12 -0
- package/claude/skills/git-split/SKILL.md +19 -8
- package/claude/skills/index-lookup/SKILL.md +2 -2
- package/claude/skills/markdown-propose/references/format.md +9 -0
- package/claude/skills/memory-review/references/later-phases.md +7 -0
- package/claude/skills/review-address/SKILL.md +6 -13
- package/claude/skills/review-address/references/rest-reads.md +43 -0
- package/claude/skills/review-craft/references/ui.md +7 -1
- package/claude/skills/review-pr/REQUIREMENT.md +1 -0
- package/claude/skills/review-pr/SKILL.md +9 -3
- package/claude/skills/review-pr/references/close-out.md +2 -2
- package/claude/skills/review-pr/references/post.md +17 -5
- package/claude/skills/review-pr/references/scope.md +10 -8
- package/claude/skills/review-ui/SKILL.md +12 -4
- package/claude/skills/review-ui/references/post.md +4 -2
- package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +38 -1
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +13 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +4 -0
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +4 -4
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +6 -0
- package/claude/skills/role-orchestrator/scripts/poll.ts +90 -27
- package/claude/skills/role-orchestrator/scripts/repo.ts +3 -5
- package/claude/skills/role-orchestrator/scripts/watch.ts +4 -7
- package/claude/skills/search-craft/REQUIREMENT.md +1 -1
- package/claude/skills/search-craft/SKILL.md +3 -1
- package/claude/skills/search-craft/references/adopted.md +30 -0
- package/claude/skills/target-setup/references/detect.md +8 -0
- package/claude/skills/target-setup/references/indexes.md +13 -0
- package/claude/skills/target-setup/references/verify.md +8 -0
- package/claude/skills/test-first/SKILL.md +2 -0
- package/claude/skills/test-first/references/adopted.md +36 -0
- package/claude/skills/write-human/SKILL.md +1 -1
- package/claude/skills/write-human/references/machine-tells.md +13 -0
- package/docs/agents/canvas.md +4 -2
- package/docs/agents/capture.md +1 -1
- package/docs/agents/commands.md +47 -47
- package/docs/agents/context-audit-checks.md +2 -2
- package/docs/agents/index.md +2 -3
- package/docs/agents/key-changes.md +3 -3
- package/docs/agents/pr-evidence.md +11 -2
- package/docs/agents/pr-reads.md +5 -4
- package/docs/agents/records-stale.md +22 -9
- package/docs/agents/records.md +1 -1
- package/docs/agents/sandbox.md +1 -1
- package/docs/agents/skills-audit.md +10 -3
- package/docs/agents/slides.md +15 -3
- package/docs/target/projects.md +4 -4
- package/docs/workflow/ai-workflow.md +1 -2
- package/docs/workflow/index.md +1 -0
- package/docs/workflow/parallel-features.md +1 -1
- package/docs/workflow/skill-map-on-request.md +42 -0
- package/docs/workflow/skill-map.md +3 -36
- package/docs/workflow/visual-design-workflow.md +1 -0
- package/governance/renames.toml +0 -4
- package/governance/rules/standards/markdown.md +1 -0
- package/package.json +1 -2
- package/scripts/core/install-check.sh +1 -1
- package/scripts/lib/sandbox-dispatch.sh +1 -1
- package/scripts/lib/sandbox-git.sh +1 -9
- package/scripts/lib/sandbox-path.sh +1 -1
- package/scripts/sandbox-hook.sh +99 -0
- package/src/audits/catalog.ts +2 -2
- package/src/canvas/capture.ts +1 -9
- package/src/canvas/client/inspector/color-field.tsx +368 -0
- package/src/canvas/client/inspector/field.tsx +200 -0
- package/src/canvas/client/inspector/fields.css +292 -0
- package/src/canvas/client/inspector/section.tsx +27 -0
- package/src/canvas/client/inspector/values.ts +183 -0
- package/src/canvas/client/inspector.tsx +176 -160
- package/src/canvas/client/shell.css +0 -101
- package/src/capture/render.ts +45 -7
- package/src/capture/sources.ts +43 -0
- package/src/census/count.ts +1 -1
- package/src/claude/cases/misc.ts +5 -0
- package/src/claude/cases/workflow.ts +6 -1
- package/src/claude/seeds.ts +1 -2
- package/src/claude/skills-audit.ts +68 -14
- package/src/client-commands.ts +1 -1
- package/src/commands/claude.ts +27 -2
- package/src/commands/design.ts +2 -64
- package/src/commands/feedback.ts +3 -3
- package/src/commands/pr.ts +219 -202
- package/src/commands/records.ts +88 -6
- package/src/commands/sandbox.ts +50 -10
- package/src/commands/slides.ts +31 -77
- package/src/context/audit.ts +8 -7
- package/src/design/tokens.ts +2 -18
- package/src/docs/read.ts +1 -1
- package/src/{github.ts → feedback/github.ts} +1 -1
- package/src/gate/measures.ts +6 -4
- package/src/gov/citations.ts +1 -1
- package/src/gov/payload.ts +1 -1
- package/src/gov/superseded.ts +2 -2
- package/src/help.ts +2 -2
- package/src/markdown/ceiling.ts +1 -1
- package/src/migrate/skill-names.ts +7 -1
- package/src/pr/evidence.ts +25 -5
- package/src/pr/rest.ts +137 -0
- package/src/record-root.ts +1 -0
- package/src/records/backup.ts +1 -0
- package/src/records/push-guard.ts +1 -1
- package/src/records/stale-canonical.ts +146 -0
- package/src/records/stale.ts +1 -1
- package/src/sandbox/hooks.ts +128 -0
- package/src/sandbox/provision.ts +596 -0
- package/src/sandbox/tree.ts +103 -4
- package/src/secrets/marker.ts +2 -2
- package/src/secrets/scan.ts +1 -1
- package/src/shipped/references.ts +2 -2
- package/src/slides/convert/export.ts +2 -16
- package/src/slides/decks.ts +110 -0
- package/src/standards/read.ts +1 -1
- package/src/sync/seeds-report.ts +1 -1
- package/src/tasks/answers.ts +1 -1
- package/src/tasks/archive.ts +1 -1
- package/src/teach/nav.ts +1 -1
- package/src/tooling/inject.ts +1 -1
- package/src/web/readme-citations.ts +1 -1
- package/standards/architecture.md +1 -0
- package/standards/canvas.md +112 -0
- package/standards/context.md +2 -1
- package/standards/figures.md +0 -1
- package/standards/index.md +1 -1
- package/standards/markdown.md +1 -0
- package/standards/mermaid.md +1 -7
- package/standards/requirements.md +2 -0
- package/standards/skill-practice.md +2 -1
- package/standards/skill.md +1 -0
- package/tooling/claude/reference.md +0 -5
- package/claude/skills/draft-diagram/REQUIREMENT.md +0 -45
- package/claude/skills/draft-diagram/SKILL.md +0 -177
- package/docs/agents/design-board.md +0 -33
- package/governance/rules/standards/diagrams.md +0 -14
- package/governance/rules/standards/mermaid.md +0 -15
- package/scripts/core/check-gallery-exclusion.sh +0 -50
- package/scripts/manage-sandbox.sh +0 -472
- package/src/design/board.ts +0 -576
- package/src/slides/layouts.ts +0 -478
- package/src/slides/parse.ts +0 -84
- package/src/slides/render.ts +0 -110
- package/src/slides/styles.ts +0 -96
- package/standards/diagrams.md +0 -132
- package/tooling/claude/seeds/.claude/diagrams/index.md +0 -8
- /package/claude/skills/write-human/references/{source-material.md → adopted.md} +0 -0
- /package/src/{commands/feedback-format.ts → feedback/body.ts} +0 -0
- /package/src/{github-format.ts → feedback/github-format.ts} +0 -0
- /package/src/{frontmatter.ts → markdown/frontmatter.ts} +0 -0
- /package/src/{binary.ts → sweep/binary.ts} +0 -0
- /package/src/{exempt-marker.ts → sweep/exempt-marker.ts} +0 -0
- /package/src/{paths.ts → tasks/paths.ts} +0 -0
- /package/src/{seed-marker.ts → tooling/seed-marker.ts} +0 -0
package/src/secrets/marker.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { isMarked } from '@/exempt-marker'
|
|
1
|
+
import { isMarked } from '@/sweep/exempt-marker'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The inline token exempting one line from the secret scan.
|
|
5
5
|
*
|
|
6
|
-
* Shaped on the `stub: true` precedent in `src/seed-marker.ts`, which answers
|
|
6
|
+
* Shaped on the `stub: true` precedent in `src/tooling/seed-marker.ts`, which answers
|
|
7
7
|
* a check whose own comment records a false-positive class. The exemption
|
|
8
8
|
* travels with the line it exempts rather than sitting in a path list away
|
|
9
9
|
* from it, so a reader meeting a muted match finds the reason on the spot.
|
package/src/secrets/scan.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
-
import { isBinary } from '@/binary'
|
|
3
|
+
import { isBinary } from '@/sweep/binary'
|
|
4
4
|
import { listRepositoryFiles } from '@/git-files'
|
|
5
5
|
import { isExempt } from '@/secrets/marker'
|
|
6
6
|
import { matchLine } from '@/secrets/patterns'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
-
import { isMarked } from '@/exempt-marker'
|
|
3
|
+
import { isMarked } from '@/sweep/exempt-marker'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The corpora a target reader reaches, which is the `files` field less `src`.
|
|
@@ -113,7 +113,7 @@ const COMMIT_SHA = /(?<![0-9A-Za-z_@/#])([0-9a-f]{7,40})(?![0-9A-Za-z_])/g
|
|
|
113
113
|
* one.
|
|
114
114
|
*
|
|
115
115
|
* The literal is hardcoded rather than read from `package.json` or
|
|
116
|
-
* `git remote`, matching `src/github-format.ts` and `src/commands/repo.ts`,
|
|
116
|
+
* `git remote`, matching `src/feedback/github-format.ts` and `src/commands/repo.ts`,
|
|
117
117
|
* which already hardcode it.
|
|
118
118
|
*
|
|
119
119
|
* Same word-boundary discipline as the two patterns above: a word character
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
copyFileSync,
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readdirSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
writeFileSync,
|
|
7
|
-
} from 'node:fs'
|
|
1
|
+
import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
8
2
|
import { basename, join } from 'node:path'
|
|
9
3
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
10
4
|
import JSZip from 'jszip'
|
|
@@ -42,6 +36,7 @@ import {
|
|
|
42
36
|
type WalkedSlide,
|
|
43
37
|
walkSlide,
|
|
44
38
|
} from '@/slides/convert/walk'
|
|
39
|
+
import { slideFiles } from '@/slides/decks'
|
|
45
40
|
import { embedFonts, type FontNotice } from '@/slides/package/fonts'
|
|
46
41
|
import {
|
|
47
42
|
type EntranceSpec,
|
|
@@ -127,15 +122,6 @@ function refused(reason: ExportRefusal, message: string): ExportResult {
|
|
|
127
122
|
return { status: 'refused', reason, message }
|
|
128
123
|
}
|
|
129
124
|
|
|
130
|
-
/** Every `.html` file directly in the folder, in filename order. */
|
|
131
|
-
export function slideFiles(sourceDir: string): string[] {
|
|
132
|
-
return readdirSync(sourceDir, { withFileTypes: true })
|
|
133
|
-
.filter((entry) => entry.isFile() && entry.name.endsWith('.html'))
|
|
134
|
-
.map((entry) => entry.name)
|
|
135
|
-
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
|
|
136
|
-
.map((name) => join(sourceDir, name))
|
|
137
|
-
}
|
|
138
|
-
|
|
139
125
|
/** A picture whose bytes the post-write pass replaces or whose corners it rounds. */
|
|
140
126
|
interface Patch {
|
|
141
127
|
readonly slide: number
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from 'node:fs'
|
|
2
|
+
import { join, relative, resolve } from 'node:path'
|
|
3
|
+
import { creationRel, recordDir } from '@/record-root'
|
|
4
|
+
import { readDeck } from '@/slides/convert/deck'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A deck is a folder under the record root's `slides/`, holding its `.html`
|
|
8
|
+
* slides, an optional `deck.json`, and its assets. `layouts/` beside the decks
|
|
9
|
+
* holds the project's shared layouts and is never a deck itself.
|
|
10
|
+
*/
|
|
11
|
+
export const SLIDES_FOLDER = 'slides'
|
|
12
|
+
|
|
13
|
+
export const LAYOUTS_FOLDER = 'layouts'
|
|
14
|
+
|
|
15
|
+
export interface Deck {
|
|
16
|
+
readonly name: string
|
|
17
|
+
/** From `deck.json`, falling back to the folder name as the export does. */
|
|
18
|
+
readonly title: string
|
|
19
|
+
readonly slides: number
|
|
20
|
+
/** Relative to the project root. */
|
|
21
|
+
readonly path: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type DeckRefusal = 'no-decks' | 'ambiguous' | 'no-deck'
|
|
25
|
+
|
|
26
|
+
export type DeckResolution =
|
|
27
|
+
| { readonly status: 'resolved'; readonly dir: string }
|
|
28
|
+
| {
|
|
29
|
+
readonly status: 'refused'
|
|
30
|
+
readonly reason: DeckRefusal
|
|
31
|
+
readonly message: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Every `.html` file directly in the folder, in filename order. */
|
|
35
|
+
export function slideFiles(dir: string): string[] {
|
|
36
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
37
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.html'))
|
|
38
|
+
.map((entry) => entry.name)
|
|
39
|
+
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
|
|
40
|
+
.map((name) => join(dir, name))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function listDecks(root: string): Deck[] {
|
|
44
|
+
const dir = recordDir(root, SLIDES_FOLDER)
|
|
45
|
+
if (!isDirectory(dir)) return []
|
|
46
|
+
|
|
47
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
48
|
+
.filter((entry) => entry.isDirectory() && entry.name !== LAYOUTS_FOLDER)
|
|
49
|
+
.map((entry) => entry.name)
|
|
50
|
+
.sort((a, b) => a.localeCompare(b))
|
|
51
|
+
.flatMap((name) => {
|
|
52
|
+
const deckDir = join(dir, name)
|
|
53
|
+
const slides = slideFiles(deckDir).length
|
|
54
|
+
if (slides === 0) return []
|
|
55
|
+
const read = readDeck(deckDir)
|
|
56
|
+
const title = read.status === 'read' ? read.deck.title : name
|
|
57
|
+
return [{ name, title, slides, path: relative(root, deckDir) }]
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The folder `render` reads. A deck name wins over a folder of the same name
|
|
63
|
+
* relative to the root, and no name at all takes the only deck there is.
|
|
64
|
+
*/
|
|
65
|
+
export function resolveDeck(
|
|
66
|
+
root: string,
|
|
67
|
+
target: string | undefined,
|
|
68
|
+
): DeckResolution {
|
|
69
|
+
const decks = listDecks(root)
|
|
70
|
+
const at = creationRel(root, SLIDES_FOLDER)
|
|
71
|
+
|
|
72
|
+
if (target === undefined) {
|
|
73
|
+
const [only, ...rest] = decks
|
|
74
|
+
if (only === undefined) {
|
|
75
|
+
return refuse('no-decks', `No deck under ${at}/. Draw one there first.`)
|
|
76
|
+
}
|
|
77
|
+
if (rest.length > 0) {
|
|
78
|
+
return refuse(
|
|
79
|
+
'ambiguous',
|
|
80
|
+
`${decks.length} decks under ${at}/, name one: ${names(decks)}`,
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
return { status: 'resolved', dir: join(root, only.path) }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const named = decks.find((deck) => deck.name === target)
|
|
87
|
+
if (named) return { status: 'resolved', dir: join(root, named.path) }
|
|
88
|
+
|
|
89
|
+
const folder = resolve(root, target)
|
|
90
|
+
if (isDirectory(folder)) return { status: 'resolved', dir: folder }
|
|
91
|
+
|
|
92
|
+
return refuse(
|
|
93
|
+
'no-deck',
|
|
94
|
+
decks.length === 0
|
|
95
|
+
? `${target} is neither a deck under ${at}/ nor a folder`
|
|
96
|
+
: `${target} is neither a deck nor a folder. Decks: ${names(decks)}`,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function names(decks: readonly Deck[]): string {
|
|
101
|
+
return decks.map((deck) => deck.name).join(', ')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function refuse(reason: DeckRefusal, message: string): DeckResolution {
|
|
105
|
+
return { status: 'refused', reason, message }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isDirectory(path: string): boolean {
|
|
109
|
+
return existsSync(path) && statSync(path).isDirectory()
|
|
110
|
+
}
|
package/src/standards/read.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
2
2
|
import { basename, join } from 'node:path'
|
|
3
|
-
import { stripFrontmatter } from '@/frontmatter'
|
|
3
|
+
import { stripFrontmatter } from '@/markdown/frontmatter'
|
|
4
4
|
import { PROJECT_ROOT } from '@/project-root'
|
|
5
5
|
|
|
6
6
|
export const INDEX_FILE = 'index.md'
|
package/src/sync/seeds-report.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
2
|
import { relative } from 'node:path'
|
|
3
3
|
import { planSeeds, type Seed } from '@/claude/seeds'
|
|
4
|
-
import { rewritesOnInstall, stripSeedMarker } from '@/seed-marker'
|
|
4
|
+
import { rewritesOnInstall, stripSeedMarker } from '@/tooling/seed-marker'
|
|
5
5
|
import { findInstalledOrigin, readHistoryIndex } from '@/sync/history'
|
|
6
6
|
|
|
7
7
|
/**
|
package/src/tasks/answers.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
import { isAbsolute, relative, resolve } from 'node:path'
|
|
4
|
-
import { isUnder } from '@/paths'
|
|
4
|
+
import { isUnder } from '@/tasks/paths'
|
|
5
5
|
import { recordDir, recordDirs } from '@/record-root'
|
|
6
6
|
import {
|
|
7
7
|
hasStagedBatches,
|
package/src/tasks/archive.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
|
|
|
2
2
|
import { mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises'
|
|
3
3
|
import { basename, dirname, join, relative, resolve, sep } from 'node:path'
|
|
4
4
|
import { regenOne } from '@/indexes/regen'
|
|
5
|
-
import { isUnder } from '@/paths'
|
|
5
|
+
import { isUnder } from '@/tasks/paths'
|
|
6
6
|
import { recordDir, recordDirs } from '@/record-root'
|
|
7
7
|
|
|
8
8
|
const TASKS = 'tasks'
|
package/src/teach/nav.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from '@/design/components'
|
|
8
8
|
import { buildDesignCss } from '@/design/css'
|
|
9
9
|
import { FAVICON_COLORS, faviconLink, renderFavicon } from '@/design/favicon'
|
|
10
|
-
import { stripFrontmatter } from '@/frontmatter'
|
|
10
|
+
import { stripFrontmatter } from '@/markdown/frontmatter'
|
|
11
11
|
import { parseFrontmatter, readField } from '@/indexes/frontmatter'
|
|
12
12
|
import { PROJECT_ROOT } from '@/project-root'
|
|
13
13
|
import { compileScript } from '@/teach/browser/compile'
|
package/src/tooling/inject.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
|
3
3
|
import { dirname, join, relative } from 'node:path'
|
|
4
4
|
import { $ } from 'bun'
|
|
5
5
|
import { copyPreservingMode } from '@/copy'
|
|
6
|
-
import { rewritesOnInstall, stripSeedMarker } from '@/seed-marker'
|
|
6
|
+
import { rewritesOnInstall, stripSeedMarker } from '@/tooling/seed-marker'
|
|
7
7
|
import { resolveSurfacePath } from '@/surface-root'
|
|
8
8
|
import { mergeSections, pruneSections } from '@/tooling/gitignore'
|
|
9
9
|
import {
|
|
@@ -54,6 +54,7 @@ Use `## Overview`, `## Key technical decisions` with one named H3 per decision,
|
|
|
54
54
|
|
|
55
55
|
- Rewrite a decision a later one changed rather than appending the change beside it. A reader should find the design that stands in one place, with the alternative that lost stated once.
|
|
56
56
|
- Hold only what is open under `## Risks / open questions`. An entry leaves the section in the change that settles it, becoming a decision here when it fills a slot and moving to the domain context entry it constrains when it does not.
|
|
57
|
+
- Carry an optional `reviewed: YYYY-MM-DD` frontmatter field naming the day someone last read the record whole for drift. Whoever finishes that review sets it as the review's last edit, and `canon records stale canonical` reads it to count the releases shipped since. A record with no field reads as never reviewed.
|
|
57
58
|
|
|
58
59
|
## Verification anchors
|
|
59
60
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Canvas reference
|
|
3
|
+
description: Page folders, frame files, the layout.json shape, token resolution, and what a frame must not assume on a local design canvas
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Canvas reference
|
|
7
|
+
|
|
8
|
+
Applies to the canvas content under `.canon/canvas/`, where each page is a folder of HTML frames and a layout file placing them. A frame is a draft drawn to be looked at and picked from, so it changes whenever someone tries a direction, and the operator clears it once the direction lands where the project keeps its design.
|
|
9
|
+
|
|
10
|
+
The folder is gitignored, so nothing in a repository checks its shape and nothing backs it up by default.
|
|
11
|
+
|
|
12
|
+
## Scope
|
|
13
|
+
|
|
14
|
+
Governs the canvas content under `.canon/canvas/`: page folder names, frame files, the `layout.json` shape, how tokens reach a frame, and what a frame must not assume about where it renders.
|
|
15
|
+
|
|
16
|
+
Does not govern:
|
|
17
|
+
|
|
18
|
+
- The design decisions a frame is drafting, such as type, color, and layout taste: the project's own design document
|
|
19
|
+
- The project's design document and the token module it renders from, which a picked direction is carried into: `design.md`
|
|
20
|
+
- The procedure that starts the canvas, reads the operator's selection, and carries a pick back, which belongs to the surface driving the canvas
|
|
21
|
+
- Voice, rhythm, and sentence construction in any text a frame shows: the `write-human` skill
|
|
22
|
+
|
|
23
|
+
## What a working canvas looks like
|
|
24
|
+
|
|
25
|
+
A canvas works when the operator and a session can point at the same thing and see the same thing:
|
|
26
|
+
|
|
27
|
+
- Which pages exist, and which frames sit on each?
|
|
28
|
+
- Where does each frame sit on its page, and at what width does it render?
|
|
29
|
+
- Does each frame show the project's own tokens rather than values copied into it?
|
|
30
|
+
- Does a frame render the same in the live canvas as in a capture?
|
|
31
|
+
|
|
32
|
+
A canvas failing these is non-conforming even when it satisfies every shape rule below.
|
|
33
|
+
|
|
34
|
+
## Pages
|
|
35
|
+
|
|
36
|
+
- Make one folder per page directly under `.canon/canvas/`. A page groups the frames compared side by side, such as the candidates for one decision.
|
|
37
|
+
- Name a page as one path segment of letters, digits, dots, dashes, and underscores, starting with a letter or a digit. A name opening with a dot is hidden from every listing.
|
|
38
|
+
- Never write a folder whose name opens with a dot. Those names are reserved for files the canvas generates and removes itself.
|
|
39
|
+
- Rename a page rather than copying it under a new name, so its layout and frames stay together.
|
|
40
|
+
|
|
41
|
+
## Frames
|
|
42
|
+
|
|
43
|
+
- Write one frame as one `.html` file inside its page folder. The filename without its extension is the frame's name, and the name follows the page naming rule.
|
|
44
|
+
- Write each frame as a whole document, with a doctype, a `head`, and a `body`. A fragment gets elements the browser builds around it, and an address the operator picks inside it can no longer be matched to the file.
|
|
45
|
+
- Write every table with its `tbody` stated, for the same reason. A browser adds one the file leaves out.
|
|
46
|
+
- Keep a frame self-contained. Inline its own styles and scripts, or link a file inside the same page folder, since a frame is served from its page and nothing outside the canvas resolves.
|
|
47
|
+
- Use the project's custom properties, `var(--<name>)`, for every value the token stylesheet defines. A literal copied from it shows the right color today and the wrong one after the next token change.
|
|
48
|
+
- Edit a frame by rewriting its file. The file is the frame, and nothing else holds a copy of its markup.
|
|
49
|
+
|
|
50
|
+
## Layout
|
|
51
|
+
|
|
52
|
+
- Keep one `layout.json` per page, beside its frames. It holds each frame's box and nothing else, so moving a frame never rewrites its markup.
|
|
53
|
+
- Key each box by the frame's name, under one top-level `frames` object.
|
|
54
|
+
- Give every box four finite numbers in pixels: `x` and `y` for its top-left corner on the page, `width` for the width the frame renders at, and `height` for the height it shows on the canvas.
|
|
55
|
+
- Leave a frame out of the layout only when its position does not matter yet. A reader places an unnamed frame in a row after the others, at the default size of 1440 by 900.
|
|
56
|
+
- Never write the file while a writer holds its lock, which is the same name with `.lock` appended. Two writers each replacing the whole file lose one of the two edits.
|
|
57
|
+
- Treat a layout that does not parse as a defect to repair, never as an empty one to overwrite. Writing over it erases every box it held.
|
|
58
|
+
|
|
59
|
+
The keys below are the ones a reader parses. The frame names are placeholders.
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"frames": {
|
|
64
|
+
"hero-a": { "x": 0, "y": 0, "width": 1440, "height": 900 },
|
|
65
|
+
"hero-b": { "x": 1520, "y": 0, "width": 1440, "height": 900 }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Tokens
|
|
71
|
+
|
|
72
|
+
- Expect the token stylesheet first in every frame's `head` when it is served, ahead of anything the frame links. A stylesheet the frame links itself still wins the cascade.
|
|
73
|
+
- Read where the tokens come from in this order: the canvas tool's own module inside its own checkout, else the project's installed base stylesheet plus anything in its project override folder, else none. A canvas with no token source renders unstyled and says so.
|
|
74
|
+
- Never paste the token stylesheet into a frame. The injected copy follows the project, and a pasted one stops following it the moment it is written.
|
|
75
|
+
|
|
76
|
+
## What a frame must not assume
|
|
77
|
+
|
|
78
|
+
- Do not assume a viewport. A frame renders at its layout width on the canvas and in a capture alike, so size it with media queries against that width rather than against the operator's window.
|
|
79
|
+
- Do not assume the theme. The canvas theme toggle is the operator's own state, and a capture shows each frame in its default theme.
|
|
80
|
+
- Do not assume a network. Name only fonts the machine has, or ship the font beside the frame, since a capture refuses a frame naming a font it cannot find.
|
|
81
|
+
- Do not assume a default font. Set `font-family` with an installed family first on an element the frame's text inherits from, since a capture checks the first family of the text it renders and the browser's default serif is absent from many machines.
|
|
82
|
+
- Do not assume the frame is the source. A picked direction is carried into the project's design document or its wireframes, and the operator clears the frame once it has been carried.
|
|
83
|
+
- Do not assume the canvas survives the checkout. The folder is gitignored, so a lost checkout loses every page in it.
|
|
84
|
+
|
|
85
|
+
## Template
|
|
86
|
+
|
|
87
|
+
A new frame starts from this skeleton. The names in it are placeholders.
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<!doctype html>
|
|
91
|
+
<html lang="en">
|
|
92
|
+
<head>
|
|
93
|
+
<meta charset="utf-8" />
|
|
94
|
+
<title>frame-name</title>
|
|
95
|
+
<style>
|
|
96
|
+
html {
|
|
97
|
+
font-family: installed-family, sans-serif;
|
|
98
|
+
}
|
|
99
|
+
main {
|
|
100
|
+
padding: var(--space-lg);
|
|
101
|
+
color: var(--color-text);
|
|
102
|
+
background: var(--color-background);
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
105
|
+
</head>
|
|
106
|
+
<body>
|
|
107
|
+
<main>
|
|
108
|
+
<h1>frame-name</h1>
|
|
109
|
+
</main>
|
|
110
|
+
</body>
|
|
111
|
+
</html>
|
|
112
|
+
```
|
package/standards/context.md
CHANGED
|
@@ -21,7 +21,8 @@ Does not govern:
|
|
|
21
21
|
- Decisions filling a stack, delivery, boundary, layout, or build-principle slot, and system-wide risks: `architecture.md`
|
|
22
22
|
- Product scope, goals, and non-goals: `requirements.md`
|
|
23
23
|
- Path-scoped coding rules: `rule.md`
|
|
24
|
-
-
|
|
24
|
+
- Wireframes, which answer layout questions an entry hands off: `wireframes.md`
|
|
25
|
+
- Figures and architecture views, which answer structure questions an entry hands off: the `draft-figure` skill
|
|
25
26
|
- The consumer-facing reference under `docs/`, whose reader is operating the project without the source open: `docs.md`
|
|
26
27
|
|
|
27
28
|
## Organizing principle
|
package/standards/figures.md
CHANGED
|
@@ -14,7 +14,6 @@ Governs a hand-drawn figure wherever one is authored, naming no path because an
|
|
|
14
14
|
Does not govern:
|
|
15
15
|
|
|
16
16
|
- Direction, layout, node and edge budgets, labels, and render verification for the Mermaid path: `mermaid.md`
|
|
17
|
-
- The toolkit's own architecture-diagram surface at `.canon/diagrams/`, which shares no code, font, or visual language with this convention: `diagrams.md`
|
|
18
17
|
- Language, word choice, punctuation, and formatting in the caption or surrounding prose: `markdown.md`
|
|
19
18
|
- Voice, rhythm, and sentence construction in that prose: the `write-human` skill
|
|
20
19
|
- Which document types may carry a figure, and what else they require: that document type's own standard
|
package/standards/index.md
CHANGED
|
@@ -10,10 +10,10 @@ Reference docs for consistent authoring across the toolkit and target projects.
|
|
|
10
10
|
- [Architecture reference](architecture.md): Shape and content rules for canon/ARCHITECTURE.md
|
|
11
11
|
- [Board reference](board.md): Readiness groups, row cells, and ordering for priority.md, the unordered backlog.md beside it, and the generated index under .canon/tasks/
|
|
12
12
|
- [Branch reference](branch.md): Branch naming format and type conventions
|
|
13
|
+
- [Canvas reference](canvas.md): Page folders, frame files, the layout.json shape, token resolution, and what a frame must not assume on a local design canvas
|
|
13
14
|
- [Commit reference](commit.md): Commit message format and type conventions
|
|
14
15
|
- [Context entry reference](context.md): Shape and content rules for canon/context/<domain>.md entries
|
|
15
16
|
- [Design reference](design.md): Shape and content rules for canon/DESIGN.md
|
|
16
|
-
- [Diagram reference](diagrams.md): Shape and content rules for .canon/diagrams/<kind>.md files
|
|
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
|
package/standards/markdown.md
CHANGED
|
@@ -7,6 +7,7 @@ rule:
|
|
|
7
7
|
- 'Read it before a substantial markdown edit. Do not work the banned characters from memory.'
|
|
8
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
9
|
- 'Voice, rhythm, and sentence construction are a separate topic. `800-prose` routes them.'
|
|
10
|
+
- 'Load the `canon:draft-figure` skill before drafting or revising a Mermaid fence or a figure, and report it rather than proceeding silently when it does not resolve.'
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
# Markdown reference
|
package/standards/mermaid.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
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.'
|
|
10
4
|
---
|
|
11
5
|
|
|
12
6
|
# Mermaid reference
|
|
@@ -19,7 +13,7 @@ Governs the Mermaid attribute wherever a fence is written, naming no path becaus
|
|
|
19
13
|
|
|
20
14
|
Does not govern:
|
|
21
15
|
|
|
22
|
-
- Which
|
|
16
|
+
- Which architecture view a figure draws and what question it answers: the `draft-figure` skill's architecture set
|
|
23
17
|
- Language, word choice, punctuation, and formatting in the prose around a fence: `markdown.md`
|
|
24
18
|
- Voice, rhythm, and sentence construction in that prose: the `write-human` skill
|
|
25
19
|
- The mechanism behind any component a diagram draws: `context.md`
|
|
@@ -46,6 +46,8 @@ This file loads into every session, so its weight is paid before any work starts
|
|
|
46
46
|
|
|
47
47
|
Once every entry in the MVP list, or in a later scope section, has shipped, delete the section. Git keeps the old text, and the document states the project as it stands. Do not annotate an entry as shipped and leave it in place. While any entry is unshipped, do not renumber the list or append to it.
|
|
48
48
|
|
|
49
|
+
The record may carry an optional `reviewed: YYYY-MM-DD` frontmatter field naming the day someone last read it whole for identity drift. Whoever finishes that review sets it as the review's last edit, and `canon records stale canonical` reads it to count the releases shipped since. A record with no field reads as never reviewed.
|
|
50
|
+
|
|
49
51
|
Later scope arrives as a new section rather than as an extension of the MVP list. Name the section for what it delivers and state its entries as outcomes, the same way the goals are stated. Nothing sequences either list into versions. Work reaches the board as discrete tasks under `tasks.md`, and `board.md` orders them by readiness, so a section here states what is wanted and never when it lands.
|
|
50
52
|
|
|
51
53
|
## Distribution
|
|
@@ -47,5 +47,6 @@ A practice skill failing any of these is non-conforming even when every heading
|
|
|
47
47
|
## Which skills are practice skills
|
|
48
48
|
|
|
49
49
|
- `canon claude skills audit` keeps the list of practice skills and checks each listed one for the three closing sections and the ledger.
|
|
50
|
+
- It keeps a wider ledger list beside it, holding every practice skill plus the skills that teach a craft drawn from outside sources without a practice's closing shape. A skill on the wider list alone owes the ledger and none of the three sections.
|
|
50
51
|
- Never mark a skill as a practice skill in its own frontmatter, since a self-declared kind lets a skill exempt itself.
|
|
51
|
-
-
|
|
52
|
+
- Both lists name this toolkit's shipped skills alone, so a project's own practice skill takes this shape with no check behind it.
|
package/standards/skill.md
CHANGED
|
@@ -130,6 +130,7 @@ allowed-tools: <tools required>
|
|
|
130
130
|
- Below the ceiling, name the branch that skips a block before moving it. A block every run dereferences costs a read and saves nothing.
|
|
131
131
|
- Past the ceiling, move a whole step to a reference, preferring a step a named branch skips. Keep the step heading in the body with one line naming the file to read on reaching it, so the body still shows every step in order.
|
|
132
132
|
- Name every file under `references/` in the body at the point a session reads it, since an unnamed reference is never loaded.
|
|
133
|
+
- Open a file under `references/` running over 100 lines with a `## Contents` H2 of anchor links after its H1 and intro, so a session that reads only the top of the file still sees everything it holds.
|
|
133
134
|
- Keep the trigger, the skip condition, and the guard in the body. A run that never reaches the block has to decide that without opening the reference.
|
|
134
135
|
- Never point one skill at a sibling skill's folder for a reference both read. Each skill carries its own copy under `references/`, generated rather than hand-copied by the rule in `## Path resolution`.
|
|
135
136
|
|
|
@@ -23,7 +23,6 @@ canon/
|
|
|
23
23
|
|
|
24
24
|
.canon/
|
|
25
25
|
├── tasks/ ← seeded. One file per task plus a generated index.md, local scratch only. `archive/` holds the tasks that shipped.
|
|
26
|
-
├── diagrams/ ← seeded. Per-kind Mermaid views. `index.md` is the discovery anchor. `<kind>.md` files hold one diagram each, grouped by the `category` frontmatter field.
|
|
27
26
|
├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries swept by context-fold into `archive/`.
|
|
28
27
|
├── review/ ← one subfolder per producer. `branch/` for review-branch, `feedback/` for canon feedback, `memory/` for memory-review with its own `archive/`, `design/` for canon design render.
|
|
29
28
|
├── tmp/ ← ephemeral scratch space, safe to delete without loss
|
|
@@ -60,10 +59,6 @@ Convert by hand, once per project:
|
|
|
60
59
|
2. Replace `MEMORY.md` with an `index.md` carrying `title` and `subtitle` frontmatter and nothing else.
|
|
61
60
|
3. Run `canon indexes regen --no-stage --root . .canon/memory/index.md` to build the catalog, and compare its entry count against the file count before deleting anything.
|
|
62
61
|
|
|
63
|
-
## Upgrading from a single-file diagram set
|
|
64
|
-
|
|
65
|
-
A project installed before the diagram surface became a folder still holds `.claude/DIAGRAMS.md`. Unlike the board, this one migrates itself. The `draft-diagram` skill reads the flat file when `.canon/diagrams/` holds no entries, splits it by kind into the folder, and reports what it wrote. The old file stays on disk so the split can be compared against its source, and deleting it is a manual step once that check passes.
|
|
66
|
-
|
|
67
62
|
## Gitignore
|
|
68
63
|
|
|
69
64
|
- `# Claude`: `.canon/`, `.claude/worktrees/`, `.claude/.tmp/` <!-- canon-keep-record-root -->
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: draft-diagram
|
|
3
|
-
description: Why diagrams are a folder of per-kind entries rather than one file, and why the verified marker is spent only on a render that was read back
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Draft diagram requirement
|
|
7
|
-
|
|
8
|
-
## Gap
|
|
9
|
-
|
|
10
|
-
Without this skill, a session asked to refresh one view rewrites the whole surface, so four diagrams nobody checked change in the same commit and the reviewer cannot tell which one the pass was about. It picks a filename of its own, and the next refresh writes a duplicate beside the old entry instead of finding it. It reaches for C4, state, or class diagrams that render differently in every viewer.
|
|
11
|
-
|
|
12
|
-
The verification failure is the expensive one. A mermaid source can satisfy every rule in the standard and still render as a picture that asserts something false about the system, so a session that judges the source and stamps the entry as verified has spent the only signal a reader has on a diagram nobody looked at. The standards hook toggles off inside a fenced block, so a banned character in a node label passes silently while the same character one line below is caught.
|
|
13
|
-
|
|
14
|
-
## Must
|
|
15
|
-
|
|
16
|
-
- Write one entry per kind under the filename and category the standard fixes, so a later refresh finds its target
|
|
17
|
-
- Pick which entries this pass writes from whether the source signal exists and whether the pass has reason to touch it
|
|
18
|
-
- Stay inside `flowchart` and `sequenceDiagram`
|
|
19
|
-
- Render every entry this pass wrote and read the image back against what the entry means to say
|
|
20
|
-
- Stamp the verified marker only on an entry whose render was read back and judged correct
|
|
21
|
-
- Name every skipped render and every surviving defect in the output
|
|
22
|
-
- Regenerate the catalog from sibling frontmatter after the last entry is written
|
|
23
|
-
- Read the node labels by hand, since no hook checks inside a fence
|
|
24
|
-
|
|
25
|
-
## Must not
|
|
26
|
-
|
|
27
|
-
- Rewrite an entry this pass had no reason to touch, or report one as written when it was left alone
|
|
28
|
-
- Stamp an untouched entry, a skipped render, or an entry whose defect survived
|
|
29
|
-
- Report a clean verification when a check did not run
|
|
30
|
-
- Commit a render, which is a verification artifact rather than a deliverable
|
|
31
|
-
- Redraw a diagram during a migration pass, since a rewrite and a move landing together leaves no way to tell which one broke it
|
|
32
|
-
- Loop on a defect past two correction passes
|
|
33
|
-
|
|
34
|
-
## Guards
|
|
35
|
-
|
|
36
|
-
- No architecture file, no requirements file, and no folder structure to scan: stop, because nothing anchors a diagram
|
|
37
|
-
- Render fails for any reason: continue to the output and name the skipped check. A missing renderer degrades the loop rather than failing it.
|
|
38
|
-
- `canon` not on PATH: report the catalog as stale rather than hand-editing it
|
|
39
|
-
|
|
40
|
-
## Out of scope
|
|
41
|
-
|
|
42
|
-
- Design tokens and the visual system, which `design-extract` owns
|
|
43
|
-
- UI roughness, which `ux-audit` owns
|
|
44
|
-
- Writing the catalog file, which the index regen owns from frontmatter
|
|
45
|
-
- Deleting a pre-split flat diagrams file after a migration, which stays the user's call
|