@erclx/canon 5.32.0 → 5.33.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/auto-ship/SKILL.md +1 -1
- package/claude/skills/auto-ship/references/worktree-name.md +1 -1
- package/claude/skills/backlog-triage/REQUIREMENT.md +2 -2
- package/claude/skills/backlog-triage/SKILL.md +1 -1
- package/claude/skills/canon-cli/REQUIREMENT.md +16 -13
- package/claude/skills/canon-cli/SKILL.md +75 -32
- package/claude/skills/canon-feedback/SKILL.md +1 -1
- package/claude/skills/{canvas → canvas-board}/REQUIREMENT.md +2 -2
- package/claude/skills/{canvas → canvas-board}/SKILL.md +2 -2
- package/{standards → claude/skills/canvas-board/references}/design.md +11 -2
- package/claude/skills/context-fold/REQUIREMENT.md +25 -33
- package/claude/skills/context-fold/SKILL.md +25 -70
- package/claude/skills/context-fold/references/classify.md +3 -3
- package/claude/skills/context-fold/references/completion-report.md +4 -4
- package/claude/skills/context-fold/references/context-refresh.md +5 -5
- package/{standards → claude/skills/context-fold/references}/context.md +16 -5
- package/claude/skills/context-fold/references/unusable-baseline.md +3 -5
- package/claude/skills/create-rule/SKILL.md +2 -2
- package/{standards → claude/skills/create-rule/references}/rule.md +12 -5
- package/claude/skills/create-skill/SKILL.md +2 -2
- package/{standards → claude/skills/create-skill/references}/skill.md +17 -14
- package/claude/skills/design-taste/REQUIREMENT.md +3 -3
- package/claude/skills/design-taste/SKILL.md +1 -1
- package/claude/skills/docs-sync/REQUIREMENT.md +1 -1
- package/claude/skills/docs-sync/SKILL.md +3 -3
- package/{standards → claude/skills/docs-sync/references}/docs.md +11 -0
- package/{standards → claude/skills/docs-sync/references}/readme.md +9 -2
- package/claude/skills/draft-and-pick/SKILL.md +5 -5
- package/claude/skills/draft-doc/SKILL.md +6 -6
- package/claude/skills/draft-figure/REQUIREMENT.md +2 -2
- package/claude/skills/draft-figure/SKILL.md +2 -2
- package/claude/skills/draft-figure/references/hand-drawn.md +1 -1
- package/claude/skills/draft-figure/references/mermaid-path.md +1 -1
- package/claude/skills/draft-identity/REQUIREMENT.md +2 -2
- package/claude/skills/draft-identity/SKILL.md +2 -2
- package/claude/skills/draft-ready/SKILL.md +1 -1
- package/claude/skills/git-branch/SKILL.md +1 -1
- package/{standards → claude/skills/git-branch/references}/branch.md +1 -1
- package/claude/skills/git-commit/SKILL.md +2 -2
- package/claude/skills/git-followup/REQUIREMENT.md +1 -1
- package/claude/skills/git-followup/SKILL.md +1 -1
- package/claude/skills/git-issue/SKILL.md +1 -1
- package/claude/skills/git-pr/SKILL.md +13 -13
- package/claude/skills/git-pr/references/evidence.md +2 -2
- package/claude/skills/git-pr/references/final-command.md +38 -14
- package/claude/skills/git-pr/references/labels.md +5 -1
- package/{standards → claude/skills/git-pr/references}/pr.md +19 -9
- package/{standards → claude/skills/git-pr/references}/publish.md +4 -2
- package/claude/skills/git-pr/references/task-number.md +1 -1
- package/claude/skills/git-ship/SKILL.md +15 -14
- package/claude/skills/git-split/SKILL.md +3 -3
- package/claude/skills/git-stage/SKILL.md +1 -1
- package/claude/skills/plan-feature/REQUIREMENT.md +1 -1
- package/claude/skills/review-address/SKILL.md +1 -1
- package/claude/skills/review-address/references/post-review-findings.md +1 -1
- package/claude/skills/review-pr/REQUIREMENT.md +1 -1
- package/claude/skills/review-pr/SKILL.md +3 -3
- package/claude/skills/review-pr/references/body-variants.md +2 -2
- package/claude/skills/review-pr/references/key-changes.md +3 -3
- package/claude/skills/review-pr/references/post.md +4 -2
- package/claude/skills/review-ui/REQUIREMENT.md +1 -1
- package/claude/skills/review-ui/SKILL.md +1 -1
- package/claude/skills/review-ui/references/post.md +1 -1
- package/claude/skills/role-orchestrator/REQUIREMENT.md +3 -0
- package/claude/skills/role-orchestrator/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +8 -5
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +23 -1
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-resume.md +3 -0
- package/claude/skills/role-planner/REQUIREMENT.md +2 -1
- package/claude/skills/role-planner/SKILL.md +13 -2
- package/claude/skills/role-reviewer/SKILL.md +1 -1
- package/claude/skills/role-worker/REQUIREMENT.md +1 -0
- package/claude/skills/role-worker/SKILL.md +9 -1
- package/claude/skills/session-map/SKILL.md +1 -1
- package/claude/skills/session-resume/REQUIREMENT.md +2 -2
- package/claude/skills/session-worktree/SKILL.md +5 -5
- package/claude/skills/target-setup/references/detect.md +1 -1
- package/claude/skills/task-board/REQUIREMENT.md +7 -12
- package/claude/skills/task-board/SKILL.md +5 -17
- package/claude/skills/task-board/references/tasks.md +2 -2
- package/claude/skills/task-complete/REQUIREMENT.md +47 -0
- package/claude/skills/task-complete/SKILL.md +86 -0
- package/claude/skills/{task-board → task-complete}/references/archive.md +4 -4
- package/claude/skills/{context-fold → task-complete}/references/receipt-sweep.md +5 -5
- package/claude/skills/task-complete/references/unusable-baseline.md +19 -0
- package/claude/skills/teach-workspace/REQUIREMENT.md +6 -2
- package/claude/skills/teach-workspace/SKILL.md +8 -4
- package/claude/skills/teach-workspace/references/lesson-craft.md +1 -1
- package/claude/skills/{context-fold/references/promoted-pages.md → teach-workspace/references/promotion-fold.md} +3 -3
- package/claude/skills/teach-workspace/references/promotion.md +3 -3
- package/claude/skills/ui-checklist/SKILL.md +1 -1
- package/claude/skills/ux-audit/SKILL.md +1 -1
- package/claude/skills/ux-measure/SKILL.md +1 -1
- package/docs/agents/canvas.md +1 -1
- package/docs/agents/commands.md +3 -2
- package/docs/agents/context-audit-checks.md +4 -4
- package/docs/agents/index.md +3 -3
- package/docs/agents/indexes.md +1 -1
- package/docs/agents/install-and-sync.md +6 -11
- package/docs/agents/key-changes.md +5 -5
- package/docs/agents/label-coverage.md +11 -0
- package/docs/agents/migrate.md +1 -1
- package/docs/agents/records-push.md +2 -2
- package/docs/agents/records-stale.md +12 -47
- package/docs/agents/records-validate.md +3 -7
- package/docs/agents/records.md +5 -27
- package/docs/agents/rule-citations.md +1 -1
- package/docs/agents/sandbox.md +4 -4
- package/docs/agents/skills-audit.md +1 -1
- package/docs/agents/tasks-plans.md +2 -2
- package/docs/agents/tasks.md +2 -2
- package/docs/target/migrations.md +10 -2
- package/docs/target/projects.md +1 -1
- package/docs/target/sync.md +1 -1
- package/docs/workflow/operating-model.md +2 -2
- package/docs/workflow/parallel-features.md +7 -7
- package/docs/workflow/skill-map-on-request.md +9 -9
- package/docs/workflow/skill-map.md +12 -11
- package/docs/workflow/visual-design-workflow.md +2 -2
- package/governance/renames.toml +0 -11
- package/governance/rules/canon/context.md +13 -0
- package/governance/rules/claude/skill.md +17 -0
- package/governance/stacks/base.toml +2 -4
- package/package.json +4 -4
- package/scripts/core/install-check.sh +3 -3
- package/scripts/lib/ui.sh +0 -159
- package/src/audits/catalog.ts +0 -1
- package/src/claude/cases/misc.ts +1 -1
- package/src/claude/cases/setup.ts +4 -0
- package/src/claude/cases/workflow.ts +6 -1
- package/src/claude/seeds.ts +1 -1
- package/src/claude/skills-audit.ts +2 -2
- package/src/cli/help.ts +3 -1
- package/src/cli/ui.ts +2 -3
- package/src/commands/claude.ts +1 -1
- package/src/commands/gov.ts +2 -15
- package/src/commands/labels.ts +326 -7
- package/src/commands/migrate.ts +6 -1
- package/src/commands/pr.ts +1 -1
- package/src/commands/records.ts +13 -399
- package/src/commands/sandbox.ts +3 -6
- package/src/commands/tasks.ts +3 -3
- package/src/context/audit.ts +5 -5
- package/src/context/folders.ts +1 -1
- package/src/design/budget.ts +1 -1
- package/src/design/components.ts +1 -1
- package/src/design/document.ts +1 -1
- package/src/design/regen.ts +1 -1
- package/src/design/render.ts +1 -1
- package/src/gate/measures.ts +1 -1
- package/src/gate/sequencer.ts +56 -1
- package/src/gate/stages.ts +1 -7
- package/src/gov/install.ts +2 -3
- package/src/gov/stacks.ts +1 -1
- package/src/hooks/post-merge.ts +1 -1
- package/src/labels/apply.ts +39 -0
- package/src/labels/format.ts +3 -3
- package/src/labels/phase.ts +3 -3
- package/src/labels/sections.ts +43 -0
- package/src/migrate/skill-names.ts +20 -1
- package/src/pr/paths.ts +4 -4
- package/src/records/backup.ts +16 -6
- package/src/records/stale-canonical.ts +31 -1
- package/src/records/validate.ts +2 -188
- package/src/sandbox/census.ts +1 -1
- package/src/sandbox/coverage.ts +2 -6
- package/src/sandbox/equivalence.ts +5 -44
- package/src/sandbox/expect.ts +4 -4
- package/src/sandbox/provision.ts +13 -36
- package/src/sandbox/scenario.ts +26 -48
- package/src/sandbox/tree.ts +2 -6
- package/src/standards/read.ts +14 -1
- package/src/sync/git.ts +1 -1
- package/src/tasks/archive.ts +5 -5
- package/src/tasks/branch.ts +3 -3
- package/src/tasks/label.ts +1 -1
- package/src/tasks/record.ts +2 -2
- package/src/tasks/validate.ts +1 -1
- package/standards/index.md +0 -13
- package/standards/standard.md +1 -7
- package/tooling/base/reference.md +1 -1
- package/tooling/base/seeds/.github/pull_request_template.md +2 -2
- package/tooling/claude/reference.md +1 -1
- package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +9 -1
- package/tooling/claude/seeds/.claude/hooks/review-post-scan.sh +171 -0
- package/tooling/claude/seeds/.claude/settings.json +9 -0
- package/governance/rules/standards/context.md +0 -14
- package/governance/rules/standards/design.md +0 -12
- package/governance/rules/standards/readme.md +0 -12
- package/governance/rules/standards/rule.md +0 -13
- package/governance/rules/standards/skill.md +0 -21
- package/governance/rules/standards/standard.md +0 -13
- package/scripts/config.sh +0 -11
- package/scripts/lib/gov.sh +0 -14
- package/scripts/lib/sandbox-fixtures.sh +0 -191
- package/scripts/lib/sandbox-git.sh +0 -125
- package/scripts/lib/sandbox-path.sh +0 -50
- package/scripts/sandbox-hook.sh +0 -99
- package/src/gov/standard-rules.ts +0 -179
- package/src/records/migrate.ts +0 -78
- package/src/records/stale.ts +0 -243
- package/src/sandbox/hooks.ts +0 -128
- /package/claude/skills/{canvas → canvas-board}/references/canvas.md +0 -0
- /package/{standards → claude/skills/draft-figure/references}/figures.md +0 -0
- /package/{standards → claude/skills/git-branch/references}/slug.md +0 -0
- /package/{standards → claude/skills/git-commit/references}/commit.md +0 -0
- /package/{standards → claude/skills/git-commit/references}/versioning.md +0 -0
package/src/standards/read.ts
CHANGED
|
@@ -60,8 +60,21 @@ export function standardRoots(root: string): StandardRoot[] {
|
|
|
60
60
|
*/
|
|
61
61
|
const MOVED_STANDARDS: Readonly<Record<string, string>> = {
|
|
62
62
|
board: 'claude/skills/task-board/references/board.md',
|
|
63
|
+
context: 'claude/skills/context-fold/references/context.md',
|
|
64
|
+
design: 'claude/skills/canvas-board/references/design.md',
|
|
65
|
+
docs: 'claude/skills/docs-sync/references/docs.md',
|
|
66
|
+
figures: 'claude/skills/draft-figure/references/figures.md',
|
|
67
|
+
readme: 'claude/skills/docs-sync/references/readme.md',
|
|
68
|
+
rule: 'claude/skills/create-rule/references/rule.md',
|
|
69
|
+
skill: 'claude/skills/create-skill/references/skill.md',
|
|
70
|
+
branch: 'claude/skills/git-branch/references/branch.md',
|
|
71
|
+
commit: 'claude/skills/git-commit/references/commit.md',
|
|
72
|
+
pr: 'claude/skills/git-pr/references/pr.md',
|
|
73
|
+
publish: 'claude/skills/git-pr/references/publish.md',
|
|
74
|
+
slug: 'claude/skills/git-branch/references/slug.md',
|
|
75
|
+
versioning: 'claude/skills/git-commit/references/versioning.md',
|
|
63
76
|
markdown: 'claude/skills/markdown-craft/references/markdown.md',
|
|
64
|
-
canvas: 'claude/skills/canvas/references/canvas.md',
|
|
77
|
+
canvas: 'claude/skills/canvas-board/references/canvas.md',
|
|
65
78
|
glossary: 'claude/skills/teach-workspace/references/glossary.md',
|
|
66
79
|
groundwork: 'claude/skills/plan-groundwork/references/groundwork.md',
|
|
67
80
|
'groundwork-spikes':
|
package/src/sync/git.ts
CHANGED
package/src/tasks/archive.ts
CHANGED
|
@@ -578,7 +578,7 @@ function isBulletFor(line: string, target: string): boolean {
|
|
|
578
578
|
|
|
579
579
|
/**
|
|
580
580
|
* Resolves the `Plan:` target against the board and against the project root
|
|
581
|
-
* both, which is how `
|
|
581
|
+
* both, which is how `task-complete` reads the same line. It accepts `../plans/x.md`
|
|
582
582
|
* and `.canon/plans/x.md` as one file, so a gate reading only the first form
|
|
583
583
|
* would pass the second and strand the plan this exists to protect.
|
|
584
584
|
*
|
|
@@ -633,7 +633,7 @@ function atOneRoot(path: string, plans: string[], root: string): string {
|
|
|
633
633
|
/**
|
|
634
634
|
* Names the other live tasks whose `Plan:` line lands on the same file through
|
|
635
635
|
* any of its links. This is
|
|
636
|
-
* the rule `
|
|
636
|
+
* the rule the `post-merge` hook applies before it archives a plan, held here so one
|
|
637
637
|
* question has one implementation: a plan another live task still cites is a
|
|
638
638
|
* plan the sweep is correct to leave, and a guard that read the folder instead
|
|
639
639
|
* refused every task sharing one plan and deadlocked the board against the
|
|
@@ -697,7 +697,7 @@ export type CitationOutcome = PlanCitations | ArchiveRefused
|
|
|
697
697
|
|
|
698
698
|
/**
|
|
699
699
|
* Answers where one task's plan sits and who else holds it, which is the whole
|
|
700
|
-
* of the last-live-citation rule.
|
|
700
|
+
* of the last-live-citation rule. The archive reads this rather than scanning
|
|
701
701
|
* the board itself, so the sweep that moves a plan and the gate that refuses a
|
|
702
702
|
* task archive cannot drift into disagreeing about which plan is free.
|
|
703
703
|
*
|
|
@@ -897,7 +897,7 @@ async function resolveStem(
|
|
|
897
897
|
|
|
898
898
|
/**
|
|
899
899
|
* Archives one task as a single unit: the move, the ordering-row removal, and
|
|
900
|
-
* the index regen. The hook and `task-
|
|
900
|
+
* the index regen. The hook and `task-complete` both call this, so every gate
|
|
901
901
|
* refuses rather than reports. A caller with nobody watching cannot act on a
|
|
902
902
|
* warning, and two callers gating differently is the drift this exists to stop.
|
|
903
903
|
*/
|
|
@@ -918,7 +918,7 @@ export async function archiveTask(
|
|
|
918
918
|
const from = join(dir, `${stem}.md`)
|
|
919
919
|
const text = await readFile(from, 'utf8')
|
|
920
920
|
|
|
921
|
-
// `
|
|
921
|
+
// `task-complete` ticks outcomes at ship time, before the merge, so an earlier
|
|
922
922
|
// slice merging after the last slice shipped would find every box ticked
|
|
923
923
|
// while the last slice is still open. Only the last number closes the task.
|
|
924
924
|
const last = readPullRequest(text).at(-1)
|
package/src/tasks/branch.ts
CHANGED
|
@@ -14,10 +14,10 @@ const MARKDOWN = '.md'
|
|
|
14
14
|
*/
|
|
15
15
|
export const PLAN_BRANCH_TYPE = 'feat'
|
|
16
16
|
|
|
17
|
-
/** The description cap in `
|
|
17
|
+
/** The description cap in `claude/skills/git-branch/references/branch.md`, in kebab-separated words. */
|
|
18
18
|
export const DESCRIPTION_WORD_CAP = 4
|
|
19
19
|
|
|
20
|
-
/** The branch length cap in `
|
|
20
|
+
/** The branch length cap in `claude/skills/git-branch/references/branch.md`, in characters. */
|
|
21
21
|
export const BRANCH_LENGTH_CAP = 50
|
|
22
22
|
|
|
23
23
|
export interface PlanBranch {
|
|
@@ -48,7 +48,7 @@ function slugOf(path: string): string {
|
|
|
48
48
|
* of them name. It is the one derivation, so the collision check and the
|
|
49
49
|
* worktree entry it gates cannot hold two answers for one plan.
|
|
50
50
|
*
|
|
51
|
-
* Conformance covers both caps `
|
|
51
|
+
* Conformance covers both caps `claude/skills/git-branch/references/branch.md` states, being the word
|
|
52
52
|
* count of the description and the length of the whole branch. A slug is a
|
|
53
53
|
* plan's own filename rather than a name anyone chose for a branch, so a plan
|
|
54
54
|
* can name a branch this refuses to grade as conforming, and reporting that is
|
package/src/tasks/label.ts
CHANGED
|
@@ -185,7 +185,7 @@ async function reserveLabel(root: string, label: string): Promise<boolean> {
|
|
|
185
185
|
* the task file after this returns, so a reservation with no file behind it is
|
|
186
186
|
* the ordinary state for a few seconds, and a scan blind to it would offer the
|
|
187
187
|
* same label to every retry. Reservations are never expired: a claim that died
|
|
188
|
-
* leaves a gap, and `
|
|
188
|
+
* leaves a gap, and `claude/skills/git-commit/references/versioning.md` permits gaps.
|
|
189
189
|
*/
|
|
190
190
|
export async function claimLabel(
|
|
191
191
|
root: string,
|
package/src/tasks/record.ts
CHANGED
|
@@ -420,7 +420,7 @@ async function openTask(
|
|
|
420
420
|
* resolve the board root in-process.
|
|
421
421
|
*
|
|
422
422
|
* `branches` names the recording branch and every name it carried before a
|
|
423
|
-
* rename, since `git-ship` renames a branch after `
|
|
423
|
+
* rename, since `git-ship` renames a branch after `task-complete` has already
|
|
424
424
|
* written the marker under the old name. Each comes off the `Pending branch:`
|
|
425
425
|
* line even when the number was already listed, so a rerun repairs a stale
|
|
426
426
|
* marker.
|
|
@@ -504,7 +504,7 @@ export async function recordPlan(
|
|
|
504
504
|
}
|
|
505
505
|
|
|
506
506
|
/**
|
|
507
|
-
* Marks the named outcomes `[x]` in place. `
|
|
507
|
+
* Marks the named outcomes `[x]` in place. `task-complete` runs this against a
|
|
508
508
|
* board it can read and cannot edit from a linked worktree, and the positions
|
|
509
509
|
* come from the read it already made.
|
|
510
510
|
*
|
package/src/tasks/validate.ts
CHANGED
|
@@ -597,7 +597,7 @@ async function listTaskStems(dir: string): Promise<string[]> {
|
|
|
597
597
|
|
|
598
598
|
/**
|
|
599
599
|
* Resolves a pointer against the board and against the project root both, the
|
|
600
|
-
* way `
|
|
600
|
+
* way `task-complete` reads the same line. A fragment is dropped first, since an
|
|
601
601
|
* anchor is part of the link and never part of the path.
|
|
602
602
|
*/
|
|
603
603
|
function resolves(target: string, dir: string, root: string): boolean {
|
package/standards/index.md
CHANGED
|
@@ -7,17 +7,4 @@ subtitle: Reference docs for consistent authoring across the toolkit and target
|
|
|
7
7
|
|
|
8
8
|
Reference docs for consistent authoring across the toolkit and target projects.
|
|
9
9
|
|
|
10
|
-
- [Branch reference](branch.md): Branch naming format and type conventions
|
|
11
|
-
- [Commit reference](commit.md): Commit message format and type conventions
|
|
12
|
-
- [Context entry reference](context.md): Shape and content rules for canon/context/<domain>.md entries
|
|
13
|
-
- [Design reference](design.md): Shape and content rules for canon/DESIGN.md
|
|
14
|
-
- [Docs reference](docs.md): Reader and jurisdiction, frontmatter, page structure, what a page links out to, the diagram permission, and when a category earns a subfolder
|
|
15
|
-
- [Figure reference](figures.md): When a figure earns its place, the render-first policy for a graph-shaped subject, freehand SVG as the escape hatch, and its wrapping, color, and accessibility rules
|
|
16
|
-
- [Pull request reference](pr.md): Pull request title and body conventions
|
|
17
|
-
- [Publish reference](publish.md): Scan an author runs against finished text, the cross-reference form each destination takes, and the response to an unreadable source
|
|
18
|
-
- [Readme reference](readme.md): Readme voice, structure, and content conventions
|
|
19
|
-
- [Governance rule reference](rule.md): Rule frontmatter, body shape, and voice for .claude/rules files
|
|
20
|
-
- [Claude skill reference](skill.md): Skill structure and authoring rules for Claude Code
|
|
21
|
-
- [Slug reference](slug.md): Transform from a git branch name to a slug, and the three responses to an empty result
|
|
22
10
|
- [Standard reference](standard.md): Shape and content rules for authoring a standard
|
|
23
|
-
- [Versioning reference](versioning.md): Phase label vs semver discipline across tasks, PRs, reviews, issues, commits, and tags
|
package/standards/standard.md
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Standard reference
|
|
3
3
|
description: Shape and content rules for authoring a standard
|
|
4
|
-
paths:
|
|
5
|
-
- 'standards/**/*.md'
|
|
6
|
-
rule:
|
|
7
|
-
- 'Read it before writing or editing a standard. Do not work the shape from memory.'
|
|
8
4
|
---
|
|
9
5
|
|
|
10
6
|
# Standard reference
|
|
@@ -68,9 +64,7 @@ A standard failing these questions is non-conforming even when it satisfies ever
|
|
|
68
64
|
|
|
69
65
|
- Start the file with a frontmatter block carrying at least `title` and `description`. A consuming surface may require more.
|
|
70
66
|
- `title`: names the doc type in sentence case, suffixed `reference` (`Commit reference`, `Branch reference`)
|
|
71
|
-
- `description`: one line naming what the standard covers. It becomes the index link label on install
|
|
72
|
-
- `paths` (optional): the globs where the standard applies, one per entry, copied verbatim into the rule `canon gov regen` generates for it. Omit it when no path should route to the standard, and no rule is generated.
|
|
73
|
-
- `rule` (optional): operative bullets the generated rule carries after its pointer bullet, one per entry, each a full sentence. Quote an entry holding a backtick, a colon, or a leading `-`.
|
|
67
|
+
- `description`: one line naming what the standard covers. It becomes the index link label on install.
|
|
74
68
|
|
|
75
69
|
## Structure
|
|
76
70
|
|
|
@@ -29,7 +29,7 @@ Seeds live in `tooling/base/seeds/`. Sync drops each once on first install and n
|
|
|
29
29
|
- `.lintstagedrc`: the glob map below.
|
|
30
30
|
- `.prettierignore`: created empty. Projects add their own entries.
|
|
31
31
|
- `.github/workflows/verify.yml`: runs on pull requests targeting `main` and on `workflow_dispatch`. A project with more than one language root adds its own jobs here.
|
|
32
|
-
- `.github/pull_request_template.md`: `## Summary`, `## Key
|
|
32
|
+
- `.github/pull_request_template.md`: `## Summary`, `## Key changes`, `## Technical context`, `## Testing`. A project adds the checklist lines its own review needs.
|
|
33
33
|
- `canon/context/`: extend the `ci` and `development` entries with project-specific commands, workflows, or deploy steps. Canonical rationale stays in this reference.
|
|
34
34
|
|
|
35
35
|
## Tool pairing
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
<Action Verb> <Direct Object> to <Result>.
|
|
4
4
|
|
|
5
|
-
## Key
|
|
5
|
+
## Key changes
|
|
6
6
|
|
|
7
7
|
- <Verb> <specific component/file/function> (<reason if non-obvious>)
|
|
8
8
|
- <Verb> <specific component/file/function>
|
|
9
9
|
|
|
10
|
-
<!-- ## Technical
|
|
10
|
+
<!-- ## Technical context (optional, omit for docs/config/trivial changes) -->
|
|
11
11
|
<!-- - <Why this approach was taken, not what changed> -->
|
|
12
12
|
|
|
13
13
|
## Testing
|
|
@@ -21,7 +21,7 @@ canon/
|
|
|
21
21
|
|
|
22
22
|
.canon/
|
|
23
23
|
├── tasks/ ← seeded. One file per task plus a generated index.md, local scratch only. `archive/` holds the tasks that shipped.
|
|
24
|
-
├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries
|
|
24
|
+
├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries moved into `archive/` by `canon tasks archive` when the `post-merge` hook archives their task.
|
|
25
25
|
├── review/ ← one subfolder per producer. `branch/` no longer written, `design/` for canon design render.
|
|
26
26
|
└── tmp/ ← ephemeral scratch space, safe to delete without loss
|
|
27
27
|
```
|
|
@@ -24,8 +24,9 @@ IFS= read -r -d '' -t 2 input
|
|
|
24
24
|
# its edit path prints a URL as well and is not an opening.
|
|
25
25
|
command=$(printf '%s' "$input" | jq -r '.tool_input.command // empty')
|
|
26
26
|
stdout=$(printf '%s' "$input" | jq -r '.tool_response.stdout // empty')
|
|
27
|
+
is_hand_opened=false
|
|
27
28
|
case "$command" in
|
|
28
|
-
*"gh pr create"*) ;;
|
|
29
|
+
*"gh pr create"*) is_hand_opened=true ;;
|
|
29
30
|
*)
|
|
30
31
|
printf '%s\n' "$stdout" | grep -qx 'opened=true' || exit 0
|
|
31
32
|
;;
|
|
@@ -57,4 +58,11 @@ session=$(printf '%s' "$input" | jq -r '.session_id // "unknown"')
|
|
|
57
58
|
printf -- '- %s session=%s pr=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$session" "$url" >>"$log_dir/log.md"
|
|
58
59
|
|
|
59
60
|
msg="Pull request $url just opened. If this session holds a worker's channel obligation, announce it to the controller now, per role-worker."
|
|
61
|
+
# A pull request opened by hand skipped git-pr, so nothing has labelled it.
|
|
62
|
+
# The verb is named only where the installed canon carries it, since a skill
|
|
63
|
+
# can reach a target ahead of the release that ships the verb.
|
|
64
|
+
if [ "$is_hand_opened" = true ] && canon labels apply --help >/dev/null 2>&1; then
|
|
65
|
+
msg="$msg Run canon labels apply ${url##*/} to label it."
|
|
66
|
+
fi
|
|
67
|
+
|
|
60
68
|
jq -nc --arg msg "$msg" '{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:$msg}}'
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Claude Code sends a payload and closes stdin. A bare read with nothing feeding
|
|
4
|
+
# it blocks forever and holds the session open, so the read is bounded. `read`
|
|
5
|
+
# rather than `timeout cat`, which macOS does not ship.
|
|
6
|
+
IFS= read -r -d '' -t 2 input
|
|
7
|
+
[ -n "$input" ] || {
|
|
8
|
+
printf '%s reads a Claude Code hook payload on stdin and cannot be run by hand.\n' "${0##*/}" >&2
|
|
9
|
+
exit 1
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# Every Bash call reaches this line, so the command is read in one jq pass and
|
|
13
|
+
# matched with a shell pattern before anything else costs a process.
|
|
14
|
+
fields=$(printf '%s' "$input" | jq -r 'if .tool_name == "Bash" then [(.cwd // ""), (.tool_input.command // "")] | @json else empty end')
|
|
15
|
+
[ -n "$fields" ] || exit 0
|
|
16
|
+
|
|
17
|
+
cmd=$(printf '%s' "$fields" | jq -r '.[1]')
|
|
18
|
+
case "$cmd" in
|
|
19
|
+
*'gh api'*) ;;
|
|
20
|
+
*) exit 0 ;;
|
|
21
|
+
esac
|
|
22
|
+
|
|
23
|
+
# A command that only mentions a post is not one: a heredoc writing a review
|
|
24
|
+
# body that quotes the command, or a search whose pattern holds it. The string
|
|
25
|
+
# is cut into the commands it runs, with heredoc bodies dropped and quoted text
|
|
26
|
+
# left whole, and only a command that starts with `gh api` is read further.
|
|
27
|
+
# Splitting on the separators outside quotes is what keeps `echo 'a; gh api'`
|
|
28
|
+
# one command rather than two.
|
|
29
|
+
IFS= read -r -d '' splitter <<'AWK'
|
|
30
|
+
{ s = s (NR > 1 ? "\n" : "") $0 }
|
|
31
|
+
function emit() {
|
|
32
|
+
gsub(/\n/, " ", seg)
|
|
33
|
+
sub(/^[ \t]+/, "", seg)
|
|
34
|
+
if (seg != "") print seg
|
|
35
|
+
seg = ""
|
|
36
|
+
}
|
|
37
|
+
END {
|
|
38
|
+
n = length(s); q = ""; seg = ""; nt = 0; i = 1
|
|
39
|
+
while (i <= n) {
|
|
40
|
+
c = substr(s, i, 1)
|
|
41
|
+
if (q == "'") { seg = seg c; if (c == "'") q = ""; i++; continue }
|
|
42
|
+
if (q == "\"") {
|
|
43
|
+
if (c == "\\") { seg = seg c substr(s, i + 1, 1); i += 2; continue }
|
|
44
|
+
if (c == "\"") q = ""
|
|
45
|
+
seg = seg c; i++; continue
|
|
46
|
+
}
|
|
47
|
+
if (c == "\\") { seg = seg c substr(s, i + 1, 1); i += 2; continue }
|
|
48
|
+
if (c == "'" || c == "\"") { q = c; seg = seg c; i++; continue }
|
|
49
|
+
if (c == "#" && (seg == "" || substr(seg, length(seg), 1) ~ /[ \t]/)) {
|
|
50
|
+
while (i <= n && substr(s, i, 1) != "\n") i++
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
if (c == "<" && substr(s, i, 2) == "<<" && substr(s, i, 3) != "<<<") {
|
|
54
|
+
j = i + 2; dash = 0
|
|
55
|
+
if (substr(s, j, 1) == "-") { dash = 1; j++ }
|
|
56
|
+
while (substr(s, j, 1) ~ /[ \t]/) j++
|
|
57
|
+
oq = substr(s, j, 1)
|
|
58
|
+
if (oq == "'" || oq == "\"") j++
|
|
59
|
+
tag = ""
|
|
60
|
+
while (substr(s, j, 1) ~ /[A-Za-z0-9_]/) { tag = tag substr(s, j, 1); j++ }
|
|
61
|
+
if (oq == "'" || oq == "\"") j++
|
|
62
|
+
if (tag != "") { nt++; tags[nt] = tag; dashes[nt] = dash; i = j; continue }
|
|
63
|
+
}
|
|
64
|
+
if (c == "\n") {
|
|
65
|
+
emit(); i++
|
|
66
|
+
for (t = 1; t <= nt; t++) {
|
|
67
|
+
while (i <= n) {
|
|
68
|
+
e = index(substr(s, i), "\n")
|
|
69
|
+
if (e == 0) { line = substr(s, i); i = n + 1 } else { line = substr(s, i, e - 1); i += e }
|
|
70
|
+
if (dashes[t]) sub(/^\t+/, "", line)
|
|
71
|
+
if (line == tags[t]) break
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
nt = 0; continue
|
|
75
|
+
}
|
|
76
|
+
if (c == ";" || c == "|" || c == "(" || c == ")" || c == "`" || c == "&") {
|
|
77
|
+
if (c == "&" && (substr(seg, length(seg), 1) ~ /[<>]/ || substr(s, i + 1, 1) == ">")) { seg = seg c; i++; continue }
|
|
78
|
+
emit(); i++; continue
|
|
79
|
+
}
|
|
80
|
+
seg = seg c; i++
|
|
81
|
+
}
|
|
82
|
+
emit()
|
|
83
|
+
}
|
|
84
|
+
AWK
|
|
85
|
+
|
|
86
|
+
# A block reaches the session through stderr, and stdout stays empty so nothing
|
|
87
|
+
# unframed lands in the hook protocol.
|
|
88
|
+
deny() {
|
|
89
|
+
printf '%s\n' "$1" >&2
|
|
90
|
+
exit 2
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# The thread a review or a comment lands on: a review post or edit, and an issue
|
|
94
|
+
# or pull request comment post or edit. A pull request's inline comments are left
|
|
95
|
+
# out, since no shipped skill posts one.
|
|
96
|
+
seg="[^/[:space:]'\"]+"
|
|
97
|
+
end="(['\" ?]|\$)"
|
|
98
|
+
endpoint="(pulls/$seg/reviews(/$seg)?|issues/$seg/comments|issues/comments/$seg)$end"
|
|
99
|
+
starts_gh_api="^([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]+)*((command|exec)[[:space:]]+)?gh[[:space:]]+api([[:space:]]|\$)"
|
|
100
|
+
body_inline="[[:space:]](-f|-F|--field|--raw-field)[[:space:]=]*['\"]?body="
|
|
101
|
+
body_file="[[:space:]](-F|--field)[[:space:]=]*['\"]?body=@([^'\"[:space:]]+)"
|
|
102
|
+
|
|
103
|
+
# Reads one command already known to start with `gh api` and returns when it
|
|
104
|
+
# has nothing to scan or the scan came back clean.
|
|
105
|
+
check() {
|
|
106
|
+
local call=$1 method='' file='' cwd out code warnings
|
|
107
|
+
|
|
108
|
+
[[ $call =~ $endpoint ]] || return 0
|
|
109
|
+
|
|
110
|
+
# gh sends POST when a field flag is present and no method is named, and the
|
|
111
|
+
# explicit method wins when one is. A call naming neither is a read.
|
|
112
|
+
if [[ $call =~ (-X|--method)[[:space:]=]*([A-Za-z]+) ]]; then
|
|
113
|
+
method=$(printf '%s' "${BASH_REMATCH[2]}" | tr '[:lower:]' '[:upper:]')
|
|
114
|
+
elif [[ $call =~ [[:space:]](-f|-F|--field|--raw-field|--input)([[:space:]=]|$) ]]; then
|
|
115
|
+
method=POST
|
|
116
|
+
fi
|
|
117
|
+
case "$method" in
|
|
118
|
+
POST | PATCH | PUT) ;;
|
|
119
|
+
*) return 0 ;;
|
|
120
|
+
esac
|
|
121
|
+
|
|
122
|
+
if [[ $call =~ $body_file ]]; then
|
|
123
|
+
file=${BASH_REMATCH[2]}
|
|
124
|
+
elif [[ $call =~ $body_inline ]]; then
|
|
125
|
+
deny 'This review or comment post passes its body inline, so the label scan cannot read it. Write the body to a file in an earlier call and pass it as -F body=@<file>.'
|
|
126
|
+
else
|
|
127
|
+
return 0
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
[ "$file" != '-' ] || deny 'This post reads its body from stdin, so the label scan cannot read it. Write the body to a file in an earlier call and pass it as -F body=@<file>.'
|
|
131
|
+
|
|
132
|
+
if ! command -v canon >/dev/null 2>&1; then
|
|
133
|
+
jq -nc '{hookSpecificOutput:{hookEventName:"PreToolUse",additionalContext:"The label scan did not run on this review or comment post, since canon is not on PATH. Run canon labels scan --body-file on the body once it is."}}'
|
|
134
|
+
exit 0
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# The payload's own cwd is the directory a relative path was written against.
|
|
138
|
+
# The project dir and this process's directory can each be a different worktree.
|
|
139
|
+
cwd=$(printf '%s' "$fields" | jq -r '.[0]')
|
|
140
|
+
[ -n "$cwd" ] || cwd=${CLAUDE_PROJECT_DIR:-.}
|
|
141
|
+
case "$file" in
|
|
142
|
+
/*) ;;
|
|
143
|
+
*) file="$cwd/$file" ;;
|
|
144
|
+
esac
|
|
145
|
+
|
|
146
|
+
# A body written by the same call does not exist when this hook runs, so the
|
|
147
|
+
# post would go out unread by the scan. Blocking is the only verdict that holds.
|
|
148
|
+
[ -f "$file" ] || deny "The body file $file does not exist yet, so the label scan cannot read it. Write the body in an earlier call, then post it."
|
|
149
|
+
|
|
150
|
+
# No --title: a review body carries none, and a title would grade it as an
|
|
151
|
+
# ordinary pull request body.
|
|
152
|
+
out=$(cd "$cwd" && canon labels scan --body-file "$file" 2>&1)
|
|
153
|
+
code=$?
|
|
154
|
+
[ "$code" -ne 0 ] || return 0
|
|
155
|
+
|
|
156
|
+
warnings=$(printf '%s\n' "$out" | grep -F '!')
|
|
157
|
+
[ -n "$warnings" ] || warnings=$out
|
|
158
|
+
if [ "$code" -eq 2 ]; then
|
|
159
|
+
deny "The label scan found text this post would publish. Fix the body file and post again.
|
|
160
|
+
$warnings"
|
|
161
|
+
fi
|
|
162
|
+
deny "The label scan could not grade $file (exit $code). Fix what it reports and post again.
|
|
163
|
+
$warnings"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
commands=$(printf '%s' "$cmd" | awk "$splitter")
|
|
167
|
+
while IFS= read -r line; do
|
|
168
|
+
[[ $line =~ $starts_gh_api ]] || continue
|
|
169
|
+
check "$line"
|
|
170
|
+
done <<<"$commands"
|
|
171
|
+
exit 0
|
|
@@ -27,6 +27,15 @@
|
|
|
27
27
|
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/scratch-guard.sh"
|
|
28
28
|
}
|
|
29
29
|
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"matcher": "Bash",
|
|
33
|
+
"hooks": [
|
|
34
|
+
{
|
|
35
|
+
"type": "command",
|
|
36
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/review-post-scan.sh"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
30
39
|
}
|
|
31
40
|
],
|
|
32
41
|
"PostToolUse": [
|
|
@@ -1,14 +0,0 @@
|
|
|
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
|
-
---
|
|
7
|
-
|
|
8
|
-
# Context entry standards
|
|
9
|
-
|
|
10
|
-
## Authority
|
|
11
|
-
|
|
12
|
-
- 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`.
|
|
13
|
-
- Before non-trivial work in a domain, read its `canon/context/<domain>.md` entry. Use `canon/context/index.md` to find it.
|
|
14
|
-
- Leave the entry conforming when work in its domain changes what it describes.
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
---
|
|
7
|
-
|
|
8
|
-
# Design standards
|
|
9
|
-
|
|
10
|
-
## Authority
|
|
11
|
-
|
|
12
|
-
- Follow the design standard for shape and content rules for canon/DESIGN.md. It is the single source. Read it with `canon standards design`.
|
|
@@ -1,12 +0,0 @@
|
|
|
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`.
|
|
@@ -1,13 +0,0 @@
|
|
|
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.
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
---
|
|
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
|
|
4
|
-
paths:
|
|
5
|
-
- '.claude/skills/**/SKILL.md'
|
|
6
|
-
- 'claude/skills/**/SKILL.md'
|
|
7
|
-
- '.claude/skills/**/REQUIREMENT.md'
|
|
8
|
-
- 'claude/skills/**/REQUIREMENT.md'
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# Claude skill standards
|
|
12
|
-
|
|
13
|
-
## Authority
|
|
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`.
|
|
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.
|
|
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.
|
|
18
|
-
- Re-read a skill body this session edited before invoking that skill again in the same session
|
|
19
|
-
- Do not read a resolved file path in a held body as evidence the body is current
|
|
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`.
|
|
@@ -1,13 +0,0 @@
|
|
|
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.
|
package/scripts/config.sh
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
if [ -z "${GITHUB_ORG:-}" ]; then
|
|
4
|
-
_origin_url="$(git config --get remote.origin.url 2>/dev/null || true)"
|
|
5
|
-
if [ -n "$_origin_url" ]; then
|
|
6
|
-
GITHUB_ORG="$(echo "$_origin_url" | sed -nE 's#^(git@github\.com:|https://github\.com/)([^/]+)/.+#\2#p')"
|
|
7
|
-
fi
|
|
8
|
-
unset _origin_url
|
|
9
|
-
fi
|
|
10
|
-
|
|
11
|
-
export GITHUB_ORG="${GITHUB_ORG:-}"
|
package/scripts/lib/gov.sh
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
# Sole survivor of the gov lib. Called once per rule file inside install and
|
|
4
|
-
# sandbox loops, where routing through the CLI would cost a process per file.
|
|
5
|
-
|
|
6
|
-
rule_subdir() {
|
|
7
|
-
local src="$1"
|
|
8
|
-
local rules_root="${2:-$PROJECT_ROOT/governance/rules}"
|
|
9
|
-
local rel="${src#"$rules_root/"}"
|
|
10
|
-
local subdir
|
|
11
|
-
subdir=$(dirname "$rel")
|
|
12
|
-
[ "$subdir" = "." ] && subdir=""
|
|
13
|
-
echo "$subdir"
|
|
14
|
-
}
|