@erclx/canon 4.89.0 → 4.92.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/design-taste/REQUIREMENT.md +78 -0
- package/claude/skills/design-taste/SKILL.md +148 -0
- package/claude/skills/design-taste/references/craft.md +58 -0
- package/claude/skills/design-taste/references/kinds.md +52 -0
- package/claude/skills/design-taste/references/preflight.md +77 -0
- package/claude/skills/design-taste/references/systems.md +49 -0
- package/claude/skills/design-taste/references/tells.md +101 -0
- package/claude/skills/design-taste/references/vocabulary.md +62 -0
- package/claude/skills/docs-fold/REQUIREMENT.md +2 -0
- package/claude/skills/docs-fold/SKILL.md +6 -0
- package/claude/skills/draft-and-pick/REQUIREMENT.md +8 -2
- package/claude/skills/draft-and-pick/SKILL.md +13 -5
- package/claude/skills/draft-ready/REQUIREMENT.md +53 -0
- package/claude/skills/draft-ready/SKILL.md +118 -0
- package/claude/skills/draft-ready/references/assembly.md +67 -0
- package/claude/skills/session-compact/REQUIREMENT.md +44 -0
- package/claude/skills/session-compact/SKILL.md +60 -0
- package/claude/skills/session-compact/references/handoff-note.md +68 -0
- package/claude/skills/session-map/REQUIREMENT.md +3 -2
- package/claude/skills/session-map/SKILL.md +2 -2
- package/claude/skills/session-resume/SKILL.md +3 -3
- package/docs/agents/audits.md +1 -1
- package/docs/agents/commands.md +1 -1
- package/docs/agents/context-audit-checks.md +4 -2
- package/docs/agents/context-audit.md +3 -1
- package/docs/agents/index.md +1 -1
- package/docs/agents/review-classification.md +1 -1
- package/docs/agents/routing.md +1 -1
- package/docs/agents/state-scoped-risk.md +1 -1
- package/docs/agents/teach.md +4 -2
- package/docs/workflow/ai-workflow.md +3 -1
- package/governance/rules/claude/540-architecture.md +1 -0
- package/governance/rules/claude/563-ready.md +4 -0
- package/governance/rules/ui/410-a11y.md +9 -0
- package/governance/rules/ui/420-forms.md +9 -0
- package/governance/rules/ui/460-design-taste.md +29 -0
- package/governance/rules/ui/470-motion.md +23 -0
- package/governance/stacks/astro.toml +1 -1
- package/governance/stacks/react.toml +1 -1
- package/package.json +1 -1
- package/src/audits/catalog.ts +6 -0
- package/src/autoship/paths.ts +1 -1
- package/src/claude/cases/authoring.ts +5 -0
- package/src/claude/cases/misc.ts +10 -0
- package/src/commands/capture.ts +1 -0
- package/src/commands/context.ts +22 -4
- package/src/commands/design.ts +5 -0
- package/src/commands/feedback.ts +1 -0
- package/src/commands/slides.ts +3 -0
- package/src/commands/transcripts.ts +1 -0
- package/src/context/architecture.ts +51 -1
- package/src/context/gate.ts +10 -4
- package/src/design/base.css +25 -19
- package/src/design/css.ts +4 -0
- package/src/design/fonts.ts +17 -0
- package/src/design/tokens.ts +46 -31
- package/src/gate/measures.ts +40 -0
- package/src/gate/stages.ts +9 -1
- package/src/teach/workspace.ts +8 -2
- package/standards/architecture.md +36 -11
- package/standards/context.md +2 -2
- package/standards/ready.md +2 -0
- package/standards/session.md +1 -0
- package/tooling/claude/seeds/canon/ARCHITECTURE.md +12 -2
package/src/design/tokens.ts
CHANGED
|
@@ -39,6 +39,12 @@ export interface TypeToken {
|
|
|
39
39
|
readonly verify?: readonly ('family' | 'weight' | 'size' | 'lineHeight')[]
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/** One size the system offers, named apart from any role that takes it. */
|
|
43
|
+
export interface TypeStep {
|
|
44
|
+
readonly step: string
|
|
45
|
+
readonly size: string
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
export interface SpaceToken {
|
|
43
49
|
readonly step: string
|
|
44
50
|
readonly multiplier: string
|
|
@@ -57,6 +63,7 @@ export interface DesignTokens {
|
|
|
57
63
|
readonly personality: string
|
|
58
64
|
readonly color: readonly ColorToken[]
|
|
59
65
|
readonly colorNote: string
|
|
66
|
+
readonly typeScale: readonly TypeStep[]
|
|
60
67
|
readonly typography: readonly TypeToken[]
|
|
61
68
|
readonly typographyNote: string
|
|
62
69
|
readonly spacing: readonly SpaceToken[]
|
|
@@ -68,20 +75,19 @@ export interface DesignTokens {
|
|
|
68
75
|
readonly preamble: string
|
|
69
76
|
}
|
|
70
77
|
|
|
71
|
-
/** The monospace stack
|
|
78
|
+
/** The monospace stack, taken by code and by the terminal frames a page embeds. */
|
|
72
79
|
const MONO = 'Noto Sans Mono, DejaVu Sans Mono, monospace'
|
|
73
80
|
|
|
74
81
|
/**
|
|
75
|
-
* The proportional stack
|
|
76
|
-
*
|
|
77
|
-
* picked for taste, so the pairing is one superfamily rather than two systems.
|
|
82
|
+
* The proportional stack every role but `code` declares. Geist is the face the
|
|
83
|
+
* visual-direction track set throughout, embedded through `FONT_FACES`.
|
|
78
84
|
*
|
|
79
85
|
* The stack stops at two names and a generic because `src/design/base.css` is
|
|
80
86
|
* written by `canon design regen` and formatted by prettier, and a declaration
|
|
81
87
|
* past 80 columns is wrapped by the second and flattened by the first. Every
|
|
82
88
|
* emitted line stays inside the width so the two writers never disagree.
|
|
83
89
|
*/
|
|
84
|
-
const SANS = '
|
|
90
|
+
const SANS = 'Geist Variable, DejaVu Sans, sans-serif'
|
|
85
91
|
|
|
86
92
|
const DARK_GROUNDS = ['background', 'surface'] as const
|
|
87
93
|
const LIGHT_GROUNDS = ['light-background', 'light-surface'] as const
|
|
@@ -94,13 +100,13 @@ export const TOKENS: DesignTokens = {
|
|
|
94
100
|
'',
|
|
95
101
|
'The two rendered captures read it as well. `scripts/core/regen-hero.sh` fills `assets/captures/hero.html.tmpl` and `assets/captures/install.html.tmpl` with what `canon design css --no-components` emits, so both frames now carry the custom properties rather than their own copies of the hex, and a value moved here moves what the next capture renders.',
|
|
96
102
|
'',
|
|
97
|
-
'The terminal framing is the one surface left holding its own values, and that is a decision rather than a gap. `scripts/lib/ui.sh` and `src/ui.ts` each spell six escape constants, and `canon/
|
|
103
|
+
'The terminal framing is the one surface left holding its own values, and that is a decision rather than a gap. `scripts/lib/ui.sh` and `src/ui.ts` each spell six escape constants, and `canon/context/scripts/framing.md` records one color source per language with a check behind each, so generating a third spelling from here would break the rule those two checks enforce. What the record is still incomplete about is the other half of those six: `WHITE` and `GREY` name no role below, so the terminal palette is described here in part rather than in whole.',
|
|
98
104
|
].join('\n'),
|
|
99
105
|
|
|
100
106
|
personality: [
|
|
101
|
-
'Warm neutrals carry the frame under a single rust accent,
|
|
107
|
+
'Warm neutrals carry the frame under a single rust accent, set in Geist. The subject is a toolkit for people who read documents and diffs as much as they run commands, so the voice is a proportional one and the page reads as prose. One accent carries every count, link, and primary action. Promoting a second and third into structural roles is what reads as a generated interface, so the palette stays at one.',
|
|
102
108
|
'',
|
|
103
|
-
'
|
|
109
|
+
'Monospace is a role the page uses rather than its voice. The `code` role takes it, and so does any terminal frame a surface embeds, since those show what the shell printed and matching the shell is what makes them legible. Every other role is Geist.',
|
|
104
110
|
].join('\n'),
|
|
105
111
|
|
|
106
112
|
colorNote: [
|
|
@@ -236,52 +242,62 @@ export const TOKENS: DesignTokens = {
|
|
|
236
242
|
],
|
|
237
243
|
|
|
238
244
|
typographyNote: [
|
|
239
|
-
'
|
|
245
|
+
'Seven sizes are on offer, named `t0` through `t6` and emitted as custom properties beside the roles. They are 3.175, 2.375, 1.375, 1.125, 0.9375, 0.8125 and 0.6875 rem, which paint at 50.8, 38, 22, 18, 15, 13 and 11 pixels, and each role takes one. A step answers what sizes exist and a role answers what a stylesheet asks for, so the two stay separate: a role whose step did not move renders as it did, and a role that moved is the only thing that changes what a surface paints. Two steps, `t3` at 18 pixels and `t6` at 11, are offered with no role pointing at them yet.',
|
|
240
246
|
'',
|
|
241
|
-
'
|
|
247
|
+
'Every role but `code` is Geist and `code` is monospace. The hero headline takes the largest step at 50.8 pixels, which no other role reaches, since a headline set at the display cap reads as an opening rather than as a section heading.',
|
|
242
248
|
'',
|
|
243
249
|
'A tagged cell is one no rendering surface exercises yet, which is a declaration the system has not tested rather than one it has.',
|
|
244
250
|
'',
|
|
245
251
|
'Two rules set tracking and no others touch it. The label role carries `0.05em`, and the display role tightens to `-0.01em`.',
|
|
246
252
|
].join('\n'),
|
|
247
253
|
|
|
254
|
+
typeScale: [
|
|
255
|
+
{ step: 't0', size: '3.175rem' },
|
|
256
|
+
{ step: 't1', size: '2.375rem' },
|
|
257
|
+
{ step: 't2', size: '1.375rem' },
|
|
258
|
+
{ step: 't3', size: '1.125rem' },
|
|
259
|
+
{ step: 't4', size: '0.9375rem' },
|
|
260
|
+
{ step: 't5', size: '0.8125rem' },
|
|
261
|
+
{ step: 't6', size: '0.6875rem' },
|
|
262
|
+
],
|
|
263
|
+
|
|
248
264
|
typography: [
|
|
249
265
|
{
|
|
250
266
|
role: 'display',
|
|
251
|
-
family:
|
|
267
|
+
family: SANS,
|
|
252
268
|
weight: '700',
|
|
253
|
-
size: '
|
|
269
|
+
size: '2.375rem',
|
|
254
270
|
lineHeight: '1.3',
|
|
255
271
|
},
|
|
256
272
|
{
|
|
257
273
|
role: 'page-display',
|
|
258
274
|
family: SANS,
|
|
259
275
|
weight: '700',
|
|
260
|
-
size: '
|
|
276
|
+
size: '3.175rem',
|
|
261
277
|
lineHeight: '1.1',
|
|
262
|
-
verify: ['
|
|
278
|
+
verify: ['size', 'lineHeight'],
|
|
263
279
|
},
|
|
264
280
|
{
|
|
265
281
|
role: 'heading',
|
|
266
|
-
family:
|
|
282
|
+
family: SANS,
|
|
267
283
|
weight: '700',
|
|
268
|
-
size: '
|
|
284
|
+
size: '1.375rem',
|
|
269
285
|
lineHeight: '1.3',
|
|
270
286
|
verify: ['lineHeight'],
|
|
271
287
|
},
|
|
272
288
|
{
|
|
273
289
|
role: 'body',
|
|
274
|
-
family:
|
|
290
|
+
family: SANS,
|
|
275
291
|
weight: '400',
|
|
276
|
-
size: '
|
|
292
|
+
size: '0.9375rem',
|
|
277
293
|
lineHeight: '1.65',
|
|
278
294
|
verify: ['weight'],
|
|
279
295
|
},
|
|
280
296
|
{
|
|
281
297
|
role: 'label',
|
|
282
|
-
family:
|
|
298
|
+
family: SANS,
|
|
283
299
|
weight: '400',
|
|
284
|
-
size: '
|
|
300
|
+
size: '0.8125rem',
|
|
285
301
|
lineHeight: '1.45',
|
|
286
302
|
verify: ['weight', 'lineHeight'],
|
|
287
303
|
},
|
|
@@ -289,27 +305,26 @@ export const TOKENS: DesignTokens = {
|
|
|
289
305
|
role: 'code',
|
|
290
306
|
family: MONO,
|
|
291
307
|
weight: '700',
|
|
292
|
-
size: '
|
|
308
|
+
size: '0.8125rem',
|
|
293
309
|
lineHeight: '1.3',
|
|
294
310
|
verify: ['lineHeight'],
|
|
295
311
|
},
|
|
296
312
|
],
|
|
297
313
|
|
|
298
314
|
spacingNote: [
|
|
299
|
-
'
|
|
315
|
+
'Seven steps run from a quarter rem to six, at 4, 8, 14, 24, 40, 64 and 96 pixels on a 16 pixel root. The scale is not a grid of one base, since the 14 between the 8 and the 24 is the step a control needs and no multiple of four supplies it, so the multiplier column counts quarter rems rather than claiming a unit.',
|
|
300
316
|
'',
|
|
301
|
-
'The outer window padding
|
|
317
|
+
'The outer window padding of the capture frames, which the previous record carried as a three-value frame register, is not part of the scale. Those frames set their own padding in the template and read no step, so the register is retired here rather than mapped onto steps no frame uses.',
|
|
302
318
|
].join('\n'),
|
|
303
319
|
|
|
304
320
|
spacing: [
|
|
305
|
-
{ step: 'xs', multiplier: '1', value: '
|
|
306
|
-
{ step: 'sm', multiplier: '2', value: '
|
|
307
|
-
{ step: 'md', multiplier: '3', value: '
|
|
308
|
-
{ step: 'lg', multiplier: '
|
|
309
|
-
{ step: 'xl', multiplier: '
|
|
310
|
-
{ step: '
|
|
311
|
-
{ step: '
|
|
312
|
-
{ step: 'frame-bottom', multiplier: 'none', value: '38px' },
|
|
321
|
+
{ step: 'xs', multiplier: '1', value: '0.25rem' },
|
|
322
|
+
{ step: 'sm', multiplier: '2', value: '0.5rem' },
|
|
323
|
+
{ step: 'md', multiplier: '3.5', value: '0.875rem' },
|
|
324
|
+
{ step: 'lg', multiplier: '6', value: '1.5rem' },
|
|
325
|
+
{ step: 'xl', multiplier: '10', value: '2.5rem' },
|
|
326
|
+
{ step: '2xl', multiplier: '16', value: '4rem' },
|
|
327
|
+
{ step: '3xl', multiplier: '24', value: '6rem' },
|
|
313
328
|
],
|
|
314
329
|
|
|
315
330
|
bordersNote: [
|
package/src/gate/measures.ts
CHANGED
|
@@ -7,6 +7,11 @@ import {
|
|
|
7
7
|
type ClientCommand,
|
|
8
8
|
clientCommandCitationsIn,
|
|
9
9
|
} from '@/client-commands'
|
|
10
|
+
import {
|
|
11
|
+
isOverCount,
|
|
12
|
+
isOverLength,
|
|
13
|
+
measureArchitecture,
|
|
14
|
+
} from '@/context/architecture'
|
|
10
15
|
import { listRepositoryFiles } from '@/git-files'
|
|
11
16
|
import {
|
|
12
17
|
isShippedCorpus,
|
|
@@ -222,6 +227,41 @@ function citedPaths(record: { paths?: unknown } | undefined): string[] {
|
|
|
222
227
|
})
|
|
223
228
|
}
|
|
224
229
|
|
|
230
|
+
/**
|
|
231
|
+
* The architecture record against the two limits it states for itself: the
|
|
232
|
+
* line ceiling its allowances derive and the entry cap.
|
|
233
|
+
*
|
|
234
|
+
* Read in-process rather than through `context audit`, whose one gating stage
|
|
235
|
+
* here runs `--citations-only` and never opens the record, which is why the
|
|
236
|
+
* line ceiling went unenforced by `bun run check` until this stage. A project
|
|
237
|
+
* carrying no record, or a record stating neither limit, passes, since both
|
|
238
|
+
* limits belong to the record rather than to the toolkit.
|
|
239
|
+
*/
|
|
240
|
+
export const architectureRecord: Measure = async (ctx) => {
|
|
241
|
+
const report = await measureArchitecture(ctx.root)
|
|
242
|
+
if (report === undefined) {
|
|
243
|
+
return { emissions: [info('No architecture record to measure')] }
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const decisions = report.decisions.length
|
|
247
|
+
const failures = [
|
|
248
|
+
isOverCount(report) &&
|
|
249
|
+
`${decisions} decisions against a cap of ${report.entryCap}. Merge two or retire one in ${report.rel}, never compress.`,
|
|
250
|
+
isOverLength(report) &&
|
|
251
|
+
`${report.lines} lines against a ceiling of ${report.ceiling} in ${report.rel}.`,
|
|
252
|
+
].filter((failure): failure is string => typeof failure === 'string')
|
|
253
|
+
|
|
254
|
+
if (failures.length > 0) return { emissions: [], failure: failures.join(' ') }
|
|
255
|
+
|
|
256
|
+
const cap =
|
|
257
|
+
report.entryCap === undefined
|
|
258
|
+
? 'no entry cap stated'
|
|
259
|
+
: `a cap of ${report.entryCap}`
|
|
260
|
+
return {
|
|
261
|
+
emissions: [info(`${decisions} decisions against ${cap}`)],
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
225
265
|
/**
|
|
226
266
|
* A second run of the records move should rewrite nothing, and the count is
|
|
227
267
|
* only knowable once the folders themselves have landed.
|
package/src/gate/stages.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
architectureRecord,
|
|
2
3
|
auditSet,
|
|
3
4
|
captureStamps,
|
|
4
5
|
clientCommandCitations,
|
|
@@ -326,7 +327,7 @@ export const STAGES: readonly Stage[] = [
|
|
|
326
327
|
{
|
|
327
328
|
// A hook, a workflow, or a husky script reaching one side of the seed or
|
|
328
329
|
// config boundary and not the other is a capability withheld with no
|
|
329
|
-
// recorded reason, per the criterion in
|
|
330
|
+
// recorded reason, per the criterion in canon/context/tooling.md.
|
|
330
331
|
id: 'capability-seeding',
|
|
331
332
|
label: 'Capability seeding',
|
|
332
333
|
checks: [
|
|
@@ -362,6 +363,13 @@ export const STAGES: readonly Stage[] = [
|
|
|
362
363
|
],
|
|
363
364
|
success: 'Context citations resolve',
|
|
364
365
|
},
|
|
366
|
+
{
|
|
367
|
+
// The record's two limits are facts it states about itself, unlike the
|
|
368
|
+
// judgment thresholds the stage above leaves out, so they gate here.
|
|
369
|
+
id: 'architecture-record',
|
|
370
|
+
label: 'Architecture record',
|
|
371
|
+
checks: [{ kind: 'measure', measure: architectureRecord }],
|
|
372
|
+
},
|
|
365
373
|
{
|
|
366
374
|
// A rule citing a file that moved fails silently. The consumed-copy drift
|
|
367
375
|
// stage passes an authored rule and its copy that are wrong together, and
|
package/src/teach/workspace.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises'
|
|
3
|
-
import { join, relative } from 'node:path'
|
|
3
|
+
import { basename, join, relative } from 'node:path'
|
|
4
4
|
import { TEACH_STYLESHEET_COMPONENTS } from '@/design/components'
|
|
5
5
|
import { buildDesignCss } from '@/design/css'
|
|
6
6
|
import { parseFrontmatter, readField } from '@/indexes/frontmatter'
|
|
@@ -230,9 +230,14 @@ export function refuse(
|
|
|
230
230
|
* Every workspace sits under the main worktree root rather than under the
|
|
231
231
|
* checkout the caller stands in. Resolving that root belongs to the caller, so
|
|
232
232
|
* this takes one and never reads the working directory.
|
|
233
|
+
*
|
|
234
|
+
* A root already named `teach` is taken as the teach folder itself rather than
|
|
235
|
+
* a project root to wrap, which is what lets a caller point straight at a
|
|
236
|
+
* folder such as `examples/teach` that holds workspaces outside any `.canon/`
|
|
237
|
+
* or `.claude/` record root.
|
|
233
238
|
*/
|
|
234
239
|
export function teachDir(root: string): string {
|
|
235
|
-
return recordDir(root, 'teach')
|
|
240
|
+
return basename(root) === 'teach' ? root : recordDir(root, 'teach')
|
|
236
241
|
}
|
|
237
242
|
|
|
238
243
|
async function listSlugs(dir: string): Promise<string[]> {
|
|
@@ -241,6 +246,7 @@ async function listSlugs(dir: string): Promise<string[]> {
|
|
|
241
246
|
return entries
|
|
242
247
|
.filter((entry) => entry.isDirectory())
|
|
243
248
|
.map((entry) => entry.name)
|
|
249
|
+
.filter((name) => WORKSPACE_NAME.test(name))
|
|
244
250
|
.sort()
|
|
245
251
|
}
|
|
246
252
|
|
|
@@ -5,7 +5,7 @@ description: Shape and content rules for canon/ARCHITECTURE.md
|
|
|
5
5
|
|
|
6
6
|
# Architecture reference
|
|
7
7
|
|
|
8
|
-
Applies to `canon/ARCHITECTURE.md`. Describes the system shape and the decisions behind it, not a tutorial, setup guide, or implementation walkthrough. Pair it with `CLAUDE.md`:
|
|
8
|
+
Applies to `canon/ARCHITECTURE.md`. Describes the system shape and the decisions behind it, not a tutorial, setup guide, or implementation walkthrough. Pair it with `CLAUDE.md`: session behavior lives there, and the system's shape lives here. Update when a decision is made or a risk is resolved.
|
|
9
9
|
|
|
10
10
|
## Scope
|
|
11
11
|
|
|
@@ -20,15 +20,26 @@ Does not govern:
|
|
|
20
20
|
## What goes in
|
|
21
21
|
|
|
22
22
|
- A high-level overview of how the system is structured and why
|
|
23
|
-
- Key technical decisions as named H3 entries: what was chosen and why over the alternatives
|
|
23
|
+
- Key technical decisions as named H3 entries, each filling one of the five slots below: what was chosen and why over the alternatives
|
|
24
24
|
- Risks and open questions still unresolved
|
|
25
25
|
|
|
26
|
+
A decision belongs here only when it fills a slot:
|
|
27
|
+
|
|
28
|
+
- Stack and runtime: the language, the runtime, and the major libraries the system is built on
|
|
29
|
+
- Delivery: how the system's content or code reaches the place it runs
|
|
30
|
+
- Enforced boundaries: a boundary the system checks mechanically, and what enforces it
|
|
31
|
+
- Layout: how the project's top-level roots and context tiers divide what they hold
|
|
32
|
+
- Build principles: a rule for how a behavior is built that changes work in more than one domain, stated in one sentence
|
|
33
|
+
|
|
34
|
+
The slots come from a scale test. Ask whether the decision would still be in this file if the project were ten times its size. At that size an always-loaded file holding one entry per decision fits no context window, and what survives is the shape: the stack, the delivery, the boundaries, the layout, and the principles. A decision that fits no slot is a domain decision, and its home is that domain's `canon/context/<domain>.md` entry under `## Decisions`, however many domains its reasoning touches. Reach is not the test, since nearly every decision reaches a second domain.
|
|
35
|
+
|
|
26
36
|
## What does not go in
|
|
27
37
|
|
|
28
38
|
- How individual functions work line by line. The code carries its own behavior.
|
|
29
39
|
- Full type definitions. They live in code. Reference the shape conceptually if needed.
|
|
30
|
-
- A
|
|
31
|
-
- A
|
|
40
|
+
- A decision that fills no slot. It lives in the domain context entry it constrains, and this file carries at most one line pointing at it.
|
|
41
|
+
- A measurement paragraph specific to one domain's own mechanism, even behind a slot decision. Route it to that domain's context entry and keep the choice, the alternative that lost, and one reason here.
|
|
42
|
+
- The instances of a build principle. The principle takes one sentence here, and each instance lives in the domain entry where it applies.
|
|
32
43
|
- The history of how a decision was reached or revised: rounds of candidates, a figure followed by its correction, a branch or change that moved a number. That trail goes to the decision log or the change that introduced it.
|
|
33
44
|
|
|
34
45
|
## Sections
|
|
@@ -38,7 +49,7 @@ Use `## Overview`, `## Key technical decisions` with one named H3 per decision,
|
|
|
38
49
|
## Keeping it current
|
|
39
50
|
|
|
40
51
|
- 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.
|
|
41
|
-
- 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
|
|
52
|
+
- 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.
|
|
42
53
|
|
|
43
54
|
## Verification anchors
|
|
44
55
|
|
|
@@ -54,27 +65,41 @@ A decision's reasoning stays correct while the numbers it cites move. The anchor
|
|
|
54
65
|
- Do not edit a claim in the pass that first anchors it. The anchor states what the claim was measured against, so changing both at once leaves nothing to check the anchor against.
|
|
55
66
|
- Refresh the anchor whenever the number is re-read, whether or not it moved. A confirmed number and an unread one are the same text without the date.
|
|
56
67
|
|
|
57
|
-
##
|
|
68
|
+
## Entry cap
|
|
58
69
|
|
|
59
|
-
Every session pays for this file before any work starts, so a heavy read is a real cost.
|
|
70
|
+
Every session pays for this file before any work starts, so a heavy read is a real cost. A file that reads heavy is carrying too many decisions, not decisions written too long, so the bound is a count of decisions rather than of words.
|
|
60
71
|
|
|
61
|
-
-
|
|
72
|
+
- State the cap in the record itself as a clause of the form `This record holds at most <n> decisions.` The cap is the record's own, so a checker reads it from the file, and a record stating none is measured and never gated.
|
|
73
|
+
- Set the cap near one decision per slot plus a small margin.
|
|
74
|
+
- At the cap, merge two decisions or retire one before adding another, and name which in the change that does it. Never compress a decision's prose to fit, and never pack two decisions under one heading, which the count cannot see.
|
|
75
|
+
- Retire a decision by moving it to the domain context entry it constrains, not by deleting its reasoning.
|
|
76
|
+
- A word count, for the file and for each decision, is read alongside the judgment when one is available, and it never gates.
|
|
62
77
|
- Yield to the paragraph weight checkpoint in `markdown.md`. A paragraph past the checkpoint is a defect no length guideline licenses.
|
|
63
78
|
|
|
64
79
|
## Template
|
|
65
80
|
|
|
66
|
-
The anchor sentence closes a decision whose reasoning cites a measured number and is absent from one that cites none.
|
|
81
|
+
The anchor sentence closes a decision whose reasoning cites a measured number and is absent from one that cites none. Each heading below names a slot, so a record starts with one decision per slot and renames each heading to the choice it records.
|
|
67
82
|
|
|
68
83
|
```markdown
|
|
69
84
|
# Architecture
|
|
70
85
|
|
|
71
86
|
## Overview
|
|
72
87
|
|
|
88
|
+
This record holds at most 12 decisions.
|
|
89
|
+
|
|
73
90
|
## Key technical decisions
|
|
74
91
|
|
|
75
|
-
###
|
|
92
|
+
### Stack and runtime
|
|
93
|
+
|
|
94
|
+
What was chosen, the alternative that lost, and why. Measured at <short-sha> on <YYYY-MM-DD>.
|
|
95
|
+
|
|
96
|
+
### Delivery
|
|
97
|
+
|
|
98
|
+
### Enforced boundaries
|
|
99
|
+
|
|
100
|
+
### Layout
|
|
76
101
|
|
|
77
|
-
|
|
102
|
+
### Build principles
|
|
78
103
|
|
|
79
104
|
## Risks / open questions
|
|
80
105
|
```
|
package/standards/context.md
CHANGED
|
@@ -13,7 +13,7 @@ Governs per-domain narrative entries under `canon/context/`: their structure, th
|
|
|
13
13
|
|
|
14
14
|
Does not govern:
|
|
15
15
|
|
|
16
|
-
-
|
|
16
|
+
- Decisions filling a stack, delivery, boundary, layout, or build-principle slot, and system-wide risks: `architecture.md`
|
|
17
17
|
- Product scope, goals, and non-goals: `requirements.md`
|
|
18
18
|
- Path-scoped coding rules: `rule.md`
|
|
19
19
|
- Diagrams and wireframes, which answer structure and layout questions an entry hands off: `diagrams.md` and `wireframes.md`
|
|
@@ -82,7 +82,7 @@ Only the `development` entry carries this section. It is not a general-purpose h
|
|
|
82
82
|
## What goes in
|
|
83
83
|
|
|
84
84
|
- Per-domain narrative on how the code is structured and why
|
|
85
|
-
- Decisions
|
|
85
|
+
- Decisions that constrain the domain, whether or not their reasoning touches another one. This is a decision's default home, and `canon/ARCHITECTURE.md` takes only a decision that fills one of the slots its standard names.
|
|
86
86
|
- Constraints, gotchas, things tried and rejected
|
|
87
87
|
- Domain-specific conventions that do not fit a `paths:`-scoped rule
|
|
88
88
|
- A measured figure, anchored the way `architecture.md` states under `## Verification anchors`, so an entry and the architecture record date a number the same way.
|
package/standards/ready.md
CHANGED
|
@@ -11,6 +11,8 @@ The folder is gitignored, and backed wherever a records remote is configured: `c
|
|
|
11
11
|
|
|
12
12
|
## Scope
|
|
13
13
|
|
|
14
|
+
The `draft-ready` skill owns the procedure that writes a ready folder, and this standard owns the shape it writes to.
|
|
15
|
+
|
|
14
16
|
Governs a ready folder under `.canon/ready/<nn>-<slug>/`: folder layout, ordinal naming, the overview's frontmatter, what the mirrored tree holds, the thin-plan contract that ships it, and the lifecycle from the live folder to the archive.
|
|
15
17
|
|
|
16
18
|
Does not govern:
|
package/standards/session.md
CHANGED
|
@@ -18,6 +18,7 @@ Does not govern:
|
|
|
18
18
|
- The task board the file sits beside, its filenames, and its archiving: `tasks.md`
|
|
19
19
|
- The transform from a branch name to the slug in the filename: `slug.md`
|
|
20
20
|
- The role-specific sections a caller adds over the core, which belong to that caller
|
|
21
|
+
- The note a plain session writes at `.canon/compact/<slug>.md`, whose shape lives in the `session-compact` skill's own reference
|
|
21
22
|
- Which memories a session captures before writing, which is the capture surface's own subject
|
|
22
23
|
|
|
23
24
|
## What a working session map looks like
|
|
@@ -4,10 +4,20 @@ Authoring guidance: the architecture standard.
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
+
This record holds at most 12 decisions.
|
|
8
|
+
|
|
7
9
|
## Key technical decisions
|
|
8
10
|
|
|
9
|
-
###
|
|
11
|
+
### Stack and runtime
|
|
12
|
+
|
|
13
|
+
What was chosen, the alternative that lost, and why.
|
|
14
|
+
|
|
15
|
+
### Delivery
|
|
16
|
+
|
|
17
|
+
### Enforced boundaries
|
|
18
|
+
|
|
19
|
+
### Layout
|
|
10
20
|
|
|
11
|
-
|
|
21
|
+
### Build principles
|
|
12
22
|
|
|
13
23
|
## Risks / open questions
|