@erclx/canon 5.19.0 → 5.20.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/create-skill/SKILL.md +1 -0
- package/claude/skills/plan-feature/SKILL.md +1 -1
- package/claude/skills/plan-groundwork/SKILL.md +1 -1
- package/claude/skills/record-screencast/REQUIREMENT.md +2 -1
- package/claude/skills/record-screencast/SKILL.md +2 -2
- package/claude/skills/role-worker/REQUIREMENT.md +3 -1
- package/claude/skills/role-worker/SKILL.md +5 -3
- package/claude/skills/session-resume/SKILL.md +1 -1
- package/claude/skills/task-board/REQUIREMENT.md +1 -0
- package/claude/skills/task-board/SKILL.md +6 -0
- package/claude/skills/task-board/references/decline.md +2 -0
- package/claude/skills/video-craft/REQUIREMENT.md +48 -0
- package/claude/skills/video-craft/SKILL.md +80 -0
- package/claude/skills/video-craft/references/adopted.md +66 -0
- package/docs/agents/canvas.md +4 -0
- package/docs/agents/demo.md +1 -1
- package/docs/agents/install-and-sync.md +17 -3
- package/docs/agents/rule-citations.md +4 -4
- package/docs/agents/sandbox.md +8 -7
- package/docs/workflow/operating-model.md +9 -9
- package/docs/workflow/parallel-features.md +1 -1
- package/docs/workflow/skill-map.md +1 -0
- package/governance/renames.toml +24 -0
- package/governance/rules/{canon/640-architecture.md → standards/architecture.md} +3 -2
- package/governance/rules/standards/context.md +15 -0
- package/governance/rules/standards/design.md +13 -0
- package/governance/rules/standards/diagrams.md +17 -0
- package/governance/rules/standards/groundwork.md +13 -0
- package/governance/rules/standards/intake.md +12 -0
- package/governance/rules/standards/markdown.md +15 -0
- package/governance/rules/standards/memory.md +12 -0
- package/governance/rules/standards/mermaid.md +15 -0
- package/governance/rules/{canon/658-plan.md → standards/plan.md} +5 -9
- package/governance/rules/standards/readme.md +12 -0
- package/governance/rules/standards/ready.md +13 -0
- package/governance/rules/standards/requirements.md +13 -0
- package/governance/rules/standards/rule.md +13 -0
- package/governance/rules/standards/session.md +13 -0
- package/governance/rules/{claude/570-skill.md → standards/skill.md} +6 -10
- package/governance/rules/standards/standard.md +13 -0
- package/governance/rules/standards/tasks.md +15 -0
- package/governance/rules/{canon/661-teach.md → standards/teach.md} +3 -2
- package/governance/rules/standards/wiki.md +13 -0
- package/governance/rules/standards/wireframes.md +14 -0
- package/governance/rules/writing/800-prose.md +1 -1
- package/governance/stacks/base.toml +4 -2
- package/package.json +1 -1
- package/src/canvas/capture.ts +228 -15
- package/src/claude/cases/misc.ts +5 -0
- package/src/claude/routing.ts +1 -1
- package/src/commands/canvas.ts +76 -2
- package/src/commands/gov.ts +19 -4
- package/src/commands/sandbox.ts +7 -0
- package/src/gate/stages.ts +7 -1
- package/src/gov/citations.ts +1 -1
- package/src/gov/standard-rules.ts +179 -0
- package/src/sandbox/equivalence.ts +43 -14
- package/src/sandbox/stub-remote.ts +140 -0
- package/standards/architecture.md +5 -0
- package/standards/context.md +6 -0
- package/standards/design.md +3 -0
- package/standards/diagrams.md +8 -0
- package/standards/groundwork-spikes.md +66 -0
- package/standards/groundwork.md +5 -25
- package/standards/index.md +6 -2
- package/standards/intake.md +2 -0
- package/standards/markdown.md +6 -0
- package/standards/memory.md +2 -0
- package/standards/mermaid.md +6 -0
- package/standards/plan-lifecycle.md +36 -0
- package/standards/plan.md +13 -11
- package/standards/readme.md +2 -0
- package/standards/ready.md +4 -0
- package/standards/requirements.md +3 -0
- package/standards/rule.md +6 -1
- package/standards/session.md +4 -0
- package/standards/skill-paths.md +50 -0
- package/standards/skill.md +15 -23
- package/standards/standard.md +7 -1
- package/standards/tasks-decline.md +38 -0
- package/standards/tasks.md +8 -13
- package/standards/teach.md +5 -0
- package/standards/wiki.md +4 -0
- package/standards/wireframes.md +5 -0
- package/governance/rules/canon/610-context.md +0 -17
- package/governance/rules/canon/620-wireframes.md +0 -13
- package/governance/rules/canon/630-requirements.md +0 -12
- package/governance/rules/canon/650-design.md +0 -12
- package/governance/rules/canon/655-tasks.md +0 -13
- package/governance/rules/canon/656-groundwork.md +0 -11
- package/governance/rules/canon/657-intake.md +0 -11
- package/governance/rules/canon/659-memory.md +0 -11
- package/governance/rules/canon/660-diagrams.md +0 -19
- package/governance/rules/canon/662-session.md +0 -15
- package/governance/rules/canon/663-ready.md +0 -15
- package/governance/rules/claude/590-rule-authoring.md +0 -12
- package/governance/rules/writing/801-markdown.md +0 -14
- package/governance/rules/writing/802-mermaid.md +0 -14
- package/governance/rules/writing/880-readme.md +0 -11
- package/governance/rules/writing/891-standard-authoring.md +0 -12
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
2
|
+
# Generated by `canon gov regen` from standards/architecture.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Shape and content rules for canon/ARCHITECTURE.md
|
|
3
4
|
paths:
|
|
4
5
|
- 'canon/ARCHITECTURE.md'
|
|
5
6
|
- '.claude/ARCHITECTURE.md'
|
|
@@ -9,5 +10,5 @@ paths:
|
|
|
9
10
|
|
|
10
11
|
## Authority
|
|
11
12
|
|
|
12
|
-
- Follow the architecture standard for
|
|
13
|
+
- Follow the architecture standard for shape and content rules for canon/ARCHITECTURE.md. It is the single source. Read it with `canon standards architecture`.
|
|
13
14
|
- 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.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/context.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Shape and content rules for canon/context/<domain>.md entries
|
|
4
|
+
paths:
|
|
5
|
+
- 'canon/context/**'
|
|
6
|
+
- '.claude/context/**'
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Context entry standards
|
|
10
|
+
|
|
11
|
+
## Authority
|
|
12
|
+
|
|
13
|
+
- Follow the context standard for shape and content rules for canon/context/<domain>.md entries. It is the single source. Read it with `canon standards context`.
|
|
14
|
+
- Before non-trivial work in a domain, read its `canon/context/<domain>.md` entry. Use `canon/context/index.md` to find it.
|
|
15
|
+
- Leave the entry conforming when work in its domain changes what it describes.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/design.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Shape and content rules for canon/DESIGN.md
|
|
4
|
+
paths:
|
|
5
|
+
- 'canon/DESIGN.md'
|
|
6
|
+
- '.claude/DESIGN.md'
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Design standards
|
|
10
|
+
|
|
11
|
+
## Authority
|
|
12
|
+
|
|
13
|
+
- Follow the design standard for shape and content rules for canon/DESIGN.md. It is the single source. Read it with `canon standards design`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/diagrams.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Shape and content rules for .canon/diagrams/<kind>.md files
|
|
4
|
+
paths:
|
|
5
|
+
- '.canon/diagrams/**'
|
|
6
|
+
- '.claude/DIAGRAMS.md'
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Diagram standards
|
|
10
|
+
|
|
11
|
+
## Authority
|
|
12
|
+
|
|
13
|
+
- Follow the diagrams standard for shape and content rules for .canon/diagrams/<kind>.md files. It is the single source. Read it with `canon standards diagrams`.
|
|
14
|
+
- Layout, budgets, accessibility, labels, and render verification inside the fence are a separate topic. The mermaid standard routes them.
|
|
15
|
+
- A diagram entry carries structure and flow, not implementation. Read the standard before adding or revising a kind.
|
|
16
|
+
- Write a new diagram to `.canon/diagrams/<kind>.md`, never to `.claude/DIAGRAMS.md`
|
|
17
|
+
- Convert a `.claude/DIAGRAMS.md` left by an older install into per-kind entries before editing it
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/groundwork.md. Edit that frontmatter, not this file.
|
|
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
|
+
---
|
|
7
|
+
|
|
8
|
+
# Groundwork standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the groundwork standard for folder layout, ordinal naming, reserved numbering, frontmatter and dating, required file contents, and conventions for a measurement track. It is the single source. Read it with `canon standards groundwork`.
|
|
13
|
+
- Follow the groundwork spikes standard for `08-spikes.md` and the folders holding what a spike produced. Read it with `canon standards groundwork-spikes`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/intake.md. Edit that frontmatter, not this file.
|
|
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/**'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Intake standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the intake standard for folder layout, ordinal naming, reserved index number, frontmatter and dating, the item template, the answer contract, and retrieval. It is the single source. Read it with `canon standards intake`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/markdown.md. Edit that frontmatter, not this file.
|
|
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
|
+
---
|
|
7
|
+
|
|
8
|
+
# Markdown standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the markdown standard for headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, file references, American spelling, and frontmatter wording. It is the single source. Read it with `canon standards markdown`.
|
|
13
|
+
- Read it before a substantial markdown edit. Do not work the banned characters from memory.
|
|
14
|
+
- Run `canon markdown audit <path>` after the edit, and rewrite the sentence carrying a hit rather than swapping the token for a near-synonym.
|
|
15
|
+
- Voice, rhythm, and sentence construction are a separate topic. `800-prose` routes them.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/memory.md. Edit that frontmatter, not this file.
|
|
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/**'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Memory standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the memory standard for filename and type prefix, frontmatter, the body shape per type, links between entries, and the lifecycle from write to retire. It is the single source. Read it with `canon standards memory`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/mermaid.md. Edit that frontmatter, not this file.
|
|
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
|
+
---
|
|
7
|
+
|
|
8
|
+
# Mermaid standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the mermaid standard for direction and layout, node and edge budgets, accessibility fields, label punctuation, and render verification for a Mermaid diagram. It is the single source. Read it with `canon standards mermaid`.
|
|
13
|
+
- Apply this rule only when the edit drafts or revises a Mermaid fence. Skip it on a markdown edit carrying no diagram.
|
|
14
|
+
- Read it before drafting the fence. Do not work the layout or the budgets from memory.
|
|
15
|
+
- Which diagram a document carries and what question it answers is a separate topic. The diagrams standard routes it.
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
2
|
+
# Generated by `canon gov regen` from standards/plan.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Filename and slug, the required sections, and the suggested-and-answer contract for a feature plan
|
|
3
4
|
paths:
|
|
4
5
|
- '.canon/plans/**'
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
# Plan standards
|
|
8
9
|
|
|
9
|
-
##
|
|
10
|
+
## Authority
|
|
10
11
|
|
|
12
|
+
- Follow the plan standard for filename and slug, the required sections, and the suggested-and-answer contract for a feature plan. It is the single source. Read it with `canon standards plan`.
|
|
11
13
|
- Never fill an `- Answer:` slot on behalf of the person who owns it. A blank slot accepts the suggestion at execution time.
|
|
12
14
|
- Never ship a question without a `- Suggested:` line. Write `- Suggested: needs your call, <why>` where the answer turns on preference.
|
|
13
15
|
- 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`.
|
|
14
|
-
|
|
15
|
-
## Archiving
|
|
16
|
-
|
|
17
16
|
- Move a shipped plan to `.canon/plans/archive/`. Never delete one.
|
|
18
17
|
- Amend a plan in place when a decision changes. Do not append a second passage narrating the change.
|
|
19
|
-
|
|
20
|
-
## Authority
|
|
21
|
-
|
|
22
|
-
- Follow the plan standard for the filename and slug, the required sections, the suggested-and-answer contract, and the lifecycle. It is the single source. Read it with `canon standards plan`.
|
|
18
|
+
- 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`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/readme.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Readme voice, structure, and content conventions
|
|
4
|
+
paths:
|
|
5
|
+
- '**/README.md'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Readme standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the readme standard for readme voice, structure, and content conventions. It is the single source. Read it with `canon standards readme`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/ready.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Folder layout, ordinal naming, the overview frontmatter, the thin-plan contract, and the archive lifecycle for a finished-file handoff
|
|
4
|
+
paths:
|
|
5
|
+
- '.canon/ready/**'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Ready standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the ready standard for folder layout, ordinal naming, the overview frontmatter, the thin-plan contract, and the archive lifecycle for a finished-file handoff. It is the single source. Read it with `canon standards ready`.
|
|
13
|
+
- Load the `canon:draft-ready` skill to write a ready folder, its overview, the thin plan, and the task row. It owns the procedure. Report it rather than proceeding silently when it does not resolve, since it ships with the plugin and this rule ships with the CLI.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/requirements.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Shape and content rules for canon/REQUIREMENTS.md
|
|
4
|
+
paths:
|
|
5
|
+
- 'canon/REQUIREMENTS.md'
|
|
6
|
+
- '.claude/REQUIREMENTS.md'
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Requirements standards
|
|
10
|
+
|
|
11
|
+
## Authority
|
|
12
|
+
|
|
13
|
+
- Follow the requirements standard for shape and content rules for canon/REQUIREMENTS.md. It is the single source. Read it with `canon standards requirements`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/rule.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Rule frontmatter, body shape, and voice for .claude/rules files
|
|
4
|
+
paths:
|
|
5
|
+
- '.claude/rules/**/*.md'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Governance rule standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the rule standard for rule frontmatter, body shape, and voice for .claude/rules files. It is the single source. Read it with `canon standards rule`.
|
|
13
|
+
- Read it before writing or editing a rule. Do not work the shape from memory.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/session.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Filename and location, the sections a handoff carries, the write and read procedures, and how a role extends it
|
|
4
|
+
paths:
|
|
5
|
+
- '.canon/tasks/session-*.md'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Session map standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the session standard for filename and location, the sections a handoff carries, the write and read procedures, and how a role extends it. It is the single source. Read it with `canon standards session`.
|
|
13
|
+
- Follow this rule rather than the tasks rule for a `session-` file. A handoff is not a task and carries neither `## Outcomes` nor `## Findings`.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
2
|
+
# Generated by `canon gov regen` from standards/skill.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Skill structure and authoring rules for Claude Code
|
|
3
4
|
paths:
|
|
4
5
|
- '.claude/skills/**/SKILL.md'
|
|
5
6
|
- 'claude/skills/**/SKILL.md'
|
|
@@ -7,19 +8,14 @@ paths:
|
|
|
7
8
|
- 'claude/skills/**/REQUIREMENT.md'
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
#
|
|
11
|
+
# Claude skill standards
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
+
## Authority
|
|
13
14
|
|
|
15
|
+
- Follow the skill standard for skill structure and authoring rules for Claude Code. It is the single source. Read it with `canon standards skill`.
|
|
14
16
|
- Confirm `canon:create-skill`'s two creation-time questions are answered before a new `SKILL.md` lands, whether drafted by hand, by another skill, or by `canon:create-skill` itself. Carry the third question into the sibling `REQUIREMENT.md`'s `Must not` section as a review criterion rather than a gate.
|
|
15
17
|
- Report it rather than proceeding silently when `canon:create-skill` does not resolve. It ships with the plugin and this rule ships with the CLI, so a project that installed governance alone does not have it.
|
|
16
|
-
|
|
17
|
-
## After editing
|
|
18
|
-
|
|
19
18
|
- Re-read a skill body this session edited before invoking that skill again in the same session
|
|
20
19
|
- Do not read a resolved file path in a held body as evidence the body is current
|
|
21
|
-
|
|
22
|
-
## Authority
|
|
23
|
-
|
|
24
|
-
- Follow the skill standard for skill structure, frontmatter fields, and invocation rules. It is the single source. Read it with `canon standards skill`.
|
|
25
20
|
- Follow the skill requirement standard for the shape a `REQUIREMENT.md` states. Read it with `canon standards skill-requirement`.
|
|
21
|
+
- Follow the skill path standard for which root a path in a body resolves against and how a body cites a standard. Read it with `canon standards skill-paths`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/standard.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Shape and content rules for authoring a standard
|
|
4
|
+
paths:
|
|
5
|
+
- 'standards/**/*.md'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Standard standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the standard standard for shape and content rules for authoring a standard. It is the single source. Read it with `canon standards standard`.
|
|
13
|
+
- Read it before writing or editing a standard. Do not work the shape from memory.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/tasks.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Folder layout, filename convention, file format, origin lines, and archiving for task files under .canon/tasks/
|
|
4
|
+
paths:
|
|
5
|
+
- '.canon/tasks/**'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Tasks standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the tasks standard for folder layout, filename convention, file format, origin lines, and archiving for task files under .canon/tasks/. It is the single source. Read it with `canon standards tasks`.
|
|
13
|
+
- Follow the board standard for `priority.md` and `backlog.md`, their readiness groups, and row order. Read it with `canon standards board`.
|
|
14
|
+
- Never hand-edit `.canon/tasks/index.md`. A hook regenerates it from sibling frontmatter.
|
|
15
|
+
- Follow the task decline standard for a task decided against. Read it with `canon standards tasks-decline`.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
2
|
+
# Generated by `canon gov regen` from standards/teach.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Workspace layout, ordinal naming, frontmatter, and the mission and learning-record formats for a learning workspace
|
|
3
4
|
paths:
|
|
4
5
|
- '.canon/teach/**'
|
|
5
6
|
---
|
|
@@ -8,6 +9,6 @@ paths:
|
|
|
8
9
|
|
|
9
10
|
## Authority
|
|
10
11
|
|
|
11
|
-
- Follow the teach standard for
|
|
12
|
+
- Follow the teach standard for workspace layout, ordinal naming, frontmatter, and the mission and learning-record formats for a learning workspace. It is the single source. Read it with `canon standards teach`.
|
|
12
13
|
- Follow the glossary standard for the glossary every workspace carries at its root. It is the single source for the entry shape, the ordering, and which terms the file carries. Read it with `canon standards glossary`.
|
|
13
14
|
- Report it rather than proceeding silently when that file does not resolve. It ships with the plugin and this rule ships with the CLI, so a project that installed governance alone does not have it.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/wiki.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Folder split, frontmatter, naming convention, and sourcing for a wiki reference page
|
|
4
|
+
paths:
|
|
5
|
+
- 'wiki/**/*.md'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Wiki standards
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
- Follow the wiki standard for folder split, frontmatter, naming convention, and sourcing for a wiki reference page. It is the single source. Read it with `canon standards wiki`.
|
|
13
|
+
- Read it before drafting a page. Do not work the placement criterion or the naming convention from memory.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Generated by `canon gov regen` from standards/wireframes.md. Edit that frontmatter, not this file.
|
|
3
|
+
description: Shape and content rules for canon/wireframes/<surface>.md files
|
|
4
|
+
paths:
|
|
5
|
+
- 'canon/wireframes/**'
|
|
6
|
+
- '.claude/wireframes/**'
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Wireframe standards
|
|
10
|
+
|
|
11
|
+
## Authority
|
|
12
|
+
|
|
13
|
+
- Follow the wireframes standard for shape and content rules for canon/wireframes/<surface>.md files. It is the single source. Read it with `canon standards wireframes`.
|
|
14
|
+
- Read it before adding or revising a surface.
|
|
@@ -12,4 +12,4 @@ paths:
|
|
|
12
12
|
- Load it before drafting a passage, not after revising one.
|
|
13
13
|
- Report it rather than proceeding silently when the skill does not resolve.
|
|
14
14
|
- Do not work these rules from memory.
|
|
15
|
-
- Punctuation, formatting, and file references are a separate topic.
|
|
15
|
+
- Punctuation, formatting, and file references are a separate topic. The markdown rule routes them.
|
|
@@ -5,5 +5,7 @@ extends = ""
|
|
|
5
5
|
# session conduct and `.claude/` authoring rules, `tooling` the dev setup and
|
|
6
6
|
# CI workflow rules, and `writing` the prose and document-shape rules. The
|
|
7
7
|
# `code` group is left to the stacks that write code, so a docs or writing
|
|
8
|
-
# project loads no rule it can never apply.
|
|
9
|
-
|
|
8
|
+
# project loads no rule it can never apply. `standards` holds one rule per
|
|
9
|
+
# standard routing its paths to it, generated by `canon gov regen` from each
|
|
10
|
+
# standard's frontmatter rather than authored.
|
|
11
|
+
rules = ["canon", "claude", "standards", "tooling", "writing", "120-bash"]
|
package/package.json
CHANGED
package/src/canvas/capture.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, join } from 'node:path'
|
|
2
3
|
import type { CaptureResult } from '@/capture/render'
|
|
3
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type ContentRefused,
|
|
6
|
+
canvasDir,
|
|
7
|
+
type Frame,
|
|
8
|
+
readPage,
|
|
9
|
+
} from '@/canvas/content'
|
|
4
10
|
import {
|
|
5
11
|
type CanvasOptions,
|
|
6
12
|
type CanvasOutcome,
|
|
@@ -134,6 +140,34 @@ export async function captureCanvas(
|
|
|
134
140
|
const early = resolveCaptureTargets(root, spec, '')
|
|
135
141
|
if (!early.ok) return early
|
|
136
142
|
|
|
143
|
+
return serveFor(root, deps, async (base, capture) => {
|
|
144
|
+
const resolved = resolveCaptureTargets(root, spec, base, out)
|
|
145
|
+
if (!resolved.ok) return resolved
|
|
146
|
+
|
|
147
|
+
const captures: { target: CaptureTarget; result: CaptureResult }[] = []
|
|
148
|
+
for (const target of resolved.targets) {
|
|
149
|
+
const [result] = await capture(target.url, {
|
|
150
|
+
selector: CAPTURE_SELECTOR,
|
|
151
|
+
width: target.width,
|
|
152
|
+
outDir: target.pngPath,
|
|
153
|
+
})
|
|
154
|
+
if (result) captures.push({ target, result })
|
|
155
|
+
}
|
|
156
|
+
return { ok: true, captures }
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
type ServeRefused = {
|
|
161
|
+
readonly ok: false
|
|
162
|
+
readonly reason: 'no-server' | 'capture-failed'
|
|
163
|
+
readonly detail: string
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function serveFor<T>(
|
|
167
|
+
root: string,
|
|
168
|
+
deps: CaptureDeps,
|
|
169
|
+
run: (base: string, capture: CaptureEngine) => Promise<T>,
|
|
170
|
+
): Promise<T | ServeRefused> {
|
|
137
171
|
const start = deps.start ?? startCanvas
|
|
138
172
|
const server = start(root, { port: 0, shell: new Response('') })
|
|
139
173
|
if (!server.ok) {
|
|
@@ -145,21 +179,9 @@ export async function captureCanvas(
|
|
|
145
179
|
}
|
|
146
180
|
|
|
147
181
|
try {
|
|
148
|
-
const resolved = resolveCaptureTargets(root, spec, server.url, out)
|
|
149
|
-
if (!resolved.ok) return resolved
|
|
150
|
-
|
|
151
182
|
const capture =
|
|
152
183
|
deps.capture ?? (await import('@/capture/render')).captureSources
|
|
153
|
-
|
|
154
|
-
for (const target of resolved.targets) {
|
|
155
|
-
const [result] = await capture(target.url, {
|
|
156
|
-
selector: CAPTURE_SELECTOR,
|
|
157
|
-
width: target.width,
|
|
158
|
-
outDir: target.pngPath,
|
|
159
|
-
})
|
|
160
|
-
if (result) captures.push({ target, result })
|
|
161
|
-
}
|
|
162
|
-
return { ok: true, captures }
|
|
184
|
+
return await run(server.url, capture)
|
|
163
185
|
} catch (error) {
|
|
164
186
|
return {
|
|
165
187
|
ok: false,
|
|
@@ -170,3 +192,194 @@ export async function captureCanvas(
|
|
|
170
192
|
await server.stop()
|
|
171
193
|
}
|
|
172
194
|
}
|
|
195
|
+
|
|
196
|
+
/** The element a composite is captured by, holding every frame and no chrome. */
|
|
197
|
+
export const COMPOSITE_SELECTOR = '#canvas-composite'
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The container holds no text, so its family exists only for the engine's
|
|
201
|
+
* font probe, which rejects a family the machine lacks. The browser default is
|
|
202
|
+
* Times New Roman, which a Linux runner often lacks, and `system-ui` is the one
|
|
203
|
+
* every platform maps. Each frame keeps its own fonts inside its iframe.
|
|
204
|
+
*/
|
|
205
|
+
const COMPOSITE_FONT = 'system-ui'
|
|
206
|
+
|
|
207
|
+
export interface Placement {
|
|
208
|
+
readonly frame: string
|
|
209
|
+
readonly url: string
|
|
210
|
+
readonly left: number
|
|
211
|
+
readonly top: number
|
|
212
|
+
readonly width: number
|
|
213
|
+
readonly height: number
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface Composite {
|
|
217
|
+
readonly placements: readonly Placement[]
|
|
218
|
+
readonly width: number
|
|
219
|
+
readonly height: number
|
|
220
|
+
readonly html: string
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* One document showing every frame of a page at its layout position. Offsets
|
|
225
|
+
* run from the top left of the union of the boxes, so a negative coordinate
|
|
226
|
+
* never clips and a lone frame fills the container exactly. Each iframe takes
|
|
227
|
+
* its box, so a document taller than its frame clips as it does on the board,
|
|
228
|
+
* and frames paint in the order given, which is the board's.
|
|
229
|
+
*/
|
|
230
|
+
export function buildComposite(
|
|
231
|
+
page: string,
|
|
232
|
+
frames: readonly Frame[],
|
|
233
|
+
base: string,
|
|
234
|
+
): Composite {
|
|
235
|
+
const left = Math.min(...frames.map((frame) => frame.x))
|
|
236
|
+
const top = Math.min(...frames.map((frame) => frame.y))
|
|
237
|
+
const right = Math.max(...frames.map((frame) => frame.x + frame.width))
|
|
238
|
+
const bottom = Math.max(...frames.map((frame) => frame.y + frame.height))
|
|
239
|
+
const width = right - left
|
|
240
|
+
const height = bottom - top
|
|
241
|
+
|
|
242
|
+
const placements = frames.map((frame) => ({
|
|
243
|
+
frame: frame.name,
|
|
244
|
+
url: frameUrl(base, page, frame),
|
|
245
|
+
left: frame.x - left,
|
|
246
|
+
top: frame.y - top,
|
|
247
|
+
width: frame.width,
|
|
248
|
+
height: frame.height,
|
|
249
|
+
}))
|
|
250
|
+
|
|
251
|
+
const iframes = placements.map(
|
|
252
|
+
(p) =>
|
|
253
|
+
`<iframe title="${escapeAttribute(p.frame)}" src="${escapeAttribute(p.url)}" style="left:${p.left}px;top:${p.top}px;width:${p.width}px;height:${p.height}px"></iframe>`,
|
|
254
|
+
)
|
|
255
|
+
const html = [
|
|
256
|
+
'<!doctype html>',
|
|
257
|
+
'<html><head><meta charset="utf-8">',
|
|
258
|
+
`<style>html,body{margin:0;background:transparent}#canvas-composite{position:relative;font-family:${COMPOSITE_FONT}}#canvas-composite iframe{position:absolute;display:block;border:0;background:transparent}</style>`,
|
|
259
|
+
'</head><body>',
|
|
260
|
+
`<div id="canvas-composite" style="width:${width}px;height:${height}px">`,
|
|
261
|
+
...iframes,
|
|
262
|
+
'</div></body></html>',
|
|
263
|
+
'',
|
|
264
|
+
].join('\n')
|
|
265
|
+
|
|
266
|
+
return { placements, width, height, html }
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function escapeAttribute(value: string): string {
|
|
270
|
+
return value.replaceAll('&', '&').replaceAll('"', '"')
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Where the generated document is served from, inside the canvas folder so it
|
|
275
|
+
* shares the frames' origin. Chromium skips painting a cross-origin iframe
|
|
276
|
+
* outside the viewport, and the engine's viewport is 720 pixels tall, so a
|
|
277
|
+
* document on another origin captured every frame below that line blank. A dot
|
|
278
|
+
* folder is no page name, so the board never lists it.
|
|
279
|
+
*/
|
|
280
|
+
const COMPOSITE_FOLDER = '.composite'
|
|
281
|
+
|
|
282
|
+
export interface CompositeTarget {
|
|
283
|
+
readonly page: string
|
|
284
|
+
readonly composite: Composite
|
|
285
|
+
readonly htmlPath: string
|
|
286
|
+
/** The served address of the document at `htmlPath`. */
|
|
287
|
+
readonly url: string
|
|
288
|
+
readonly pngPath: string
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export type CompositeTargetOutcome =
|
|
292
|
+
| { readonly ok: true; readonly target: CompositeTarget }
|
|
293
|
+
| ContentRefused
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Turns `<page>` into one composite. `out` is the PNG, and without it the PNG
|
|
297
|
+
* lands at `<page>.png` beside the per-frame folder of the same name, so the
|
|
298
|
+
* two captures of one page never collide.
|
|
299
|
+
*/
|
|
300
|
+
export function resolveCompositeTarget(
|
|
301
|
+
root: string,
|
|
302
|
+
spec: string,
|
|
303
|
+
base: string,
|
|
304
|
+
out?: string,
|
|
305
|
+
): CompositeTargetOutcome {
|
|
306
|
+
if (spec.includes('/')) {
|
|
307
|
+
return refuse(
|
|
308
|
+
'invalid-name',
|
|
309
|
+
`--composite takes a page, not ${spec}, so capture <page> or drop the flag`,
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const page = readPage(root, spec)
|
|
314
|
+
if (!page) return refuse('no-page', `page ${spec} does not exist`)
|
|
315
|
+
if (page.frames.length === 0) {
|
|
316
|
+
return refuse('no-frame', `page ${spec} has no frames`)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const file = `${spec}.html`
|
|
320
|
+
return {
|
|
321
|
+
ok: true,
|
|
322
|
+
target: {
|
|
323
|
+
page: spec,
|
|
324
|
+
composite: buildComposite(spec, page.frames, base),
|
|
325
|
+
htmlPath: join(canvasDir(root), COMPOSITE_FOLDER, file),
|
|
326
|
+
url: `${base}${FRAMES_PREFIX.slice(1)}${COMPOSITE_FOLDER}/${encodeURIComponent(file)}`,
|
|
327
|
+
pngPath:
|
|
328
|
+
out ?? join(recordDir(root, 'tmp', 'canvas-capture'), `${spec}.png`),
|
|
329
|
+
},
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export type CompositeOutcome =
|
|
334
|
+
| {
|
|
335
|
+
readonly ok: true
|
|
336
|
+
readonly composite: {
|
|
337
|
+
readonly target: CompositeTarget
|
|
338
|
+
readonly result: CaptureResult
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
| ContentRefused
|
|
342
|
+
| ServeRefused
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Captures a page as one PNG through the served canvas, the way `captureCanvas`
|
|
346
|
+
* captures each frame. The viewport is the container's width, so no frame's
|
|
347
|
+
* media query resolves against a window narrower than the frame itself. The
|
|
348
|
+
* page load the engine waits on waits on every iframe.
|
|
349
|
+
*/
|
|
350
|
+
export async function captureComposite(
|
|
351
|
+
root: string,
|
|
352
|
+
spec: string,
|
|
353
|
+
out?: string,
|
|
354
|
+
deps: CaptureDeps = {},
|
|
355
|
+
): Promise<CompositeOutcome> {
|
|
356
|
+
const early = resolveCompositeTarget(root, spec, '')
|
|
357
|
+
if (!early.ok) return early
|
|
358
|
+
|
|
359
|
+
return serveFor(root, deps, async (base, capture) => {
|
|
360
|
+
const resolved = resolveCompositeTarget(root, spec, base, out)
|
|
361
|
+
if (!resolved.ok) return resolved
|
|
362
|
+
|
|
363
|
+
const { target } = resolved
|
|
364
|
+
mkdirSync(dirname(target.htmlPath), { recursive: true })
|
|
365
|
+
writeFileSync(target.htmlPath, target.composite.html)
|
|
366
|
+
let result: CaptureResult | undefined
|
|
367
|
+
try {
|
|
368
|
+
;[result] = await capture(target.url, {
|
|
369
|
+
selector: COMPOSITE_SELECTOR,
|
|
370
|
+
width: target.composite.width,
|
|
371
|
+
outDir: target.pngPath,
|
|
372
|
+
})
|
|
373
|
+
} finally {
|
|
374
|
+
rmSync(target.htmlPath, { force: true })
|
|
375
|
+
}
|
|
376
|
+
if (!result) {
|
|
377
|
+
return {
|
|
378
|
+
ok: false,
|
|
379
|
+
reason: 'capture-failed',
|
|
380
|
+
detail: `the engine rendered nothing for ${spec}`,
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return { ok: true, composite: { target, result } }
|
|
384
|
+
})
|
|
385
|
+
}
|