@erclx/canon 5.43.0 → 5.45.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/backlog-triage/references/apply.md +6 -6
- package/claude/skills/canon-cli/SKILL.md +4 -5
- package/claude/skills/canvas-board/REQUIREMENT.md +1 -1
- package/claude/skills/canvas-board/SKILL.md +1 -1
- package/claude/skills/context-fold/references/context.md +1 -1
- package/claude/skills/create-skill/REQUIREMENT.md +1 -1
- package/claude/skills/create-skill/SKILL.md +2 -2
- package/claude/skills/create-skill/references/skill-requirement.md +68 -9
- package/claude/skills/create-skill/references/skill.md +1 -1
- package/claude/skills/design-identity/REQUIREMENT.md +40 -0
- package/claude/skills/design-identity/SKILL.md +138 -0
- package/claude/skills/{draft-identity → design-identity}/references/card-route.md +2 -2
- package/claude/skills/design-taste/REQUIREMENT.md +6 -6
- package/claude/skills/design-taste/SKILL.md +2 -2
- package/claude/skills/design-taste/references/kinds.md +1 -1
- package/claude/skills/design-taste/references/preflight.md +1 -1
- package/claude/skills/draft-and-pick/REQUIREMENT.md +8 -69
- package/claude/skills/draft-and-pick/SKILL.md +3 -153
- package/claude/skills/draft-doc/REQUIREMENT.md +9 -30
- package/claude/skills/draft-doc/SKILL.md +4 -76
- package/claude/skills/draft-figure/REQUIREMENT.md +8 -42
- package/claude/skills/draft-figure/SKILL.md +3 -77
- package/claude/skills/draft-identity/REQUIREMENT.md +8 -23
- package/claude/skills/draft-identity/SKILL.md +3 -130
- package/claude/skills/draft-ready/REQUIREMENT.md +8 -37
- package/claude/skills/draft-ready/SKILL.md +3 -128
- package/claude/skills/draft-screencast/REQUIREMENT.md +8 -23
- package/claude/skills/draft-screencast/SKILL.md +3 -62
- package/claude/skills/draft-slides/REQUIREMENT.md +8 -27
- package/claude/skills/draft-slides/SKILL.md +3 -72
- package/claude/skills/draw-figure/REQUIREMENT.md +59 -0
- package/claude/skills/draw-figure/SKILL.md +85 -0
- package/claude/skills/{draft-figure → draw-figure}/references/architecture-set.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/hand-drawn.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/mermaid-path.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/mermaid.md +1 -1
- package/claude/skills/make-deck/REQUIREMENT.md +44 -0
- package/claude/skills/make-deck/SKILL.md +80 -0
- package/claude/skills/markdown-craft/REQUIREMENT.md +1 -1
- package/claude/skills/markdown-craft/SKILL.md +2 -2
- package/claude/skills/pick-by-eye/REQUIREMENT.md +36 -0
- package/claude/skills/pick-by-eye/SKILL.md +161 -0
- package/claude/skills/plan-feature/SKILL.md +2 -1
- package/claude/skills/plan-feature/references/plan.md +24 -10
- package/claude/skills/plan-groundwork/SKILL.md +1 -1
- package/claude/skills/plan-intake/SKILL.md +1 -1
- package/claude/skills/plan-ready/REQUIREMENT.md +54 -0
- package/claude/skills/plan-ready/SKILL.md +136 -0
- package/claude/skills/{draft-ready → plan-ready}/references/assembly.md +1 -1
- package/claude/skills/{draft-ready → plan-ready}/references/ready.md +1 -1
- package/claude/skills/record-screencast/REQUIREMENT.md +3 -3
- package/claude/skills/record-screencast/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/REQUIREMENT.md +16 -99
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +5 -2
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +18 -13
- package/claude/skills/role-planner/REQUIREMENT.md +15 -73
- package/claude/skills/role-planner/SKILL.md +2 -1
- package/claude/skills/role-worker/REQUIREMENT.md +13 -41
- package/claude/skills/role-worker/SKILL.md +9 -0
- package/claude/skills/script-screencast/REQUIREMENT.md +40 -0
- package/claude/skills/script-screencast/SKILL.md +70 -0
- package/claude/skills/{draft-screencast → script-screencast}/references/beats-template.md +2 -2
- package/claude/skills/search-craft/REQUIREMENT.md +4 -4
- package/claude/skills/search-craft/SKILL.md +1 -1
- package/claude/skills/target-setup/SKILL.md +3 -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/SKILL.md +11 -9
- package/claude/skills/task-board/references/board.md +15 -12
- package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
- package/claude/skills/teach-workspace/SKILL.md +1 -1
- package/claude/skills/teach-workspace/references/lesson-craft.md +2 -2
- package/claude/skills/teach-workspace/references/promotion.md +5 -5
- package/claude/skills/ui-checklist/SKILL.md +2 -0
- package/claude/skills/write-doc/REQUIREMENT.md +46 -0
- package/claude/skills/write-doc/SKILL.md +83 -0
- package/claude/skills/{draft-doc → write-doc}/references/context.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/docs.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/readme.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/wiki-kind.md +3 -3
- package/docs/agents/install-and-sync.md +2 -2
- package/docs/agents/markdown-audit.md +4 -2
- package/docs/agents/migrate.md +2 -2
- package/docs/agents/output-shape.md +1 -2
- package/docs/target/migrations.md +35 -4
- package/docs/target/projects.md +2 -3
- package/docs/target/scenarios.md +4 -1
- package/docs/workflow/parallel-features.md +3 -2
- package/docs/workflow/skill-map-on-request.md +19 -13
- package/docs/workflow/skill-map.md +13 -12
- package/docs/workflow/visual-design-workflow.md +2 -2
- package/governance/detect.toml +4 -0
- package/governance/ledger/framework/astro.md +73 -0
- package/governance/ledger/framework/fastapi.md +76 -0
- package/governance/ledger/framework/nextjs.md +86 -0
- package/governance/ledger/framework/react.md +83 -0
- package/governance/ledger/framework/shadcn.md +60 -0
- package/governance/ledger/framework/tailwind.md +55 -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/rules/framework/astro.md +12 -14
- package/governance/rules/framework/fastapi.md +20 -24
- package/governance/rules/framework/nextjs.md +19 -27
- package/governance/rules/framework/react.md +22 -33
- package/governance/rules/framework/shadcn.md +11 -15
- package/governance/rules/framework/tailwind.md +12 -14
- 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/rules/writing/markdown.md +1 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +1 -1
- package/src/claude/cases/authoring.ts +15 -3
- package/src/claude/cases/misc.ts +12 -0
- package/src/claude/cases/workflow.ts +19 -1
- package/src/claude/skills-audit.ts +113 -1
- package/src/cli/help.ts +0 -1
- package/src/cli.ts +0 -2
- package/src/commands/claude.ts +34 -11
- package/src/commands/demo.ts +1 -1
- package/src/commands/gov.ts +4 -0
- package/src/commands/init.ts +2 -2
- package/src/commands/markdown.ts +1 -1
- package/src/commands/migrate.ts +4 -0
- package/src/commands/pr.ts +30 -6
- package/src/commands/tasks.ts +383 -2
- package/src/commands/tooling.ts +18 -5
- package/src/context/audit.ts +1 -1
- package/src/demo/beats.ts +1 -1
- package/src/design/components.ts +1 -1
- package/src/gov/audit.ts +25 -4
- package/src/init/plan.ts +1 -8
- package/src/init/steps.ts +0 -8
- package/src/inventory/subjects.ts +1 -1
- package/src/markdown/scope.ts +49 -35
- package/src/migrate/record-layout.ts +1 -1
- package/src/migrate/scratch-evidence.ts +12 -4
- package/src/migrate/skill-names.ts +68 -21
- package/src/pr/frames.ts +14 -3
- package/src/records/backup.ts +3 -3
- package/src/records/validate.ts +31 -0
- package/src/roots/record.ts +17 -8
- package/src/tasks/board.ts +854 -0
- package/src/tasks/record.ts +20 -1
- package/src/tasks/validate.ts +206 -9
- 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/tooling/astro/configs/card-src/pages/og-card.astro +1 -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 +1 -1
- 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 +65 -0
- 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/settings.json +4 -0
- 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/src/commands/wiki.ts +0 -100
- package/src/wiki/init.ts +0 -85
- /package/claude/skills/{draft-figure → draw-figure}/references/figures.md +0 -0
- /package/claude/skills/{draft-slides → make-deck}/references/deck.md +0 -0
- /package/claude/skills/{draft-and-pick → pick-by-eye}/references/live-arms.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/agent-facing.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/application.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/cli.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/library.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/plugin.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/requirements.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/wiki.md +0 -0
|
@@ -11,21 +11,18 @@ paths:
|
|
|
11
11
|
|
|
12
12
|
## Punctuation
|
|
13
13
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
14
|
+
- Write no em dash (`—`) or semicolon in rendered copy. Use a comma or a period, or restructure the sentence.
|
|
15
|
+
- Add no trailing punctuation to a button label or a short action string.
|
|
16
16
|
|
|
17
17
|
## Button copy
|
|
18
18
|
|
|
19
|
-
-
|
|
20
|
-
- Avoid padded phrasing: `Save` over `Save changes`, `Delete` over `Click to delete`.
|
|
19
|
+
- Label a button with the verb of the action it performs, such as `Save` or `Delete`, never with filler such as `Click to delete`.
|
|
21
20
|
|
|
22
21
|
## Error messages
|
|
23
22
|
|
|
24
|
-
-
|
|
25
|
-
- Keep
|
|
26
|
-
- One sentence maximum.
|
|
23
|
+
- Tell the person how to fix the error, such as `Enter an email address` over `Email is invalid`.
|
|
24
|
+
- Keep an error message to one short sentence in plain language, with no technical jargon or error codes.
|
|
27
25
|
|
|
28
26
|
## Placeholder text
|
|
29
27
|
|
|
30
|
-
-
|
|
31
|
-
- Reserve instructions for labels or helper text below the input.
|
|
28
|
+
- Never use placeholder text as a label, a hint, or an example. Put a format example such as `name@example.com` in hint text under the label.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Form validation timing,
|
|
2
|
+
description: Form validation timing, submission, error placement, and derived state for rendered forms
|
|
3
3
|
paths:
|
|
4
4
|
- '**/*.tsx'
|
|
5
5
|
- '**/*.jsx'
|
|
@@ -11,35 +11,29 @@ paths:
|
|
|
11
11
|
|
|
12
12
|
## Validation timing
|
|
13
13
|
|
|
14
|
-
- Validate
|
|
15
|
-
-
|
|
16
|
-
- Apply the same validation timing to all fields in a form. Do not mix blur-only and change-only validation within one form.
|
|
17
|
-
- Do not show an error on a field the user has never touched.
|
|
14
|
+
- Validate when the person submits or continues, never when focus leaves a field. Realtime feedback the feature exists for, such as a character count, is the exception.
|
|
15
|
+
- Re-validate a field on every change once it shows an error, so the error clears as soon as the input is valid.
|
|
18
16
|
|
|
19
|
-
##
|
|
17
|
+
## Submission
|
|
20
18
|
|
|
21
|
-
-
|
|
22
|
-
- Treat whitespace-only values as empty for required fields.
|
|
19
|
+
- Refuse a submit while a required field is empty, whitespace-only, or invalid, and show each error. Keep the submit button enabled rather than disabling it.
|
|
23
20
|
|
|
24
21
|
## Error placement
|
|
25
22
|
|
|
26
|
-
- Show
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
## Submission errors
|
|
30
|
-
|
|
31
|
-
- When a failed submission carries several errors, move focus to a summary that links to each failing field. Keep the inline error beside each field.
|
|
23
|
+
- Show each error next to the field that caused it, never under an unrelated field.
|
|
24
|
+
- Show a cross-field conflict under the field the person edited, not under the field it conflicts with.
|
|
25
|
+
- Move focus to an error summary linking to each failing field when a submission fails, and keep the inline error beside each field.
|
|
32
26
|
|
|
33
27
|
## Authentication and re-entry
|
|
34
28
|
|
|
35
|
-
- Let a password manager fill sign-in fields and accept a pasted value.
|
|
36
|
-
-
|
|
29
|
+
- Let a password manager fill sign-in fields and accept a pasted value. Never block paste or autofill on a password or code field (WCAG 3.3.8).
|
|
30
|
+
- Never ask again for information the person gave earlier in the same flow. Prefill it or offer it for selection (WCAG 3.3.7).
|
|
37
31
|
|
|
38
32
|
## Input sanitization
|
|
39
33
|
|
|
40
|
-
- Trim leading and trailing whitespace from text
|
|
41
|
-
-
|
|
34
|
+
- Trim leading and trailing whitespace from a text input on submit, never on every keystroke.
|
|
35
|
+
- Say near a case-sensitive field that it is case-sensitive, rather than silently treating `Foo` and `foo` as different values.
|
|
42
36
|
|
|
43
37
|
## Derived display state
|
|
44
38
|
|
|
45
|
-
- Reset status
|
|
39
|
+
- Reset a status indicator, count, or derived value to neutral when its source input changes.
|
|
@@ -13,19 +13,17 @@ paths:
|
|
|
13
13
|
|
|
14
14
|
## Sentence case
|
|
15
15
|
|
|
16
|
-
- Use sentence case for
|
|
17
|
-
-
|
|
18
|
-
- Treat title case on every heading as a template tell, since it reads as a default rather than a choice.
|
|
16
|
+
- Use sentence case for rendered headings, labels, nav items, tab titles, and dialog titles, since title case on every heading reads as a template default.
|
|
17
|
+
- Keep the original casing of a proper noun or product name, such as `GitHub`, `macOS`, or `TypeScript`.
|
|
19
18
|
|
|
20
19
|
## Uppercase labels
|
|
21
20
|
|
|
22
|
-
- Keep small uppercase labels above headings to
|
|
23
|
-
- Treat the count as a judgment, since a glob cannot check it.
|
|
21
|
+
- Keep small uppercase labels above headings to `ceil(sections / 3)`, the first screen counting as one. Delete the extra labels, since position already names a section.
|
|
24
22
|
|
|
25
23
|
## Where it applies
|
|
26
24
|
|
|
27
|
-
-
|
|
25
|
+
- Apply the same casing to interface copy kept outside the globs, such as a locale file, which this rule never fires on.
|
|
28
26
|
|
|
29
27
|
## Overriding
|
|
30
28
|
|
|
31
|
-
- Depart from sentence case where the case needs it, such as an acronym, a brand mark, or a set-in-caps identity the design chose on purpose.
|
|
29
|
+
- Depart from sentence case only where the case needs it, such as an acronym, a brand mark, or a set-in-caps identity the design chose on purpose.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Focus styles, keyboard operation, focus management, and pointer targets for rendered UI
|
|
3
|
+
paths:
|
|
4
|
+
- '**/*.tsx'
|
|
5
|
+
- '**/*.jsx'
|
|
6
|
+
- '**/*.astro'
|
|
7
|
+
- '**/*.html'
|
|
8
|
+
- '**/*.css'
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Keyboard and focus standards
|
|
12
|
+
|
|
13
|
+
## Focus styles
|
|
14
|
+
|
|
15
|
+
- Style focus through the project's one focus ring rather than a per-component focus style.
|
|
16
|
+
- Never set `outline: none` or `outline-none` without a replacement focus indicator in the same rule.
|
|
17
|
+
- Give the focus ring 3:1 contrast against its background in every theme the project ships.
|
|
18
|
+
|
|
19
|
+
## Keyboard operation
|
|
20
|
+
|
|
21
|
+
- Close a dialog, popover, menu, or inline confirmation on Escape, and cancel an in-progress edit the same way.
|
|
22
|
+
- Bind no single printable character as a shortcut unless a person can turn it off or remap it, or it fires only while its control has focus (WCAG 2.1.4).
|
|
23
|
+
- Give every author-defined drag a single-pointer alternative and a keyboard path, such as move buttons or arrow-key reordering (WCAG 2.5.7).
|
|
24
|
+
|
|
25
|
+
## Composite widgets
|
|
26
|
+
|
|
27
|
+
- Keep one item of a composite widget in the tab sequence, and move between its items with the arrow keys through roving `tabindex` or `aria-activedescendant`.
|
|
28
|
+
- Open a combobox popup on Down Arrow, accept the focused option on Enter, and close the popup on Escape without changing the value.
|
|
29
|
+
|
|
30
|
+
## Focus management
|
|
31
|
+
|
|
32
|
+
- Move focus into a dialog when it opens, and return it to the element that opened it when it closes.
|
|
33
|
+
- Never let a sticky header, footer, or overlay fully cover the focused control. Scroll it clear of them (WCAG 2.4.11).
|
|
34
|
+
|
|
35
|
+
## Pointer targets
|
|
36
|
+
|
|
37
|
+
- Size a web pointer target at least 24 by 24 CSS pixels, or space it so a 24 pixel circle centered on it touches no other target (WCAG 2.5.8).
|
|
38
|
+
- Size a native touch target at the platform figure, 44 points on iOS and 48 dp on Android, and never swap the web and native figures.
|
|
@@ -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.
|
|
@@ -21,4 +21,4 @@ paths:
|
|
|
21
21
|
|
|
22
22
|
## Figures
|
|
23
23
|
|
|
24
|
-
- Load the `canon:
|
|
24
|
+
- Load the `canon:draw-figure` skill before drafting or revising a Mermaid fence or a figure, and report it rather than proceeding silently when it does not resolve.
|
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
|
|
@@ -22,26 +22,38 @@ export const AUTHORING_CASES: readonly SkillCase[] = [
|
|
|
22
22
|
{
|
|
23
23
|
prompt:
|
|
24
24
|
'Write a brand-new docs page for the capture command, nothing under docs/ covers it yet.',
|
|
25
|
+
expect: 'write-doc',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
prompt:
|
|
29
|
+
'I typed draft-doc and it said the doc skill was renamed to write-doc.', // canon-keep-retired
|
|
30
|
+
// The pointer answers under its old name on purpose. canon-keep-retired
|
|
25
31
|
expect: 'draft-doc',
|
|
26
32
|
},
|
|
27
33
|
{
|
|
28
34
|
prompt:
|
|
29
35
|
'I already wrote the finished skill files, package them as a ready folder with a plan and a task for a worker to copy.',
|
|
36
|
+
expect: 'plan-ready',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
prompt:
|
|
40
|
+
'I typed draft-ready and it said the ready skill was renamed to plan-ready.', // canon-keep-retired
|
|
41
|
+
// The pointer answers under its old name on purpose. canon-keep-retired
|
|
30
42
|
expect: 'draft-ready',
|
|
31
43
|
},
|
|
32
44
|
{
|
|
33
45
|
prompt: 'This project has no README.md at all, write one from scratch.',
|
|
34
|
-
expect: '
|
|
46
|
+
expect: 'write-doc',
|
|
35
47
|
},
|
|
36
48
|
{
|
|
37
49
|
prompt:
|
|
38
50
|
'Write a context entry for the payments domain, there is no canon/context page for it yet.',
|
|
39
|
-
expect: '
|
|
51
|
+
expect: 'write-doc',
|
|
40
52
|
},
|
|
41
53
|
{
|
|
42
54
|
prompt:
|
|
43
55
|
'Write a wiki reference page for Claude Code output styles, no page covers that subject yet.',
|
|
44
|
-
expect: '
|
|
56
|
+
expect: 'write-doc',
|
|
45
57
|
},
|
|
46
58
|
{
|
|
47
59
|
prompt: 'This passage reads flat and robotic, give it some real cadence.',
|
package/src/claude/cases/misc.ts
CHANGED
|
@@ -10,6 +10,12 @@ export const MISC_CASES: readonly SkillCase[] = [
|
|
|
10
10
|
{
|
|
11
11
|
prompt:
|
|
12
12
|
'Show me a few different treatments for this callout so I can pick one by looking.',
|
|
13
|
+
expect: 'pick-by-eye',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
prompt:
|
|
17
|
+
'I typed draft-and-pick and it said the pick skill was renamed to pick-by-eye.', // canon-keep-retired
|
|
18
|
+
// The pointer answers under its old name on purpose. canon-keep-retired
|
|
13
19
|
expect: 'draft-and-pick',
|
|
14
20
|
},
|
|
15
21
|
{
|
|
@@ -20,6 +26,12 @@ export const MISC_CASES: readonly SkillCase[] = [
|
|
|
20
26
|
{
|
|
21
27
|
prompt:
|
|
22
28
|
'This project has no logo yet. Draft one and give me a social card to go with it.',
|
|
29
|
+
expect: 'design-identity',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
prompt:
|
|
33
|
+
'I typed draft-identity and it said the identity skill was renamed to design-identity.', // canon-keep-retired
|
|
34
|
+
// The pointer answers under its old name on purpose. canon-keep-retired
|
|
23
35
|
expect: 'draft-identity',
|
|
24
36
|
},
|
|
25
37
|
{
|
|
@@ -22,12 +22,18 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
|
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
prompt: 'Draw me a diagram of how the pieces of this system connect.',
|
|
25
|
+
expect: 'draw-figure',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
prompt:
|
|
29
|
+
'I typed draft-figure and it said the figure skill was renamed to draw-figure.', // canon-keep-retired
|
|
30
|
+
// The pointer answers under its old name on purpose. canon-keep-retired
|
|
25
31
|
expect: 'draft-figure',
|
|
26
32
|
},
|
|
27
33
|
{
|
|
28
34
|
prompt:
|
|
29
35
|
'This onboarding doc needs a hand-drawn picture of the signup-to-active path, sketch it in and make sure it actually renders correctly.',
|
|
30
|
-
expect: '
|
|
36
|
+
expect: 'draw-figure',
|
|
31
37
|
},
|
|
32
38
|
{
|
|
33
39
|
prompt:
|
|
@@ -108,10 +114,22 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
|
|
|
108
114
|
},
|
|
109
115
|
{
|
|
110
116
|
prompt: 'Draft me a script with beats for a screencast recording.',
|
|
117
|
+
expect: 'script-screencast',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
prompt:
|
|
121
|
+
'I typed draft-screencast and it said the screencast skill was renamed to script-screencast.', // canon-keep-retired
|
|
122
|
+
// The pointer answers under its old name on purpose. canon-keep-retired
|
|
111
123
|
expect: 'draft-screencast',
|
|
112
124
|
},
|
|
113
125
|
{
|
|
114
126
|
prompt: 'Turn this topic into a slide deck I can render.',
|
|
127
|
+
expect: 'make-deck',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
prompt:
|
|
131
|
+
'I typed draft-slides and it said the slides skill was renamed to make-deck.', // canon-keep-retired
|
|
132
|
+
// The pointer answers under its old name on purpose. canon-keep-retired
|
|
115
133
|
expect: 'draft-slides',
|
|
116
134
|
},
|
|
117
135
|
// Rot across documents nobody changed is what this reads, rather than a
|
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
|
|
|
2
2
|
import { dirname, join } from 'node:path'
|
|
3
3
|
import { isFamilyKey } from '@/claude/skills-families'
|
|
4
4
|
import { parseFrontmatter, readField } from '@/indexes/frontmatter'
|
|
5
|
-
import { bodyLines, maskDisplayed } from '@/markdown/scan'
|
|
5
|
+
import { type BodyLine, bodyLines, maskDisplayed } from '@/markdown/scan'
|
|
6
6
|
|
|
7
7
|
/** Returned when a skill folder carries no `REQUIREMENT.md`, the gating check. */
|
|
8
8
|
export const EXIT_MISSING_REQUIREMENT = 2
|
|
@@ -13,6 +13,19 @@ export const DESCRIPTION_LIMIT = 1024
|
|
|
13
13
|
/** The two headings the requirement template declares, matched at any level. */
|
|
14
14
|
export const REQUIREMENT_SECTIONS: readonly string[] = ['Gap', 'Must']
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The caps `claude/skills/create-skill/references/skill-requirement.md` states.
|
|
18
|
+
* Words count body tokens carrying a letter or digit, so a heading or bullet
|
|
19
|
+
* marker costs nothing and frontmatter is left out.
|
|
20
|
+
*/
|
|
21
|
+
export const REQUIREMENT_LIMITS = { bullets: 5, words: 400 } as const
|
|
22
|
+
|
|
23
|
+
/** The section the standard retired into Must not, matched at any level. */
|
|
24
|
+
export const RETIRED_REQUIREMENT_SECTION = 'Guards'
|
|
25
|
+
|
|
26
|
+
/** The section that states today's failures, where history is reported. */
|
|
27
|
+
const GAP_SECTION = 'Gap'
|
|
28
|
+
|
|
16
29
|
/** The one tree that ships to a target, and the only one the practice list names. */
|
|
17
30
|
export const SHIPPED_CORPUS = join('claude', 'skills')
|
|
18
31
|
|
|
@@ -100,6 +113,13 @@ const SECTION = /^##\s+(.+?)\s*$/
|
|
|
100
113
|
*/
|
|
101
114
|
const ISO_DATE = /\b\d{4}-\d{2}-\d{2}\b/g
|
|
102
115
|
|
|
116
|
+
/** An issue or pull request number, which a heading's `#` never matches. */
|
|
117
|
+
const ISSUE_NUMBER = /(?<![\w&/#])#\d+\b/g
|
|
118
|
+
|
|
119
|
+
const TOP_LEVEL_BULLET = /^(?:[-*+]|\d+[.)])\s/
|
|
120
|
+
|
|
121
|
+
const WORD = /\S*[\p{L}\p{N}]\S*/gu
|
|
122
|
+
|
|
103
123
|
export interface SkillFinding {
|
|
104
124
|
readonly rel: string
|
|
105
125
|
readonly detail: string
|
|
@@ -126,6 +146,7 @@ export interface SkillsAudit {
|
|
|
126
146
|
readonly nameMismatch: readonly SkillFinding[]
|
|
127
147
|
readonly longDescription: readonly SkillFinding[]
|
|
128
148
|
readonly requirementSections: readonly SkillFinding[]
|
|
149
|
+
readonly requirementLimits: readonly SkillFinding[]
|
|
129
150
|
readonly datedProvenance: readonly SkillFinding[]
|
|
130
151
|
readonly practiceShape: readonly SkillFinding[]
|
|
131
152
|
readonly referenceContents: readonly SkillFinding[]
|
|
@@ -142,6 +163,7 @@ interface SkillSource {
|
|
|
142
163
|
readonly family: string | undefined
|
|
143
164
|
/** Undefined when the folder carries no `REQUIREMENT.md` at all. */
|
|
144
165
|
readonly requirementHeadings: readonly string[] | undefined
|
|
166
|
+
readonly requirement: string | undefined
|
|
145
167
|
/** Every H2 in `SKILL.md` outside a fence, for the practice shape. */
|
|
146
168
|
readonly sections: readonly string[]
|
|
147
169
|
readonly hasLedger: boolean
|
|
@@ -198,6 +220,7 @@ export async function auditSkills(root: string): Promise<SkillsAudit> {
|
|
|
198
220
|
nameMismatch: sources.flatMap(nameFindings),
|
|
199
221
|
longDescription: sources.flatMap(lengthFindings),
|
|
200
222
|
requirementSections: sources.flatMap(sectionFindings),
|
|
223
|
+
requirementLimits: sources.flatMap(limitFindings),
|
|
201
224
|
datedProvenance: sources.flatMap((source) =>
|
|
202
225
|
source.bodies.flatMap(dateFindings),
|
|
203
226
|
),
|
|
@@ -269,6 +292,7 @@ async function readSkill(
|
|
|
269
292
|
family: declaredFamily(fields?.fields.metadata),
|
|
270
293
|
requirementHeadings:
|
|
271
294
|
requirement === undefined ? undefined : headings(requirement),
|
|
295
|
+
requirement,
|
|
272
296
|
sections: sectionsOf(body),
|
|
273
297
|
hasLedger: existsSync(join(skillDir, PRACTICE_LEDGER)),
|
|
274
298
|
bodies: [
|
|
@@ -349,6 +373,94 @@ function sectionFindings(source: SkillSource): SkillFinding[] {
|
|
|
349
373
|
]
|
|
350
374
|
}
|
|
351
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Reports every cap the standard states, each as its own finding so a file
|
|
378
|
+
* breaching two reads as two edits. Silent on an absent requirement for the
|
|
379
|
+
* reason `sectionFindings` is.
|
|
380
|
+
*/
|
|
381
|
+
function limitFindings(source: SkillSource): SkillFinding[] {
|
|
382
|
+
const { requirement, requirementHeadings } = source
|
|
383
|
+
if (requirement === undefined || requirementHeadings === undefined) return []
|
|
384
|
+
|
|
385
|
+
const rel = join(source.rel, 'REQUIREMENT.md')
|
|
386
|
+
const lines = bodyLines(requirement)
|
|
387
|
+
const details = [
|
|
388
|
+
...bulletDetails(lines),
|
|
389
|
+
...wordDetails(lines),
|
|
390
|
+
...gapHistoryDetails(lines),
|
|
391
|
+
...(requirementHeadings.includes(RETIRED_REQUIREMENT_SECTION)
|
|
392
|
+
? [`## ${RETIRED_REQUIREMENT_SECTION}: retired, fold it into Must not`]
|
|
393
|
+
: []),
|
|
394
|
+
]
|
|
395
|
+
|
|
396
|
+
return details.map((detail) => ({ rel, detail }))
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Counts top-level bullets per H2, since the template holds one list per
|
|
401
|
+
* section. A nested bullet elaborates its parent rather than adding to it.
|
|
402
|
+
*/
|
|
403
|
+
function bulletDetails(lines: readonly BodyLine[]): string[] {
|
|
404
|
+
const counts = new Map<string, number>()
|
|
405
|
+
let section: string | undefined
|
|
406
|
+
|
|
407
|
+
for (const line of lines) {
|
|
408
|
+
if (line.fenced) continue
|
|
409
|
+
const heading = SECTION.exec(line.text)?.[1]
|
|
410
|
+
if (heading !== undefined) {
|
|
411
|
+
section = heading
|
|
412
|
+
continue
|
|
413
|
+
}
|
|
414
|
+
if (section === undefined || !TOP_LEVEL_BULLET.test(line.text)) continue
|
|
415
|
+
counts.set(section, (counts.get(section) ?? 0) + 1)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return [...counts]
|
|
419
|
+
.filter(([, count]) => count > REQUIREMENT_LIMITS.bullets)
|
|
420
|
+
.map(
|
|
421
|
+
([name, count]) =>
|
|
422
|
+
`## ${name}: ${count} bullets, cap ${REQUIREMENT_LIMITS.bullets}`,
|
|
423
|
+
)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function wordDetails(lines: readonly BodyLine[]): string[] {
|
|
427
|
+
const words = lines.reduce(
|
|
428
|
+
(total, line) => total + (line.text.match(WORD)?.length ?? 0),
|
|
429
|
+
0,
|
|
430
|
+
)
|
|
431
|
+
if (words <= REQUIREMENT_LIMITS.words) return []
|
|
432
|
+
return [`${words} words, cap ${REQUIREMENT_LIMITS.words}`]
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Gap states what fails today, so a date or a number naming the incident that
|
|
437
|
+
* earned a line is history git already holds. A code span is quoted data, such
|
|
438
|
+
* as a sample filename, and is masked before matching.
|
|
439
|
+
*/
|
|
440
|
+
function gapHistoryDetails(lines: readonly BodyLine[]): string[] {
|
|
441
|
+
const details: string[] = []
|
|
442
|
+
let inGap = false
|
|
443
|
+
|
|
444
|
+
for (const line of lines) {
|
|
445
|
+
if (line.fenced) continue
|
|
446
|
+
const heading = HEADING.exec(line.text)?.[1]
|
|
447
|
+
if (heading !== undefined) {
|
|
448
|
+
inGap = heading === GAP_SECTION
|
|
449
|
+
continue
|
|
450
|
+
}
|
|
451
|
+
if (!inGap) continue
|
|
452
|
+
const masked = maskDisplayed(line.text)
|
|
453
|
+
for (const match of [
|
|
454
|
+
...masked.matchAll(ISO_DATE),
|
|
455
|
+
...masked.matchAll(ISSUE_NUMBER),
|
|
456
|
+
]) {
|
|
457
|
+
details.push(`line ${line.number}: ${match[0]} in ${GAP_SECTION}`)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return details
|
|
462
|
+
}
|
|
463
|
+
|
|
352
464
|
/**
|
|
353
465
|
* A listed folder the shipped corpus does not hold, such as a renamed skill or
|
|
354
466
|
* a misspelled append, would otherwise read as a pass. Silent where the
|