@erclx/canon 5.44.0 → 5.46.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 +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/REQUIREMENT.md +15 -31
- package/claude/skills/auto-ship/REQUIREMENT.md +14 -25
- package/claude/skills/backlog-triage/REQUIREMENT.md +12 -23
- package/claude/skills/build-in-slices/REQUIREMENT.md +15 -25
- package/claude/skills/canon-cli/REQUIREMENT.md +15 -27
- package/claude/skills/canon-cli/SKILL.md +0 -1
- package/claude/skills/canon-feedback/REQUIREMENT.md +10 -18
- package/claude/skills/canon-feedback-triage/REQUIREMENT.md +10 -15
- package/claude/skills/canvas-board/REQUIREMENT.md +14 -28
- package/claude/skills/ci-workflow/REQUIREMENT.md +12 -21
- package/claude/skills/code-craft/REQUIREMENT.md +14 -28
- package/claude/skills/codebase-layout/REQUIREMENT.md +14 -31
- package/claude/skills/context-fold/REQUIREMENT.md +15 -39
- package/claude/skills/context-fold/references/context.md +3 -3
- package/claude/skills/create-rule/REQUIREMENT.md +10 -24
- package/claude/skills/create-rule/SKILL.md +3 -0
- package/claude/skills/create-skill/REQUIREMENT.md +10 -21
- package/claude/skills/decision-escalate/REQUIREMENT.md +12 -24
- package/claude/skills/deploy-app/REQUIREMENT.md +12 -22
- package/claude/skills/deprecation-migration/REQUIREMENT.md +14 -42
- package/claude/skills/design-identity/REQUIREMENT.md +14 -20
- package/claude/skills/design-taste/REQUIREMENT.md +16 -57
- package/claude/skills/docs-sync/REQUIREMENT.md +14 -23
- package/claude/skills/document-health/REQUIREMENT.md +15 -29
- package/claude/skills/document-health/SKILL.md +2 -2
- package/claude/skills/document-repair/REQUIREMENT.md +32 -0
- package/claude/skills/document-repair/SKILL.md +76 -0
- package/claude/skills/document-repair/references/batches.md +44 -0
- package/claude/skills/draft-and-pick/REQUIREMENT.md +4 -3
- package/claude/skills/draft-doc/REQUIREMENT.md +4 -3
- package/claude/skills/draft-figure/REQUIREMENT.md +4 -3
- package/claude/skills/draft-identity/REQUIREMENT.md +4 -3
- package/claude/skills/draft-ready/REQUIREMENT.md +4 -3
- package/claude/skills/draft-screencast/REQUIREMENT.md +4 -3
- package/claude/skills/draft-slides/REQUIREMENT.md +4 -3
- package/claude/skills/draw-figure/REQUIREMENT.md +15 -39
- package/claude/skills/git-branch/REQUIREMENT.md +12 -26
- package/claude/skills/git-commit/REQUIREMENT.md +11 -15
- package/claude/skills/git-followup/REQUIREMENT.md +11 -22
- package/claude/skills/git-issue/REQUIREMENT.md +9 -15
- package/claude/skills/git-pr/REQUIREMENT.md +13 -32
- package/claude/skills/git-pr/SKILL.md +10 -2
- package/claude/skills/git-pr/references/closing-issues.md +47 -0
- package/claude/skills/git-pr/references/pr.md +4 -0
- package/claude/skills/git-ship/REQUIREMENT.md +13 -26
- package/claude/skills/git-split/REQUIREMENT.md +10 -17
- package/claude/skills/git-stage/REQUIREMENT.md +12 -18
- package/claude/skills/git-worktree/REQUIREMENT.md +15 -19
- package/claude/skills/make-deck/REQUIREMENT.md +13 -21
- package/claude/skills/markdown-craft/REQUIREMENT.md +9 -3
- package/claude/skills/pick-by-eye/REQUIREMENT.md +17 -67
- package/claude/skills/plan-feature/REQUIREMENT.md +15 -24
- package/claude/skills/plan-groundwork/REQUIREMENT.md +12 -28
- package/claude/skills/plan-groundwork/SKILL.md +5 -2
- package/claude/skills/plan-groundwork/references/output.md +47 -1
- package/claude/skills/plan-intake/REQUIREMENT.md +11 -26
- package/claude/skills/plan-intake/references/intake.md +2 -2
- package/claude/skills/plan-intake-answer/REQUIREMENT.md +14 -29
- package/claude/skills/plan-intake-answer/SKILL.md +7 -1
- package/claude/skills/plan-ready/REQUIREMENT.md +17 -36
- package/claude/skills/pr-evidence/REQUIREMENT.md +11 -17
- package/claude/skills/read-frames/REQUIREMENT.md +3 -8
- package/claude/skills/record-screencast/REQUIREMENT.md +14 -17
- package/claude/skills/review-address/REQUIREMENT.md +14 -38
- package/claude/skills/review-craft/REQUIREMENT.md +14 -32
- package/claude/skills/review-pr/REQUIREMENT.md +15 -34
- package/claude/skills/review-ui/REQUIREMENT.md +14 -39
- package/claude/skills/role-orchestrator/REQUIREMENT.md +16 -99
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +5 -0
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +9 -0
- package/claude/skills/role-planner/REQUIREMENT.md +15 -73
- package/claude/skills/role-reviewer/REQUIREMENT.md +15 -65
- package/claude/skills/role-reviewer/SKILL.md +5 -1
- package/claude/skills/role-worker/REQUIREMENT.md +13 -41
- package/claude/skills/role-worker/SKILL.md +2 -0
- package/claude/skills/script-screencast/REQUIREMENT.md +10 -16
- package/claude/skills/search-craft/REQUIREMENT.md +12 -36
- package/claude/skills/search-craft/SKILL.md +1 -1
- package/claude/skills/search-craft/references/adopted.md +2 -2
- package/claude/skills/session-compact/REQUIREMENT.md +13 -23
- package/claude/skills/session-map/REQUIREMENT.md +13 -29
- package/claude/skills/session-relay/REQUIREMENT.md +14 -58
- package/claude/skills/session-relay/SKILL.md +6 -0
- package/claude/skills/session-resume/REQUIREMENT.md +13 -27
- package/claude/skills/session-worktree/REQUIREMENT.md +14 -43
- package/claude/skills/systematic-debugging/REQUIREMENT.md +13 -34
- package/claude/skills/target-check/REQUIREMENT.md +16 -23
- package/claude/skills/target-setup/REQUIREMENT.md +11 -34
- package/claude/skills/target-setup/SKILL.md +2 -2
- package/claude/skills/target-setup/references/detect.md +0 -1
- package/claude/skills/target-setup/references/verify.md +2 -2
- package/claude/skills/task-board/REQUIREMENT.md +13 -22
- package/claude/skills/task-board/SKILL.md +7 -0
- package/claude/skills/task-board/references/tasks.md +1 -1
- package/claude/skills/task-complete/REQUIREMENT.md +11 -24
- package/claude/skills/task-complete/references/archive.md +1 -0
- package/claude/skills/teach-workspace/REQUIREMENT.md +14 -45
- package/claude/skills/teach-workspace/references/promotion.md +5 -5
- package/claude/skills/test-craft/REQUIREMENT.md +15 -46
- package/claude/skills/test-first/REQUIREMENT.md +14 -20
- package/claude/skills/ui-checklist/REQUIREMENT.md +14 -29
- package/claude/skills/ux-audit/REQUIREMENT.md +3 -7
- package/claude/skills/ux-measure/REQUIREMENT.md +14 -28
- package/claude/skills/video-craft/REQUIREMENT.md +14 -27
- package/claude/skills/write-doc/REQUIREMENT.md +14 -24
- package/claude/skills/write-human/REQUIREMENT.md +15 -27
- package/claude/skills/youtube-transcripts/REQUIREMENT.md +9 -15
- package/docs/agents/context-audit.md +19 -12
- package/docs/agents/install-and-sync.md +2 -2
- package/docs/agents/markdown-audit.md +15 -10
- package/docs/agents/output-shape.md +1 -2
- package/docs/target/migrations.md +18 -0
- package/docs/target/projects.md +3 -2
- package/docs/target/scenarios.md +4 -1
- package/docs/workflow/skill-map-on-request.md +1 -1
- package/docs/workflow/skill-map.md +6 -5
- package/governance/detect.toml +4 -0
- package/governance/ledger/lang/go.md +61 -0
- package/governance/ledger/lang/php.md +50 -0
- package/governance/ledger/lang/python.md +68 -0
- package/governance/ledger/lang/typescript.md +64 -0
- package/governance/ledger/lib/database.md +65 -0
- package/governance/ledger/lib/e2e-reliability.md +57 -0
- package/governance/ledger/lib/pydantic.md +61 -0
- package/governance/ledger/lib/security-server.md +62 -0
- package/governance/ledger/lib/security-web.md +51 -0
- package/governance/ledger/lib/tanstack-query.md +57 -0
- package/governance/ledger/lib/test-scope.md +43 -0
- package/governance/ledger/lib/testing-go.md +57 -0
- package/governance/ledger/lib/testing-php.md +51 -0
- package/governance/ledger/lib/testing-py.md +67 -0
- package/governance/ledger/lib/testing-ts.md +63 -0
- package/governance/ledger/lib/zod.md +47 -0
- package/governance/ledger/tooling/ci-workflow.md +33 -0
- package/governance/ledger/tooling/code-comments.md +48 -0
- package/governance/ledger/tooling/config-comments.md +27 -0
- package/governance/ledger/tooling/dependencies.md +38 -0
- package/governance/ledger/ui/a11y.md +55 -0
- package/governance/ledger/ui/copy.md +49 -0
- package/governance/ledger/ui/design-reference.md +42 -0
- package/governance/ledger/ui/design-taste.md +19 -0
- package/governance/ledger/ui/forms.md +71 -0
- package/governance/ledger/ui/interface-casing.md +47 -0
- package/governance/ledger/ui/keyboard.md +68 -0
- package/governance/ledger/ui/link-behavior.md +29 -0
- package/governance/ledger/ui/motion.md +33 -0
- package/governance/ledger/ui/surface-capture.md +69 -0
- package/governance/ledger/ui/ux-completeness.md +79 -0
- package/governance/renames.toml +0 -2
- package/governance/rules/claude/output.md +5 -1
- package/governance/rules/code/routing.md +5 -1
- package/governance/rules/lang/go.md +7 -16
- package/governance/rules/lang/php.md +3 -3
- package/governance/rules/lang/python.md +9 -18
- package/governance/rules/lang/typescript.md +9 -19
- package/governance/rules/lib/database.md +5 -5
- package/governance/rules/lib/e2e-reliability.md +4 -6
- package/governance/rules/lib/pydantic.md +14 -16
- package/governance/rules/lib/security-server.md +4 -4
- package/governance/rules/lib/security-web.md +6 -10
- package/governance/rules/lib/tanstack-query.md +10 -12
- package/governance/rules/lib/test-scope.md +4 -4
- package/governance/rules/lib/testing-go.md +1 -2
- package/governance/rules/lib/testing-php.md +4 -5
- package/governance/rules/lib/testing-py.md +11 -19
- package/governance/rules/lib/testing-ts.md +7 -16
- package/governance/rules/lib/zod.md +6 -8
- package/governance/rules/tooling/code-comments.md +2 -4
- package/governance/rules/tooling/config-comments.md +1 -4
- package/governance/rules/tooling/dependencies.md +1 -1
- package/governance/rules/ui/a11y.md +16 -45
- package/governance/rules/ui/copy.md +6 -9
- package/governance/rules/ui/forms.md +13 -19
- package/governance/rules/ui/interface-casing.md +5 -7
- package/governance/rules/ui/keyboard.md +38 -0
- package/governance/rules/ui/link-behavior.md +3 -3
- package/governance/rules/ui/motion.md +3 -3
- package/governance/rules/ui/surface-capture.md +15 -27
- package/governance/rules/ui/ux-completeness.md +16 -39
- package/governance/stacks/base.toml +0 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +1 -1
- package/src/claude/cases/workflow.ts +12 -0
- package/src/cli/help.ts +0 -1
- package/src/cli.ts +0 -2
- package/src/commands/context.ts +57 -3
- package/src/commands/gov.ts +4 -0
- package/src/commands/init.ts +2 -2
- package/src/commands/labels.ts +83 -3
- package/src/commands/markdown.ts +188 -21
- package/src/commands/sessions.ts +36 -3
- package/src/commands/tasks.ts +180 -0
- package/src/commands/tooling.ts +18 -5
- package/src/commands/worktrees.ts +2 -0
- package/src/context/coverage.ts +210 -0
- package/src/context/gate.ts +12 -3
- package/src/design/components.ts +1 -1
- package/src/gate/stages.ts +1 -1
- package/src/gov/audit.ts +22 -2
- package/src/init/plan.ts +1 -8
- package/src/init/steps.ts +44 -7
- package/src/intake/items.ts +14 -0
- package/src/inventory/subjects.ts +1 -1
- package/src/labels/closing.ts +46 -0
- package/src/markdown/gated.ts +250 -0
- package/src/markdown/scope.ts +119 -2
- package/src/migrate/rename.ts +6 -0
- package/src/migrate/scratch-evidence.ts +12 -4
- package/src/records/backup.ts +3 -3
- package/src/records/validate.ts +3 -3
- package/src/roots/record.ts +17 -8
- package/src/sessions/claim.ts +7 -0
- package/src/sessions/live.ts +53 -6
- package/src/sessions/registry.ts +11 -0
- package/src/sessions/resolve.ts +32 -5
- package/src/tasks/closes.ts +136 -0
- package/src/tasks/record.ts +1 -1
- package/src/tooling/inject.ts +26 -7
- package/src/tooling/package.ts +32 -0
- package/src/tooling/read.ts +46 -3
- package/src/tooling/scan.ts +8 -2
- package/src/worktrees/reclaim.ts +16 -1
- package/tooling/astro/reference.md +64 -93
- package/tooling/base/configs/.github/workflows/phase-label-gate.yml +1 -1
- package/tooling/base/configs/scripts/verify.ts +2 -2
- package/tooling/base/reference.md +1 -9
- package/tooling/base/seeds/canon/context/{ci.md → development/ci.md} +1 -1
- package/tooling/base/seeds/canon/context/development/index.md +11 -0
- package/tooling/base/seeds/canon/context/{development.md → development/setup.md} +8 -12
- package/tooling/base/seeds/canon/context/index.md +1 -2
- package/tooling/claude/manifest.toml +1 -5
- package/tooling/claude/reference.md +36 -59
- package/tooling/claude/seeds/.claude/hooks/board-validate.sh +1 -1
- package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +2 -7
- package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +2 -7
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +3 -10
- package/tooling/claude/seeds/.claude/hooks/search-reminder.sh +2 -7
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +2 -4
- package/tooling/claude/seeds/CLAUDE.md +2 -2
- package/tooling/nextjs/reference.md +0 -6
- package/tooling/php/reference.md +0 -4
- package/tooling/web/configs/.github/workflows/verify.yml +28 -0
- package/tooling/web/configs/.oxlintrc.json +7 -0
- package/tooling/web/configs/e2e/capture-types.ts +35 -0
- package/tooling/web/configs/e2e/screenshot.ts +23 -72
- package/tooling/web/manifest.toml +1 -1
- package/tooling/web/reference.md +77 -124
- package/tooling/web/seeds/e2e/capture/cases.ts +47 -0
- package/governance/rules/canon/task-scope.md +0 -12
- package/src/commands/wiki.ts +0 -100
- package/src/wiki/init.ts +0 -85
|
@@ -11,9 +11,9 @@ paths:
|
|
|
11
11
|
|
|
12
12
|
## Default target
|
|
13
13
|
|
|
14
|
-
- Open a link in the same tab. Reserve `target="_blank"` for a
|
|
15
|
-
-
|
|
14
|
+
- Open a link in the same tab. Reserve `target="_blank"` for a page the person must consult while finishing a task in the current one.
|
|
15
|
+
- Never add `target="_blank"` as a default for every external link.
|
|
16
16
|
|
|
17
17
|
## mailto: links
|
|
18
18
|
|
|
19
|
-
- Open a `mailto:` link in the same tab. It hands off to the mail client rather than replacing page
|
|
19
|
+
- Open a `mailto:` link in the same tab. It hands off to the mail client rather than replacing the page, so a new tab leaves an empty tab behind.
|
|
@@ -12,12 +12,12 @@ paths:
|
|
|
12
12
|
|
|
13
13
|
## Moving content
|
|
14
14
|
|
|
15
|
-
- Give any content that moves, blinks, or scrolls automatically for more than five seconds a control to pause, stop, or hide it (WCAG
|
|
15
|
+
- Give any content that moves, blinks, or scrolls automatically for more than five seconds a control to pause, stop, or hide it (WCAG 2.2.2).
|
|
16
16
|
- Halt an auto-advancing carousel or ticker while it holds keyboard focus or pointer hover.
|
|
17
|
-
-
|
|
17
|
+
- Never flash content more than three times in any one second (WCAG 2.3.1).
|
|
18
18
|
|
|
19
19
|
## Reduced motion
|
|
20
20
|
|
|
21
21
|
- Honor `prefers-reduced-motion: reduce` for every animation and transition that conveys no state. Remove it or replace it with an instant change.
|
|
22
22
|
- Keep essential state changes visible under reduced motion, using a fade or a static indicator instead of movement.
|
|
23
|
-
-
|
|
23
|
+
- Start no video or animated background on its own when the preference is set.
|
|
@@ -12,39 +12,27 @@ paths:
|
|
|
12
12
|
|
|
13
13
|
## What a surface is
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- A component the production build strips out is not a surface, such as a dev-only scenario switcher gated behind a build flag. The build removes it before anything renders, so no comparison exists to capture.
|
|
15
|
+
- Treat anything the project renders for a person as a surface, a generated page as much as a route.
|
|
16
|
+
- Skip a component the production build strips out, such as a dev-only switcher.
|
|
18
17
|
|
|
19
18
|
## When to capture
|
|
20
19
|
|
|
21
|
-
- Run the project's capture command after changing
|
|
22
|
-
- Capture
|
|
23
|
-
- Capture every theme the surface ships. Do not capture the default theme alone.
|
|
24
|
-
- Capture again after fixing a defect a capture found. A repair inside a shared stylesheet can cancel a rule written earlier in the same file.
|
|
20
|
+
- Run the project's capture command against a preview server, never a dev server, after changing a surface. Report it when the project has none.
|
|
21
|
+
- Capture every theme the surface ships, and again after fixing a defect a capture found.
|
|
25
22
|
|
|
26
23
|
## What a capture covers
|
|
27
24
|
|
|
28
|
-
- Capture the full page
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
- Read a capture at 320 as proof that a frame was taken there, not that the page reflows correctly. The criterion is stated in `a11y` and nothing here enforces it.
|
|
35
|
-
- Write the capture into a folder named `evidence`. The image is collected for the pull request comment when any segment of its path is literally `evidence` and its filename carries an image extension, so the folder name is fixed and its position in the tree is the project's own.
|
|
36
|
-
- Several `evidence/` folders in one project is the ordinary shape rather than a problem to consolidate. One per surface is how a project with more than one rendered surface ends up.
|
|
37
|
-
- Drive a state a screenshot cannot reach. An initial render reports nothing about a menu that opens, an answer that is chosen, or a rail that tracks scrolling.
|
|
38
|
-
- Add a case to the capture record when adding a surface.
|
|
39
|
-
- Remove a surface's case in the change that removes the surface.
|
|
25
|
+
- Capture the full page, never a component in isolation.
|
|
26
|
+
- Capture a layout case at 320, the reflow floor in `a11y`, plus each breakpoint in the `## Layout` section of `DESIGN.md`, or at 320 alone when there is none.
|
|
27
|
+
- Capture resizable chrome at its narrowest and widest clamp, at the narrowest width keeping it persistent. Judge the content width beside it.
|
|
28
|
+
- Capture a state case at one width rather than the full set.
|
|
29
|
+
- Drive a state an initial render cannot reach, such as an open menu or a rail tracking scroll.
|
|
30
|
+
- Add a surface's case with the surface, and remove it with the surface.
|
|
40
31
|
|
|
41
32
|
## Sharing a capture
|
|
42
33
|
|
|
43
|
-
-
|
|
44
|
-
- Commit a flagged case's evidence output
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
- Do not commit the state as evidence when the masked outputs still differ, which happens when the value's width reflows its neighbors. Name the variance in the pull request instead, and never re-run toward a match.
|
|
49
|
-
- Recommitting an unbounded sweep on every run reaches a gigabyte of repository history inside a hundred merges. A small, committed evidence set is what a reviewer needs and what lets GitHub draw its own before-and-after comparison on the pull request.
|
|
50
|
-
- `git-pr` and `git-followup` maintain a comparison comment on the pull request automatically once a case is committed, so a reviewer never has to open Files Changed to see it.
|
|
34
|
+
- Write a capture into a folder named `evidence`, which the pull request comment collects from.
|
|
35
|
+
- Commit a flagged case's evidence output and never the sweep, unless the project already commits the same comparison elsewhere.
|
|
36
|
+
- Commit a new evidence case only after two captures with no code change between them match byte for byte.
|
|
37
|
+
- Mask the smallest element holding a value that moves between runs, such as a clock, before the double capture.
|
|
38
|
+
- Name the variance in the pull request when masked outputs still differ, never re-running toward a match.
|
|
@@ -10,56 +10,33 @@ paths:
|
|
|
10
10
|
|
|
11
11
|
# UX completeness standards
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Feedback
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- Show
|
|
15
|
+
- Confirm every user-initiated mutation, such as a save, delete, or import, inline on success, and show an inline error when it fails.
|
|
16
|
+
- Auto-dismiss a success message only when its outcome stays visible elsewhere. Keep a message carrying the only copy of its information until dismissed (WCAG 2.2.1).
|
|
17
|
+
- Show no optimistic status, such as a green dot or "connected", while unsaved edits are pending.
|
|
18
18
|
|
|
19
19
|
## Empty states
|
|
20
20
|
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
- Distinguish between "nothing exists yet" (onboarding) and "nothing matches your filter" (recovery). Use different copy for each.
|
|
24
|
-
- Filter-driven empty states must include a way to clear the filter (link or button).
|
|
21
|
+
- Give every list, table, or collection an explicit empty state carrying a next action, such as a button or link.
|
|
22
|
+
- Write different copy for "nothing exists yet" and "nothing matches your filter", and give the filtered state a way to clear the filter.
|
|
25
23
|
|
|
26
24
|
## Loading states
|
|
27
25
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
26
|
+
- Show a progress indicator for any operation a person waits on for longer than about one second.
|
|
27
|
+
- Mark previous content as updating while new content loads, rather than showing it unmarked as current.
|
|
30
28
|
|
|
31
29
|
## Destructive actions
|
|
32
30
|
|
|
33
|
-
-
|
|
34
|
-
- Confirmation UI must be dismissible via Escape in addition to a cancel button.
|
|
31
|
+
- Confirm a destructive action, such as a delete, disconnect, or discard, before it runs, unless it can be undone afterward.
|
|
35
32
|
|
|
36
|
-
##
|
|
33
|
+
## Element states
|
|
37
34
|
|
|
38
|
-
-
|
|
39
|
-
-
|
|
35
|
+
- Give every interactive element distinct hover, active, focus, and disabled styles.
|
|
36
|
+
- Never flash an intermediate state, such as a light frame before a dark theme or a layout jump before data arrives. Reserve the space or settle it first.
|
|
40
37
|
|
|
41
|
-
##
|
|
38
|
+
## Overflow
|
|
42
39
|
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
|
|
46
|
-
## State transitions
|
|
47
|
-
|
|
48
|
-
- Do not flash intermediate states during transitions (e.g., white flash on theme load, layout jump before data arrives).
|
|
49
|
-
- Use CSS transitions or deferred rendering to prevent visible flicker between states.
|
|
50
|
-
|
|
51
|
-
## Interactive element states
|
|
52
|
-
|
|
53
|
-
- All interactive elements must visually respond to hover, active, focus, and disabled states.
|
|
54
|
-
- Disabled elements must use reduced opacity or muted colors and `cursor: not-allowed`.
|
|
55
|
-
- Do not rely on a single state (e.g., focus only) to communicate interactivity.
|
|
56
|
-
|
|
57
|
-
## Overflow and truncation
|
|
58
|
-
|
|
59
|
-
- Long text in constrained containers must truncate with ellipsis, not overflow or wrap unexpectedly.
|
|
60
|
-
- Truncated text must be accessible via `title` attribute or expandable interaction.
|
|
61
|
-
|
|
62
|
-
## Scroll and clipping
|
|
63
|
-
|
|
64
|
-
- Focus rings and interactive borders must not be clipped by parent `overflow: hidden` or `overflow: auto` containers.
|
|
65
|
-
- Add padding or adjust overflow to prevent clipping on edge items in scrollable lists.
|
|
40
|
+
- Truncate long text in a constrained container with an ellipsis rather than letting it overflow.
|
|
41
|
+
- Expose truncated text through an expand control or full text on focus, never through a `title` tooltip alone.
|
|
42
|
+
- Keep focus rings and interactive borders clear of a parent's `overflow` clipping, padding the edge items of a scrollable list.
|
package/package.json
CHANGED
|
@@ -100,7 +100,7 @@ log_step "Assert scaffold"
|
|
|
100
100
|
# Authoring references name no path because they ship inside the plugin's
|
|
101
101
|
# skills rather than installing into a target. The two
|
|
102
102
|
# rule paths stand for the `claude` and `canon` groups `base` takes whole.
|
|
103
|
-
for path in "CLAUDE.md" ".claude
|
|
103
|
+
for path in "CLAUDE.md" ".claude" "canon/context/index.md" \
|
|
104
104
|
".oxfmtrc.json" ".lintstagedrc" ".husky/pre-commit" ".github/workflows/verify.yml" "scripts/verify.ts" \
|
|
105
105
|
".claude/rules/canon/claude/output.md" ".claude/rules/canon/canon/scratch.md"; do
|
|
106
106
|
if [ ! -e "$TARGET_DIR/$path" ]; then
|
|
@@ -146,6 +146,18 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
|
|
|
146
146
|
'Are our requirements and architecture docs still true, and when were they last reviewed?',
|
|
147
147
|
expect: 'document-health',
|
|
148
148
|
},
|
|
149
|
+
// A repair rather than a report, and folder by folder until each gates,
|
|
150
|
+
// which is what separates it from `document-health` above.
|
|
151
|
+
{
|
|
152
|
+
prompt:
|
|
153
|
+
'Our docs folder fails the paragraph and bullet limits everywhere, so rewrite it until it passes and lock it in.',
|
|
154
|
+
expect: 'document-repair',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
prompt:
|
|
158
|
+
'Bring the context entries under the density limits one folder at a time and gate each once it is clean.',
|
|
159
|
+
expect: 'document-repair',
|
|
160
|
+
},
|
|
149
161
|
{
|
|
150
162
|
prompt: 'Open a new entry on the task board for this piece of work.',
|
|
151
163
|
expect: 'task-board',
|
package/src/cli/help.ts
CHANGED
|
@@ -29,7 +29,6 @@ export const COMMAND_GROUPS: { title: string; rows: CommandRow[] }[] = [
|
|
|
29
29
|
['gov [cmd]', 'Governance rules'],
|
|
30
30
|
['tooling [cmd]', 'Tooling stacks'],
|
|
31
31
|
['claude [cmd]', 'Claude workflow'],
|
|
32
|
-
['wiki [cmd]', 'Wiki pages'],
|
|
33
32
|
['indexes [cmd]', 'Regenerate index.md files'],
|
|
34
33
|
['docs [cmd|topic]', 'Emit toolkit reference docs'],
|
|
35
34
|
],
|
package/src/cli.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { register as sync } from '@/commands/sync'
|
|
|
7
7
|
import { register as gov } from '@/commands/gov'
|
|
8
8
|
import { register as tooling } from '@/commands/tooling'
|
|
9
9
|
import { register as claude } from '@/commands/claude'
|
|
10
|
-
import { register as wiki } from '@/commands/wiki'
|
|
11
10
|
import { register as indexes } from '@/commands/indexes'
|
|
12
11
|
import { register as docs } from '@/commands/docs'
|
|
13
12
|
import { register as design } from '@/commands/design'
|
|
@@ -68,7 +67,6 @@ sync(program)
|
|
|
68
67
|
gov(program)
|
|
69
68
|
tooling(program)
|
|
70
69
|
claude(program)
|
|
71
|
-
wiki(program)
|
|
72
70
|
indexes(program)
|
|
73
71
|
docs(program)
|
|
74
72
|
design(program)
|
package/src/commands/context.ts
CHANGED
|
@@ -56,6 +56,11 @@ import {
|
|
|
56
56
|
presentNames,
|
|
57
57
|
resolveFolders,
|
|
58
58
|
} from '@/context/folders'
|
|
59
|
+
import {
|
|
60
|
+
type CoverageReport,
|
|
61
|
+
measureCoverage,
|
|
62
|
+
readRoots,
|
|
63
|
+
} from '@/context/coverage'
|
|
59
64
|
import { isGating } from '@/context/gate'
|
|
60
65
|
import { isShapeGated, SHAPE_GATED_FOLDERS } from '@/context/shape-gated'
|
|
61
66
|
import { currentWorktreeRoot } from '@/git/worktree'
|
|
@@ -101,7 +106,7 @@ export function register(program: Command): void {
|
|
|
101
106
|
context
|
|
102
107
|
.command('audit')
|
|
103
108
|
.description(
|
|
104
|
-
'Report required sections, entry length, citations, reference form, catalog tables, provenance, index drift, and the requirements record against its own word cap',
|
|
109
|
+
'Report required sections, entry length, citations, reference form, catalog tables, provenance, index drift, code folders no entry names, and the requirements record against its own word cap',
|
|
105
110
|
)
|
|
106
111
|
.argument('[path]', 'Project root, defaulting to the current directory')
|
|
107
112
|
.helpOption('-h, --help', 'Show this help message')
|
|
@@ -113,7 +118,7 @@ export function register(program: Command): void {
|
|
|
113
118
|
.option('--citations-only', 'Run the gating citation check alone')
|
|
114
119
|
.option(
|
|
115
120
|
'--gate',
|
|
116
|
-
'Also fail on a missing required section, an H2 outside the set, a shape break in a gated folder,
|
|
121
|
+
'Also fail on a missing required section, an H2 outside the set, a shape break in a gated folder, index drift, or a code folder no entry names, the findings that are facts',
|
|
117
122
|
)
|
|
118
123
|
.addHelpText(
|
|
119
124
|
'after',
|
|
@@ -129,7 +134,9 @@ export function register(program: Command): void {
|
|
|
129
134
|
'--citations-only, which never measures it. A record stating no',
|
|
130
135
|
'cap is reported and never gated. --gate widens the gate to the',
|
|
131
136
|
'findings that are facts rather than judgments: a missing',
|
|
132
|
-
'required section, an H2 outside the closed set,
|
|
137
|
+
'required section, an H2 outside the closed set, index drift, and',
|
|
138
|
+
'a code folder no Layout names or two entries claim. The roots come',
|
|
139
|
+
'from canon/config/context.toml and default to src when present.',
|
|
133
140
|
'A context entry requires Overview and Layout and allows Gotchas.',
|
|
134
141
|
'A shape break, such as a Decisions section or a sixth gotcha, is',
|
|
135
142
|
'reported everywhere and gated only under a shape-gated folder.',
|
|
@@ -667,6 +674,9 @@ async function runAudit(
|
|
|
667
674
|
isShapeGated(relative(top, join(root, rel))),
|
|
668
675
|
)
|
|
669
676
|
const length = gateOnly ? undefined : lengthFindings(entries)
|
|
677
|
+
const coverage: CoverageReport | undefined = gateOnly
|
|
678
|
+
? undefined
|
|
679
|
+
: measureCoverage(root, readRoots(root), folders)
|
|
670
680
|
// Absent under `--citations-only` and null when the project carries no
|
|
671
681
|
// record. A run that never looked and a project with nothing to look at are
|
|
672
682
|
// different answers, and one value for both reports the second as the first.
|
|
@@ -685,6 +695,7 @@ async function runAudit(
|
|
|
685
695
|
reportTables(entries)
|
|
686
696
|
reportProvenance(entries, folders)
|
|
687
697
|
reportDrift(drift)
|
|
698
|
+
reportCoverage(coverage ?? { kind: 'no-root' })
|
|
688
699
|
reportRequirements(requirements, root)
|
|
689
700
|
outro()
|
|
690
701
|
}
|
|
@@ -721,6 +732,8 @@ async function runAudit(
|
|
|
721
732
|
extraSections: extras,
|
|
722
733
|
shapeFindings: shape,
|
|
723
734
|
indexDrift: drift,
|
|
735
|
+
// Absent under `--citations-only`, which never measures it.
|
|
736
|
+
coverage,
|
|
724
737
|
// Null says the run opened the project and found no record, which a
|
|
725
738
|
// target that never wrote one is entitled to. Absent says the run
|
|
726
739
|
// never looked, which is `--citations-only`.
|
|
@@ -753,6 +766,7 @@ async function runAudit(
|
|
|
753
766
|
extraSections: extras,
|
|
754
767
|
shape,
|
|
755
768
|
drift,
|
|
769
|
+
coverage: coverage ?? { kind: 'no-root' },
|
|
756
770
|
widened,
|
|
757
771
|
})
|
|
758
772
|
|
|
@@ -1210,6 +1224,46 @@ function reportProvenance(
|
|
|
1210
1224
|
)
|
|
1211
1225
|
}
|
|
1212
1226
|
|
|
1227
|
+
function reportCoverage(report: CoverageReport): void {
|
|
1228
|
+
logStep('Coverage')
|
|
1229
|
+
|
|
1230
|
+
if (report.kind === 'no-root') {
|
|
1231
|
+
logInfo(
|
|
1232
|
+
'No code root found, since no canon/config/context.toml declares one and there is no src/, so nothing was measured.',
|
|
1233
|
+
)
|
|
1234
|
+
return
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
if (report.kind === 'invalid') {
|
|
1238
|
+
logWarn(report.reason)
|
|
1239
|
+
return
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
if (report.kind === 'opted-out') {
|
|
1243
|
+
logInfo(
|
|
1244
|
+
'canon/config/context.toml declares roots = [], so coverage was not measured.',
|
|
1245
|
+
)
|
|
1246
|
+
return
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
const { unnamed, doubled, missingRoots } = report.findings
|
|
1250
|
+
const lines = [
|
|
1251
|
+
...missingRoots.map((root) => `${root} matches no folder`),
|
|
1252
|
+
...unnamed.map((folder) => `${folder} named in no Layout`),
|
|
1253
|
+
...doubled.map(
|
|
1254
|
+
({ folder, rels }) => `${folder} Layout H3 in ${rels.join(', ')}`,
|
|
1255
|
+
),
|
|
1256
|
+
]
|
|
1257
|
+
|
|
1258
|
+
if (lines.length === 0) {
|
|
1259
|
+
logInfo(`Every folder under ${report.roots.join(', ')} is named once.`)
|
|
1260
|
+
return
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
logWarn(plural(lines.length, 'coverage finding'))
|
|
1264
|
+
pipeOutput(lines.join('\n'))
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1213
1267
|
/** Reports the requirements record against the word cap it states, if any. */
|
|
1214
1268
|
function reportRequirements(
|
|
1215
1269
|
report: RequirementsReport | undefined,
|
package/src/commands/gov.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
hasFinding,
|
|
38
38
|
PRACTICE_FOLDERS,
|
|
39
39
|
readRuleAudit,
|
|
40
|
+
RULE_GATE_EXEMPT,
|
|
40
41
|
RULE_GATED_FOLDERS,
|
|
41
42
|
RULE_LIMITS,
|
|
42
43
|
type RuleAuditReport,
|
|
@@ -631,6 +632,7 @@ export function register(program: Command): void {
|
|
|
631
632
|
' inside it.',
|
|
632
633
|
'',
|
|
633
634
|
`Gated folders: ${RULE_GATED_FOLDERS.length === 0 ? 'none yet' : RULE_GATED_FOLDERS.join(', ')}`,
|
|
635
|
+
`Exempt from the gate: ${RULE_GATE_EXEMPT.length === 0 ? 'none' : RULE_GATE_EXEMPT.join(', ')}`,
|
|
634
636
|
'',
|
|
635
637
|
'Exit codes:',
|
|
636
638
|
' 0 no finding, or under --gate no finding in a gated folder',
|
|
@@ -691,6 +693,7 @@ async function runAudit(
|
|
|
691
693
|
root,
|
|
692
694
|
limits: RULE_LIMITS,
|
|
693
695
|
gated: RULE_GATED_FOLDERS,
|
|
696
|
+
exempt: RULE_GATE_EXEMPT,
|
|
694
697
|
...report,
|
|
695
698
|
})}\n`,
|
|
696
699
|
)
|
|
@@ -699,6 +702,7 @@ async function runAudit(
|
|
|
699
702
|
return auditExit(report, {
|
|
700
703
|
gate: opts.gate ?? false,
|
|
701
704
|
gated: RULE_GATED_FOLDERS,
|
|
705
|
+
exempt: RULE_GATE_EXEMPT,
|
|
702
706
|
})
|
|
703
707
|
}
|
|
704
708
|
|
package/src/commands/init.ts
CHANGED
|
@@ -39,8 +39,8 @@ export function register(program: Command): void {
|
|
|
39
39
|
'',
|
|
40
40
|
'Runs `canon tooling sync base --write` first, which replaces every',
|
|
41
41
|
'base golden config the target holds, then `canon claude init`,',
|
|
42
|
-
'`canon gov install
|
|
43
|
-
'
|
|
42
|
+
'and `canon gov install`. Refuses a target that is not a directory or is',
|
|
43
|
+
'the toolkit root.',
|
|
44
44
|
'',
|
|
45
45
|
'Exit codes:',
|
|
46
46
|
' 0 every domain installed, or the prompt was cancelled',
|
package/src/commands/labels.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { execa } from 'execa'
|
|
|
5
5
|
import { gitEnv } from '@/git/env'
|
|
6
6
|
import { diffLabels } from '@/labels/apply'
|
|
7
7
|
import { type LabelAuditRefusal, auditLabels } from '@/labels/audit'
|
|
8
|
+
import { checkClosing } from '@/labels/closing'
|
|
8
9
|
import { resolveScanInput } from '@/labels/event'
|
|
9
10
|
import { checkTitleFormat, type TitleFormatIssue } from '@/labels/format'
|
|
10
11
|
import { MAP_REL } from '@/labels/map'
|
|
@@ -34,6 +35,7 @@ interface ScanOptions {
|
|
|
34
35
|
readonly body?: string
|
|
35
36
|
readonly bodyFile?: string
|
|
36
37
|
readonly head?: string
|
|
38
|
+
readonly closes?: string
|
|
37
39
|
readonly json?: boolean
|
|
38
40
|
}
|
|
39
41
|
|
|
@@ -196,6 +198,10 @@ export function register(program: Command): void {
|
|
|
196
198
|
'Body to scan, read from a file, overriding the event payload',
|
|
197
199
|
)
|
|
198
200
|
.option('--head <ref>', 'Head branch, overriding the event payload')
|
|
201
|
+
.option(
|
|
202
|
+
'--closes <numbers>',
|
|
203
|
+
'Issues the body must close, a comma list or `none`, graded when given',
|
|
204
|
+
)
|
|
199
205
|
.option('--json', 'Add a machine-readable record on stdout')
|
|
200
206
|
.addHelpText(
|
|
201
207
|
'after',
|
|
@@ -239,12 +245,20 @@ export function register(program: Command): void {
|
|
|
239
245
|
'character, an em dash or a semicolon, in the title or body of a pull',
|
|
240
246
|
'request or a review, outside a code span or a link.',
|
|
241
247
|
'',
|
|
248
|
+
'With --closes, it also grades the closing lines. Each issue named needs',
|
|
249
|
+
'its own `Closes #NNN` keyword in the body, and a closing keyword for an',
|
|
250
|
+
'issue the list leaves out fails. `none` expects no closing line.',
|
|
251
|
+
'Without the flag the check does not run, so the pull request job and a',
|
|
252
|
+
'followup keep their behavior. A keyword in a code span or a fence is',
|
|
253
|
+
'not read, as GitHub does not read it.',
|
|
254
|
+
'',
|
|
242
255
|
'Exit codes:',
|
|
243
|
-
' 0 none of the
|
|
256
|
+
' 0 none of the eight found',
|
|
244
257
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
245
258
|
' 2 the title or body carries a phase label, a board identifier, a',
|
|
246
259
|
' session link, a banned character, a heading outside the closed',
|
|
247
|
-
' list or a required one missing, or
|
|
260
|
+
' list or a required one missing, a missing or stray closing line',
|
|
261
|
+
' under --closes, or a title',
|
|
248
262
|
" breaking claude/skills/git-pr/references/pr.md's format, casing, or length",
|
|
249
263
|
'',
|
|
250
264
|
'Examples:',
|
|
@@ -536,11 +550,42 @@ async function runApply(number: string, opts: ApplyOptions): Promise<number> {
|
|
|
536
550
|
return owed.length === 0 ? 0 : 2
|
|
537
551
|
}
|
|
538
552
|
|
|
553
|
+
/**
|
|
554
|
+
* Reads `--closes` as the issues the body must close. Undefined when the flag
|
|
555
|
+
* is absent, which skips the check, and a message when the value is neither
|
|
556
|
+
* `none` nor a comma list of numbers, since a guessed list would grade the
|
|
557
|
+
* body against the wrong issues.
|
|
558
|
+
*/
|
|
559
|
+
function parseCloses(value: string | undefined): number[] | string | undefined {
|
|
560
|
+
if (value === undefined) return undefined
|
|
561
|
+
if (value === 'none') return []
|
|
562
|
+
if (!/^\d+(,\d+)*$/.test(value)) {
|
|
563
|
+
return `--closes takes a comma list of issue numbers or none, not ${value}`
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return [...new Set(value.split(',').map(Number))]
|
|
567
|
+
}
|
|
568
|
+
|
|
539
569
|
async function runScan(opts: ScanOptions): Promise<number> {
|
|
540
570
|
const emitJson = opts.json ?? false
|
|
541
571
|
|
|
542
572
|
intro('canon labels scan')
|
|
543
573
|
|
|
574
|
+
const expected = parseCloses(opts.closes)
|
|
575
|
+
|
|
576
|
+
if (typeof expected === 'string') {
|
|
577
|
+
logStep('Refused')
|
|
578
|
+
logWarn(expected)
|
|
579
|
+
outro()
|
|
580
|
+
|
|
581
|
+
if (emitJson) {
|
|
582
|
+
process.stdout.write(
|
|
583
|
+
`${JSON.stringify({ reason: 'bad-input', message: expected })}\n`,
|
|
584
|
+
)
|
|
585
|
+
}
|
|
586
|
+
return 1
|
|
587
|
+
}
|
|
588
|
+
|
|
544
589
|
const resolved = resolveScanInput(opts)
|
|
545
590
|
|
|
546
591
|
if (resolved.kind === 'refused') {
|
|
@@ -693,6 +738,37 @@ async function runScan(opts: ScanOptions): Promise<number> {
|
|
|
693
738
|
for (const issue of titleFormatIssues) logWarn(TITLE_FORMAT_MESSAGES[issue])
|
|
694
739
|
}
|
|
695
740
|
|
|
741
|
+
const closing =
|
|
742
|
+
expected === undefined
|
|
743
|
+
? { missing: [], stray: [] }
|
|
744
|
+
: checkClosing(resolved.body, expected)
|
|
745
|
+
|
|
746
|
+
logStep(
|
|
747
|
+
expected === undefined
|
|
748
|
+
? 'Closing lines not checked'
|
|
749
|
+
: closing.missing.length === 0 && closing.stray.length === 0
|
|
750
|
+
? 'Clean'
|
|
751
|
+
: 'Closing line issue found',
|
|
752
|
+
)
|
|
753
|
+
if (expected === undefined) {
|
|
754
|
+
logInfo('no --closes list, so the closing lines are not graded')
|
|
755
|
+
} else if (closing.missing.length === 0 && closing.stray.length === 0) {
|
|
756
|
+
logInfo('the closing lines match the issues the task closes')
|
|
757
|
+
} else {
|
|
758
|
+
if (closing.missing.length > 0) {
|
|
759
|
+
logWarn(
|
|
760
|
+
`${plural(closing.missing.length, 'issue')} with no closing line. Add one \`Closes #NNN\` per issue.`,
|
|
761
|
+
)
|
|
762
|
+
for (const number of closing.missing) logWarn(`#${number}`)
|
|
763
|
+
}
|
|
764
|
+
if (closing.stray.length > 0) {
|
|
765
|
+
logWarn(
|
|
766
|
+
`${plural(closing.stray.length, 'closing line')} for an issue this pull request does not close. Delete each.`,
|
|
767
|
+
)
|
|
768
|
+
for (const number of closing.stray) logWarn(`#${number}`)
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
696
772
|
outro()
|
|
697
773
|
|
|
698
774
|
if (emitJson) {
|
|
@@ -706,6 +782,8 @@ async function runScan(opts: ScanOptions): Promise<number> {
|
|
|
706
782
|
titleFormatIssues,
|
|
707
783
|
sections: { checked: sectionsChecked, ...sections },
|
|
708
784
|
bannedCharacters,
|
|
785
|
+
missingClosures: closing.missing,
|
|
786
|
+
strayClosures: closing.stray,
|
|
709
787
|
})}\n`,
|
|
710
788
|
)
|
|
711
789
|
}
|
|
@@ -716,7 +794,9 @@ async function runScan(opts: ScanOptions): Promise<number> {
|
|
|
716
794
|
titleFormatIssues.length === 0 &&
|
|
717
795
|
sections.outside.length === 0 &&
|
|
718
796
|
sections.missing.length === 0 &&
|
|
719
|
-
banFindings.length === 0
|
|
797
|
+
banFindings.length === 0 &&
|
|
798
|
+
closing.missing.length === 0 &&
|
|
799
|
+
closing.stray.length === 0
|
|
720
800
|
? 0
|
|
721
801
|
: 2
|
|
722
802
|
}
|