@erclx/canon 5.8.0 → 5.9.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/git-followup/SKILL.md +1 -1
- package/claude/skills/git-pr/references/evidence.md +1 -1
- package/claude/skills/review-craft/REQUIREMENT.md +8 -2
- package/claude/skills/review-craft/SKILL.md +63 -7
- package/claude/skills/review-craft/references/adopted.md +49 -0
- package/docs/agents/commands.md +2 -0
- package/docs/agents/hooks.md +56 -0
- package/docs/agents/index.md +1 -0
- package/docs/agents/install-and-sync.md +1 -1
- package/docs/agents/markdown-audit.md +1 -1
- package/docs/agents/pr-evidence.md +33 -17
- package/docs/agents/records-push.md +1 -1
- package/docs/agents/records-validate.md +1 -1
- package/docs/agents/scripting.md +6 -0
- package/docs/agents/tasks-archive.md +4 -2
- package/docs/agents/tasks.md +1 -1
- package/docs/agents/worktrees.md +2 -2
- package/docs/target/scenarios.md +2 -2
- package/package.json +1 -1
- package/src/claude/skills-audit.ts +1 -0
- package/src/cli.ts +2 -0
- package/src/commands/claude.ts +1 -1
- package/src/commands/docs.ts +51 -11
- package/src/commands/hooks.ts +67 -0
- package/src/commands/pr.ts +146 -76
- package/src/commands/standards.ts +33 -2
- package/src/commands/tasks.ts +45 -0
- package/src/commands/tooling.ts +3 -3
- package/src/commands/upgrade.ts +3 -2
- package/src/commands/worktrees.ts +4 -3
- package/src/docs/list.ts +103 -0
- package/src/docs/read.ts +63 -19
- package/src/gov/stacks.ts +1 -1
- package/src/help.ts +2 -0
- package/src/hooks/post-merge.ts +331 -0
- package/src/pr/evidence.ts +21 -0
- package/src/records/validate.ts +1 -38
- package/src/standards/list.ts +33 -0
- package/src/standards/read.ts +13 -0
- package/src/standards/scope.ts +56 -0
- package/src/sync/check.ts +38 -4
- package/src/tasks/archive.ts +168 -3
- package/src/tooling/inject.ts +4 -3
- package/src/tooling/manifest.ts +62 -8
- package/src/tooling/scan.ts +4 -3
- package/src/tooling/stamp.ts +11 -2
- package/src/tooling/subfolder.ts +7 -3
- package/tooling/astro/reference.md +1 -1
- package/tooling/base/configs/scripts/verify.json +3 -0
- package/tooling/base/configs/scripts/verify.sh +1 -110
- package/tooling/base/configs/scripts/verify.ts +378 -0
- package/tooling/base/manifest.toml +1 -0
- package/tooling/base/reference.md +5 -3
- package/tooling/go/configs/scripts/verify.json +10 -0
- package/tooling/go/reference.md +5 -4
- package/tooling/php/configs/scripts/verify.json +7 -0
- package/tooling/php/reference.md +5 -4
- package/tooling/python/configs/scripts/verify.json +4 -0
- package/tooling/python/reference.md +3 -3
- package/tooling/web/configs/scripts/verify.json +3 -0
- package/tooling/web/reference.md +3 -3
- package/scripts/docs/list.sh +0 -184
- package/scripts/lib/frontmatter.sh +0 -30
- package/scripts/standards/list.sh +0 -115
- package/src/commands/pass-through.ts +0 -39
- package/tooling/go/configs/scripts/verify.sh +0 -142
- package/tooling/php/configs/scripts/verify.sh +0 -147
- package/tooling/python/configs/scripts/verify.sh +0 -141
- package/tooling/web/configs/scripts/verify.sh +0 -153
|
@@ -32,7 +32,7 @@ A missing tracking ref is no longer a guard. An open pull request proves the bra
|
|
|
32
32
|
5. Post the evidence comparison, on every invocation including `reply-owned`, since this comment is not the reply step 8 owns. Run `canon pr evidence <number> --json`, resolving `<number>` from `gh pr view --json number`, and read `reason` on the record rather than the exit code.
|
|
33
33
|
- `no-evidence`: nothing changed under an `evidence/` segment this push. Say nothing and move on.
|
|
34
34
|
- `ok`: write `body` to `.canon/tmp/pr/evidence/body-<number>.md` at the main worktree root (resolved the way `session-worktree` does), then post the comment with `gh pr comment <number> --body-file <main-root>/.canon/tmp/pr/evidence/body-<number>.md` when the record carries no `commentId`, or edit the existing one in place with `gh api -X PATCH repos/{owner}/{repo}/issues/comments/<commentId> -F body=@<main-root>/.canon/tmp/pr/evidence/body-<number>.md` when it does. Clean up the tmp file only after the call reports success.
|
|
35
|
-
- Any other reason is one of the mirrored git refusals (`gh-missing`, `gh-failed`, `no-base`, `unreadable-
|
|
35
|
+
- Any other reason is one of the mirrored git refusals (`gh-missing`, `gh-failed`, `no-base`, `unreadable-changes`, `would-empty`). Report it and continue without stopping the chain.
|
|
36
36
|
6. Check for existing review comments: `gh api 'repos/{owner}/{repo}/pulls/<number>/comments' --jq 'length'`, resolving `<number>` from `gh pr view --json number`.
|
|
37
37
|
7. Sync the body and title on every invocation, before the routing below decides on the reply.
|
|
38
38
|
- Run `gh pr view --json url,title,body` and update the body with `gh pr edit --body` when the new commit changes scope, and the title with `gh pr edit --title` when the scope shifted enough to make it inaccurate.
|
|
@@ -64,7 +64,7 @@ gh api -X PATCH repos/{owner}/{repo}/issues/comments/<commentId> -F body=@<main-
|
|
|
64
64
|
|
|
65
65
|
Delete the handoff file once that call reports success, per the cleanup below, since the checklist now lives on the pull request. Clean up the tmp body file the same way.
|
|
66
66
|
|
|
67
|
-
Any other `reason` is one of the mirrored git refusals (`gh-missing`, `gh-failed`, `no-base`, `unreadable-
|
|
67
|
+
Any other `reason` is one of the mirrored git refusals (`gh-missing`, `gh-failed`, `no-base`, `unreadable-changes`, `would-empty`). Report it and move on without stopping the chain: a branch that carries no evidence images most of the time should not fail here on a transient git or `gh` read. Fall through to the checklist step, which posts the checklist alone rather than losing it to a transient read.
|
|
68
68
|
|
|
69
69
|
## Post the UI checklist alone
|
|
70
70
|
|
|
@@ -13,10 +13,16 @@ The miss is measured. On a seeded branch that deleted a dirty-tree guard while t
|
|
|
13
13
|
|
|
14
14
|
Rendered output is the larger hole. `git-pr` posts an `## Evidence` comment through `canon pr evidence`, carrying base and head screenshots and a checklist a reviewer ticks. The pull request review named that heading only in its heading set, and no step opened an image. On a seeded pull request whose narrow head screenshot shows the navigation running off the edge while a ticked box claims it collapses, the review filed the finding off the stylesheet diff and said it had not rendered the page. On a styling change with no screenshot at all, on a project whose trunk keeps them, it did not mention the absence.
|
|
15
15
|
|
|
16
|
+
The axes also said nothing about a change that passes by lowering the bar it has to clear, such as a test skipped, a threshold moved, or a path excluded from a checker, nothing about performance, and one sentence about design with no procedure behind it. A refactor that only moved complexity read as a design gain, and a removal could call code dead without reading why it was added. The body carried no excuses, red flags, or closing checklist, so a session about to stop at the hunk met no answer first.
|
|
17
|
+
|
|
16
18
|
## Must
|
|
17
19
|
|
|
18
20
|
- Open with the one fact the change's safety rests on, read on a ladder of asserted, pointed at, walked through, tested, and reproduced
|
|
19
|
-
- State the axes in reading order: design and scope, correctness and edge cases, what breaks outside the diff, tests, security, rendered output, developer experience and operations, and executable prose
|
|
21
|
+
- State the axes in reading order: design and scope, correctness and edge cases, what breaks outside the diff, tests, guard the bar, performance, security, rendered output, developer experience and operations, and executable prose
|
|
22
|
+
- Carry the design review as an ordered procedure in the body, stack-neutral, plus the scope checks for a change carrying two concerns, complexity moved rather than reduced, and a removal that has not read why the code existed
|
|
23
|
+
- Name a lowered bar as a finding unless the diff states why, and accept a suppression whose stated reason holds
|
|
24
|
+
- Ask a claimed speedup for repeated measurements on both sides, and read a result inside the noise as an argument to revert
|
|
25
|
+
- Close with the practice skill's excuses, red flags, and closing checklist, and record every external source in `references/adopted.md`
|
|
20
26
|
- Require every consumer of a changed contract to be listed and checked, and every doc the diff touches to be read whole for a claim the change made false
|
|
21
27
|
- State a design finding as a concrete cost, so it survives the procedure's filter against subjective suggestions
|
|
22
28
|
- Carry the evidence bar: confirm against the file at the reviewed head, quote a cited rule, name the breaking input, and drop what a gate owns or what rests on unread state
|
|
@@ -24,7 +30,7 @@ Rendered output is the larger hole. `git-pr` posts an `## Evidence` comment thro
|
|
|
24
30
|
- Open the evidence images on a rendered change through `git show` at the row's own sha, never through the embedded address, within a stated read budget
|
|
25
31
|
- Test each ticked checklist box against an opened image, and name a ticked box no image covers as untested
|
|
26
32
|
- Flag a rendered change that carries no screenshot, as a finding where the trunk already keeps them and as a question elsewhere
|
|
27
|
-
- Read the posted comment rather than running `canon pr evidence`, which
|
|
33
|
+
- Read the posted comment rather than running `canon pr evidence`, which renders a new body from the pull request's files and can differ from the comment already posted
|
|
28
34
|
|
|
29
35
|
## Must not
|
|
30
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review-craft
|
|
3
|
-
description: Carries what a code review looks for and how much evidence a finding needs, from design and scope through breakage outside the diff, developer experience, executable prose, stale docs, security classes, and rendered output checked against the screenshots a pull request carries. Use when reviewing a change, when a review procedure loads it for its axes, or when asked "what should a review look for", "what does a good review check", "how sure do I need to be before flagging this", or "did the review look at the screenshots". Do NOT use to run a review, post it, or grade its findings, which is `review-branch` for local work and `review-pr` for a pull request, and do NOT use for a deliberate full security audit, which is the built-in `security-review`.
|
|
3
|
+
description: Carries what a code review looks for and how much evidence a finding needs, from design and scope through breakage outside the diff, a test or check the change weakens to pass, performance, developer experience, executable prose, stale docs, security classes, and rendered output checked against the screenshots a pull request carries. Use when reviewing a change, when a review procedure loads it for its axes, or when asked "what should a review look for", "what does a good review check", "how sure do I need to be before flagging this", or "did the review look at the screenshots". Do NOT use to run a review, post it, or grade its findings, which is `review-branch` for local work and `review-pr` for a pull request, and do NOT use for a deliberate full security audit, which is the built-in `security-review`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Review craft
|
|
@@ -23,16 +23,43 @@ A risky claim resting on assertion alone is where the review spends its reading.
|
|
|
23
23
|
|
|
24
24
|
## Read the axes in this order
|
|
25
25
|
|
|
26
|
-
An axis whose condition the diff meets is not optional. When
|
|
26
|
+
An axis whose condition the diff meets is not optional. When the security or the rendered output axis names a reference and its condition holds, read that reference before moving to the next axis, however clear the diff looks without it.
|
|
27
27
|
|
|
28
|
-
1. **Design and scope.** Does the change do one thing, is it more complex than the problem needs, and does it build what nobody asked for? State a design finding as a concrete cost, such as a second path nobody calls, a layer with one caller, or a flag no caller passes, never as a preference.
|
|
28
|
+
1. **Design and scope.** Does the change do one thing, is it more complex than the problem needs, and does it build what nobody asked for? State a design finding as a concrete cost, such as a second path nobody calls, a layer with one caller, or a flag no caller passes, never as a preference. Walk it in the order `Walk the design` below gives.
|
|
29
29
|
2. **Correctness and edge cases.** The empty input, the boundary value, the refused call, the second run, the concurrent run, and the error path a caller actually meets.
|
|
30
30
|
3. **What breaks outside the diff.** List every consumer of a changed contract, whether a function signature, a file format, a flag, an output shape, or a heading another tool matches, and check the change against each one. Then list each behavior the diff removes or changes, such as a guard, a refusal, a default, or an output, and search every doc the diff touches, in full rather than at the hunk, for a sentence still promising the old behavior. File that sentence as a finding against the doc, quoting it and naming its section, since the doc is what goes wrong for the next reader.
|
|
31
31
|
4. **Tests.** Hand each test the change adds or edits to `canon:test-craft` and its final filter. Report it rather than proceeding silently when that skill does not resolve.
|
|
32
|
-
5. **
|
|
33
|
-
6. **
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
5. **Guard the bar.** A change can pass by lowering its own bar: a threshold moved, a test skipped, deleted, or emptied of assertions, an inline suppression, a path dropped from a lint or type run, a stub that throws or an empty catch where behavior was promised, or a new config exception. Each is a finding unless the diff says why, and a suppression whose stated reason holds is the author's call.
|
|
33
|
+
6. **Performance.** A query or a remote call inside a loop, a read with no bound on its size, and a list endpoint with no pagination. A claimed speedup needs a before and an after measured the same way and repeated past run-to-run noise. One run on each side is an anecdote, and a result inside the noise argues for reverting the added complexity.
|
|
34
|
+
|
|
35
|
+
### Axes a condition opens
|
|
36
|
+
|
|
37
|
+
Read each of these when the diff reaches what it names.
|
|
38
|
+
|
|
39
|
+
7. **Security.** Read `${CLAUDE_SKILL_DIR}/references/security.md` when the diff runs a command, writes a file, handles input from outside the process, touches a secret, adds a dependency, or changes a default a consumer installs.
|
|
40
|
+
8. **Rendered output.** Read `${CLAUDE_SKILL_DIR}/references/ui.md` when the diff touches a file that paints, such as markup, a stylesheet, a component, a template, or a design token, or when the pull request carries an evidence comment. A finding read off a stylesheet diff says what the author changed, and only the screenshot says what a user sees, so the reference opens the images and checks whether any exist.
|
|
41
|
+
9. **Developer experience and operations.** Ports, hooks, scripts, config, and scaffold defaults a consumer installs. A new required step nobody documented, a default that fails on a clean machine, or a hook that slows every commit is a finding even when the code is correct.
|
|
42
|
+
10. **Executable prose.** A skill body, a rule, or a prompt is instructions a model runs. Read it the way you read code: a step that races another, a precondition it never states, a branch with no exit, and two instructions that contradict each other are bugs.
|
|
43
|
+
|
|
44
|
+
### Walk the design
|
|
45
|
+
|
|
46
|
+
Walk the design axis in this order, and skip a step the change gives nothing to read:
|
|
47
|
+
|
|
48
|
+
1. Name the next change this code is likely to need, and count the files and units it would touch. A count that grows with each new case is the cost to report.
|
|
49
|
+
2. State what each new unit hides from its callers. A unit that passes its arguments straight through to another hides nothing and is a layer with no work.
|
|
50
|
+
3. Find each rule or constant the change states, and check that it lives in one place. The same rule in two places drifts the first time one copy changes.
|
|
51
|
+
4. Look for a parameter that switches what a function does, a chain of calls reaching through one object into another, and a read or write of global state. Each ties a caller to internals it should not know.
|
|
52
|
+
5. Ask each changed file for its one reason to change. A file that two unrelated requests would both edit carries two concerns.
|
|
53
|
+
6. List what the code reads without being handed it, such as the clock, the environment, the filesystem, or a random source. A hidden input is what makes the code hard to test and to reuse.
|
|
54
|
+
7. Match each pattern or abstraction the change introduces to a problem the code has today. One built for a case nobody has asked for is speculative.
|
|
55
|
+
8. Check that the rules of the domain sit apart from the code that moves data in and out, so a change of transport or storage leaves them untouched.
|
|
56
|
+
9. Read names, unexplained literal numbers, errors caught and dropped, and side effects a function's name does not declare.
|
|
57
|
+
|
|
58
|
+
Three scope checks follow the walk:
|
|
59
|
+
|
|
60
|
+
- When a change carries two concerns that could land as two changes, name the split. Size is no finding on its own, since one concern can run long.
|
|
61
|
+
- On a refactor, count the concepts a reader must hold before and after. Complexity moved into a new file or function is moved, not reduced, so report it as such rather than as a design gain.
|
|
62
|
+
- Before calling removed code dead, state why it existed. Read `git blame` and the commit that added it, since a guard nobody remembers is often still guarding something.
|
|
36
63
|
|
|
37
64
|
## Hold every finding to the evidence bar
|
|
38
65
|
|
|
@@ -42,6 +69,35 @@ An axis whose condition the diff meets is not optional. When axis 5 or 6 names a
|
|
|
42
69
|
- Drop what a linter, a type checker, or a gate in the project already owns. The gate reports it with more certainty than a review can.
|
|
43
70
|
- Drop a finding resting on state you did not read. Say what you would need to read instead.
|
|
44
71
|
|
|
72
|
+
## Excuses and rebuttals
|
|
73
|
+
|
|
74
|
+
| Excuse | Rebuttal |
|
|
75
|
+
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| The tests pass, so the change works | Read what the diff did to the tests first. A test skipped, deleted, or stripped of its assertions passes by proving nothing. |
|
|
77
|
+
| The hunk reads clean, so the review is done | The hunk is where the author looked. A consumer, a doc a section away, and the commit that added a removed guard are not in it. |
|
|
78
|
+
| The checker passes on the branch | Read the checker's config and the inline suppressions in the diff. A path excluded from the run passes by not being read. |
|
|
79
|
+
| It was split into smaller functions, so it is simpler | Count the concepts a reader holds before and after. Moved complexity reads as simpler only to the person who moved it. |
|
|
80
|
+
| Nothing calls it, so deleting it is safe | Read why it was added. Code nobody calls today can still be guarding a path nobody exercised in the change. |
|
|
81
|
+
| The description says it is faster | A claimed speedup with no measurement, or one run each side, is asserted. Ask for runs that beat the noise. |
|
|
82
|
+
| The change only touches styles, so the screenshots can wait | A stylesheet diff says what changed and only the image says what a user sees. Open them, or say none exist. |
|
|
83
|
+
|
|
84
|
+
## Red flags
|
|
85
|
+
|
|
86
|
+
- You are about to call the change clean having read only the lines it changed.
|
|
87
|
+
- A threshold, an exclude list, or a skip marker changed and you have not found the sentence explaining it.
|
|
88
|
+
- You are approving a refactor because it has more, smaller pieces.
|
|
89
|
+
- You are calling code dead without having read the commit that added it.
|
|
90
|
+
- A performance claim rests on a number you cannot trace to a repeated measurement.
|
|
91
|
+
- An axis condition held and you moved past it without reading the reference it names.
|
|
92
|
+
|
|
93
|
+
## Before handing over
|
|
94
|
+
|
|
95
|
+
- Every axis whose condition the diff meets has a finding or a stated reason it found none.
|
|
96
|
+
- Every changed threshold, skip, suppression, and exclusion in the diff is either a finding or paired with the reason the diff gives.
|
|
97
|
+
- Every removal the review accepted names why the removed code existed.
|
|
98
|
+
- Every design finding states a concrete cost, and every refactor finding says whether complexity was reduced or moved.
|
|
99
|
+
- Every finding was confirmed against the file at the reviewed head and names the input or state that breaks.
|
|
100
|
+
|
|
45
101
|
## What this delegates
|
|
46
102
|
|
|
47
103
|
- Which files a pass reads, the high-signal filter, the severity ladder, and where findings go: `review-branch` for local work and `review-pr` for a pull request
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Adopted and declined
|
|
3
|
+
description: Which external review patterns this skill adopted, which it declined, and why, so a later session extends the position instead of re-deriving it
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Adopted and declined
|
|
7
|
+
|
|
8
|
+
External sources were read and filtered rather than imported. This file is the record. A later session extending the skill adds to it rather than re-arguing an item already settled here.
|
|
9
|
+
|
|
10
|
+
## Adopted
|
|
11
|
+
|
|
12
|
+
### Design and scope
|
|
13
|
+
|
|
14
|
+
**The design procedure.** The nine ordered steps under the design axis draw on four sources, each restated without a language, a framework, or a transport so the walk holds on any stack.
|
|
15
|
+
|
|
16
|
+
- Ousterhout's deep modules, information hiding, and pass-through methods ([A Philosophy of Software Design](https://web.stanford.edu/~ouster/cgi-bin/book.php)): the step asking what each unit hides and flagging a unit that passes its arguments straight through.
|
|
17
|
+
- Parnas's criterion that a module boundary hides a decision likely to change ([On the Criteria To Be Used in Decomposing Systems into Modules](https://dl.acm.org/doi/10.1145/361598.361623)): the opening step naming the next likely change and counting what it touches.
|
|
18
|
+
- Beck's design rules as Fowler records them, especially no duplication and fewest elements ([BeckDesignRules](https://martinfowler.com/bliki/BeckDesignRules.html)): the one-place step and the step matching each pattern to a problem the code has today.
|
|
19
|
+
- Google's reviewer guide on design, complexity, and over-engineering ([What to look for in a code review](https://google.github.io/eng-practices/review/reviewer/looking-for.html)): the frame for the whole walk, and the reason a speculative abstraction is a finding.
|
|
20
|
+
|
|
21
|
+
The procedure stays in the body rather than a reference, since every review reads the design axis and a reference would cost a read on every run.
|
|
22
|
+
|
|
23
|
+
**Change sizing as a split.** Google's [small CLs](https://google.github.io/eng-practices/review/developer/small-cls.html) asks for one self-contained change. Adopted as the scope check that names a split when two concerns could land as two changes. From `addyosmani/agent-skills@2686b620`, `skills/code-review-and-quality/SKILL.md`, the same check.
|
|
24
|
+
|
|
25
|
+
**Relocate versus reduce.** From the same `code-review-and-quality` skill: a refactor counts the concepts a reader must hold before and after. Adopted as the scope check that reports moved complexity as moved.
|
|
26
|
+
|
|
27
|
+
**Chesterton's Fence.** From `addyosmani/agent-skills@2686b620`, `skills/code-simplification/SKILL.md`: read why code exists before removing it. Adopted as the scope check that reads `git blame` and the commit that added a removed path before calling it dead.
|
|
28
|
+
|
|
29
|
+
### Guard the bar
|
|
30
|
+
|
|
31
|
+
From `addyosmani/agent-skills@2686b620`, `skills/constraint-driven-development/SKILL.md`: a change must not pass by weakening the constraint it has to meet. Adopted as its own axis after tests, listing the moved threshold, the skipped or hollowed test, the inline suppression, the excluded path, the throwing stub, the empty catch, and the new config exception. The clause accepting a suppression whose stated reason holds is this skill's own, since a legitimate suppression with its reason beside it would otherwise report noise the procedures' filter then drops.
|
|
32
|
+
|
|
33
|
+
### Performance
|
|
34
|
+
|
|
35
|
+
From `addyosmani/agent-skills@2686b620`, `skills/code-review-and-quality/SKILL.md` for the axis itself, a call inside a loop and an unbounded fetch, and `skills/performance-optimization/SKILL.md` for keep or revert: measure before and after the same way, and a neutral result argues for removing the added complexity. The repeated-run requirement against noise is stated here so one run on each side reads as an anecdote.
|
|
36
|
+
|
|
37
|
+
### Closing sections
|
|
38
|
+
|
|
39
|
+
The excuses, red flags, and closing checklist follow the practice skill shape every practice skill in this toolkit carries, taken from `addyosmani/agent-skills@2686b620`, [`docs/skill-anatomy.md`](https://github.com/addyosmani/agent-skills/blob/2686b620fc1fed2e8f60c704839c766b8594c6b6/docs/skill-anatomy.md). Each excuse row rebuts a reason a review stops at the hunk, such as passing tests against a test the diff skipped, rather than an objection nobody raised.
|
|
40
|
+
|
|
41
|
+
## Declined
|
|
42
|
+
|
|
43
|
+
**Google's approval standard.** "Approve a change once it definitely improves the overall code health" is a grading rule. Grading and posting belong to the procedures that load this skill, which each own a severity ladder, so the standard stays out of the body.
|
|
44
|
+
|
|
45
|
+
**A fixed line count for sizing.** Google's small-CL page sets no hard limit, and one concern regularly runs past a hundred lines here. A number would fire on nearly every branch, so the split is stated by concern instead.
|
|
46
|
+
|
|
47
|
+
**Named structural remedies as a catalog.** A list of refactorings to propose reads as a menu a session fills in. A design finding states the cost, and the remedy is the author's.
|
|
48
|
+
|
|
49
|
+
**The web and database tables from `performance-optimization`.** They name a stack, and the axis stays stack-neutral until a target asks for a per-stack reference.
|
package/docs/agents/commands.md
CHANGED
|
@@ -40,6 +40,7 @@ One row per top-level command, in the order `canon --help` prints them. A domain
|
|
|
40
40
|
| `canon records <verb>` | Validate, size, prune, and back up the session records |
|
|
41
41
|
| `canon sessions <verb>` | Resolve live sessions to worktree and branch, and move one between machines |
|
|
42
42
|
| `canon worktrees <verb>` | Report and remove worktrees whose pull request merged |
|
|
43
|
+
| `canon hooks <verb>` | Run the steps a git hook drives |
|
|
43
44
|
| `canon autoship <verb>` | Decide whether a changed set needs the review pass |
|
|
44
45
|
| `canon pr <verb>` | Read a pull request's diff, head, checks, and review, and post its evidence |
|
|
45
46
|
| `canon feedback` | Write toolkit feedback from stdin, or open an issue with `--github` |
|
|
@@ -100,6 +101,7 @@ Each domain exposes a consistent shape where applicable: `list`, `install`, `syn
|
|
|
100
101
|
- `records`, in `records.md`: `validate`, `migrate`, `ordinal`, `size`, `stale`, `prune-tmp`, `push`, `pull`
|
|
101
102
|
- `sessions`, in `sessions.md`: `list`, `export`, `import`
|
|
102
103
|
- `worktrees`, in `worktrees.md`: `list`, `reclaim`
|
|
104
|
+
- `hooks`, in `hooks.md`: `post-merge`
|
|
103
105
|
- `autoship`, in `review-classification.md`: `classify`
|
|
104
106
|
- `pr`, in `pr-reads.md`: `key-changes`, `head`, `checks`, `review-state`, `evidence`, `preview`, `local`
|
|
105
107
|
- `audits`, in `audits.md`: `run`, `list`
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Hooks
|
|
3
|
+
description: Running the post-merge steps through one verb, the order and the child call behind each, the root and working directory each step reads, the skip switches, and the line each record maps to
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hooks
|
|
7
|
+
|
|
8
|
+
`canon hooks post-merge` runs the steps a git `post-merge` hook drives, so the hook itself stays a guard plus one call. It reads which pull requests the merge brought in, runs each step as a child `canon` call with `--json`, and prints one line per outcome worth reporting.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
canon hooks post-merge
|
|
12
|
+
canon hooks post-merge --root "$root"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Option | Behavior |
|
|
16
|
+
| --------------- | ---------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `--root <path>` | Main worktree root holding the board and the records, resolved from `git worktree list` when omitted |
|
|
18
|
+
|
|
19
|
+
Exit codes: `0` on every path, since a hook failing aborts nothing useful. Lines go to stderr, and stdout stays empty. An ordinary merge prints nothing.
|
|
20
|
+
|
|
21
|
+
## The steps
|
|
22
|
+
|
|
23
|
+
The steps run in this order, and the verb's `--help` lists the child calls from the same table it runs them from.
|
|
24
|
+
|
|
25
|
+
| Step | Child call | Runs from |
|
|
26
|
+
| ------------------------ | ------------------------------------------------------------- | ---------------- |
|
|
27
|
+
| Archive the merged task | `canon tasks archive --pull-request <n> --root <root> --json` | the caller's cwd |
|
|
28
|
+
| Push the records | `canon records push --root <root> --json` | the caller's cwd |
|
|
29
|
+
| Reclaim merged worktrees | `canon worktrees reclaim --json` | the caller's cwd |
|
|
30
|
+
| Reinstall the CLI | `canon upgrade --json`, under `CANON_NON_INTERACTIVE=1` | the caller's cwd |
|
|
31
|
+
| Update the plugin cache | `canon claude plugin-update --json` | the caller's cwd |
|
|
32
|
+
|
|
33
|
+
The archive runs once per pull request number, read as the trailing `(#NNN)` on every subject in `ORIG_HEAD..HEAD`, or on the tip alone when the merge left no `ORIG_HEAD`.
|
|
34
|
+
|
|
35
|
+
The reclaim takes no `--root`. It refuses the worktree its own working directory names, so a root would let a pull inside a linked worktree remove the worktree it stands in. The archive and the push take the root because the board and the records live at the main worktree root.
|
|
36
|
+
|
|
37
|
+
Each step is a separate process rather than an in-process call, so the plugin update runs the binary the upgrade just installed. Every child, and every `git` read, runs with the repository variables a hook exports stripped, since `GIT_DIR` beats `-C`.
|
|
38
|
+
|
|
39
|
+
## Skip switches
|
|
40
|
+
|
|
41
|
+
| Variable | Skips |
|
|
42
|
+
| ---------------------------- | ----------------------- |
|
|
43
|
+
| `CANON_SKIP_RECLAIM=1` | the worktree reclaim |
|
|
44
|
+
| `CANON_SKIP_UPGRADE=1` | the CLI reinstall |
|
|
45
|
+
| `CANON_SKIP_PLUGIN_UPDATE=1` | the plugin cache update |
|
|
46
|
+
|
|
47
|
+
Any non-empty value skips the step, and each switch skips its own step only.
|
|
48
|
+
|
|
49
|
+
## What prints
|
|
50
|
+
|
|
51
|
+
Each line is decided from the child's parsed record. A child producing no parseable record is an older binary carrying no such subcommand, and that step stays quiet.
|
|
52
|
+
|
|
53
|
+
- Archive: the archived task and its number, plus a `Links not repointed in:` line naming the files in a non-empty `relinkFailed`, or the refusal's `message` with a pointer to the task board. A record carrying no `relinkFailed` reads as empty. `no-match` and `no-board` stay quiet.
|
|
54
|
+
- Records push: the count of changed paths when above zero, or the refusal reason. `no-repository` stays quiet.
|
|
55
|
+
- Reclaim: the removal count and the failure count, both read from the fields whatever the exit, or an unreadable reason. `gh-missing` stays quiet.
|
|
56
|
+
- Upgrade and plugin update: the record's `message` on any state but `current`, or on a failure. The plugin update stays quiet on `no-claude` and `no-plugin`.
|
package/docs/agents/index.md
CHANGED
|
@@ -24,6 +24,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
|
|
|
24
24
|
- [Drift surfaces](drift-surfaces.md): The six sections canon sync --check reports beside the per-domain scan, being seeds, superseded artifacts, unmigrated domains, new rules and skills, and the reverse walk, with the managed-target gate and what counts toward the exit code
|
|
25
25
|
- [Driver](driver.md): Walking a page through named interactions, the probe catalog and the false finding each one carries, why viewport heights are never defaulted, and what each refusal reports
|
|
26
26
|
- [Merge gate](gate.md): Running the gate this repository verifies a branch with, what the stage table holds and what stays a script, how the changed set scopes three stages, and why a stage that cannot read its input reports rather than passing
|
|
27
|
+
- [Hooks](hooks.md): Running the post-merge steps through one verb, the order and the child call behind each, the root and working directory each step reads, the skip switches, and the line each record maps to
|
|
27
28
|
- [Indexes](indexes.md): Flags, exit codes, and JSON shape for canon indexes regen and canon indexes list, plus when regen auto-stages what it rewrote
|
|
28
29
|
- [Install and sync](install-and-sync.md): What each install and sync verb writes, refuses, or leaves alone, and how drift is attributed in a target project
|
|
29
30
|
- [Intake](intake.md): Reading intake folder counts and items, the three read states an item can be in, landing a batch of selections in one cluster, the refusal reasons, and why a call is scoped to one file
|
|
@@ -74,7 +74,7 @@ There is no `canon standards sync` and no `canon standards install`. The corpus
|
|
|
74
74
|
installs into no project, so the domain has nothing in a target to reconcile.
|
|
75
75
|
`canon standards <name>` prints one, resolving `standards/` at the working root
|
|
76
76
|
and then the corpus inside the package, and `canon standards list --json` carries
|
|
77
|
-
the catalog.
|
|
77
|
+
the catalog, each entry labeled by the root it was read from.
|
|
78
78
|
|
|
79
79
|
## Tooling diff and sync
|
|
80
80
|
|
|
@@ -87,7 +87,7 @@ A hit the closed set could not separate from correct prose was the case with no
|
|
|
87
87
|
|
|
88
88
|
### Where the rules are enforced
|
|
89
89
|
|
|
90
|
-
Five surfaces apply the ban set and four of them go through this verb. `.claude/hooks/standards-audit.sh` runs it against a single file after each markdown edit, the seed copy a project installs does the same, the `Markdown bans` stage in `canon gate run` runs it across the whole corpus before this repository's own push, and the same-named stage in every stack
|
|
90
|
+
Five surfaces apply the ban set and four of them go through this verb. `.claude/hooks/standards-audit.sh` runs it against a single file after each markdown edit, the seed copy a project installs does the same, the `Markdown bans` stage in `canon gate run` runs it across the whole corpus before this repository's own push, and the same-named stage in the one verify runner every stack shares, `scripts/verify.ts`, runs it across a scaffolded project's tracked markdown before its own push. Each hook parsed its own copy of the word bans in awk before that, which left a British spelling passing at edit time and failing the push with nothing in between explaining the difference.
|
|
91
91
|
|
|
92
92
|
The seed copy moved onto the verb when the sets became data, since its awk had nothing left to parse. It resolves one runner where the toolkit copy resolves two, looking for no checkout source, and a machine carrying no `canon` gets a report naming the binary to install rather than a silent pass. `scripts/core/check-seed-independence.sh` scopes its walk to markdown and leaves the seed hooks outside it, which its own comment records as deliberate.
|
|
93
93
|
|
|
@@ -6,10 +6,10 @@ description: What canon pr evidence compares, the marker that lets it edit its o
|
|
|
6
6
|
# The pull request evidence comparison
|
|
7
7
|
|
|
8
8
|
`canon pr evidence` renders one comment naming every changed image under an
|
|
9
|
-
`evidence/` path segment, comparing each against the pull request's merge base
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
`evidence/` path segment, comparing each against the pull request's merge base.
|
|
10
|
+
`git-pr` posts it when a pull request opens or is edited, and `git-followup`
|
|
11
|
+
posts it again after every later push, so a reviewer never has to open Files
|
|
12
|
+
Changed to see what a case looked like before and after.
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
canon pr evidence
|
|
@@ -55,9 +55,9 @@ image or marks the case new.
|
|
|
55
55
|
| `gh-failed` | `gh` could not answer for this repository or branch, or read its comments. |
|
|
56
56
|
| `no-branch` | The pull request carries no head branch name. |
|
|
57
57
|
| `no-object-head` | The pull request object reported no head commit. |
|
|
58
|
-
| `no-base` |
|
|
59
|
-
| `unreadable-
|
|
60
|
-
| `
|
|
58
|
+
| `no-base` | GitHub reported no merge base for the pull request. |
|
|
59
|
+
| `unreadable-changes` | GitHub could not list what the pull request changed. |
|
|
60
|
+
| `would-empty` | The render holds no cases and the marked comment holds some. No body. |
|
|
61
61
|
|
|
62
62
|
`no-evidence` is not a refusal a caller reports. A project on a stack that
|
|
63
63
|
carries no evidence path, such as `base` or `python`, hits this reason on
|
|
@@ -83,16 +83,32 @@ reporting the fields absent.
|
|
|
83
83
|
|
|
84
84
|
## What `[number]` selects, and what it does not
|
|
85
85
|
|
|
86
|
-
Naming a number picks
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
head
|
|
86
|
+
Naming a number picks the pull request the whole record describes: its head
|
|
87
|
+
commit, its merge base, its changed set, and the marked comment to edit in
|
|
88
|
+
place. The changed paths and whether each was added or modified come from the
|
|
89
|
+
pull request's files endpoint, paged to the end, and the merge base comes from
|
|
90
|
+
the compare endpoint, so the record is the same from any checkout. A run from
|
|
91
|
+
the main worktree against a branch built elsewhere renders that branch's
|
|
92
|
+
comparison.
|
|
93
|
+
|
|
94
|
+
The trade is that the verb no longer sees evidence that is uncommitted or
|
|
95
|
+
unpushed, which the comment's head-pinned image links could not show anyway.
|
|
96
|
+
A path the pull request renamed or copied counts as added, since the new path
|
|
97
|
+
has no counterpart at the merge base, so a rename loses its before image and
|
|
98
|
+
renders `*(new)*`. A path the pull request removed is dropped, since it has no
|
|
99
|
+
head image to show. A failed files read refuses as `unreadable-changes` rather
|
|
100
|
+
than rendering a short set, and a missing merge base refuses as `no-base`.
|
|
101
|
+
|
|
102
|
+
## A render never empties a comment that carries cases
|
|
103
|
+
|
|
104
|
+
When the render holds no cases and the marked comment holds some, the verb
|
|
105
|
+
refuses `would-empty` and prints no body. A render with no cases cannot tell a
|
|
106
|
+
case removed on purpose from a short read, so the destructive direction needs a
|
|
107
|
+
person: they edit the comment directly. The record keeps `commentId` and the
|
|
108
|
+
carried fields, so the caller can still find the comment. A comment holding
|
|
109
|
+
only a preview or a checklist has no cases to lose and still re-renders `ok`.
|
|
110
|
+
`no-evidence` without a flag returns before any render, so the guard touches
|
|
111
|
+
only `--preview`, `--local`, and `--checklist` calls.
|
|
96
112
|
|
|
97
113
|
## One comment, found by its own marker
|
|
98
114
|
|
|
@@ -58,4 +58,4 @@ The two `pull` refusals exist because the directions are not symmetric. A push o
|
|
|
58
58
|
|
|
59
59
|
## When it runs
|
|
60
60
|
|
|
61
|
-
`.husky/post-merge` runs `push` after the task archiving
|
|
61
|
+
`canon hooks post-merge`, which `.husky/post-merge` calls, runs `push` after the task archiving, on every merge rather than only on one that archived a task. A review report and a memory entry both land on runs that close nothing. The verb exits zero whatever the push answers, so an unreachable remote neither aborts the hook nor delays the archiving before it, and a checkout that never ran the setup reports nothing. `hooks.md` covers the verb. Anything the hook misses is covered by running the verb by hand.
|
|
@@ -46,7 +46,7 @@ A memory `category` is compared against the sentence-case form of the filename p
|
|
|
46
46
|
|
|
47
47
|
The standards filename check derives a word from the governed path rather than counting words in the name. Every member of the corpus is named for the artifact its scope statement governs, and one word is what that produces rather than the rule itself, so a check keyed on word count would pass a conforming single word naming the wrong artifact. Each path segment offers its own word, a dotted container segment offers none, a hyphenated segment offers its parts, and the singular and the plural both match.
|
|
48
48
|
|
|
49
|
-
The scope statement is read exactly as `
|
|
49
|
+
The scope statement is read exactly as `canon standards list` reads it for the catalog's `appliesTo` field: backticked spans in the first sentence alone, with an attribute standard resolving where that sentence backticks nothing and the statement says it governs an attribute. One sentence read two ways would let a standard pass the check while publishing a different jurisdiction to every consumer of the catalog. An attribute standard is exempt from the filename derivation because it governs no path to derive from, and a statement that anchors nothing and claims no attribute is reported as `scope-unanchored` rather than passed, since silence there would let the backticks be removed to disable the check.
|
|
50
50
|
|
|
51
51
|
An absent `## Scope` section suppresses the filename finding. The name derives from the statement, so a missing section leaves nothing to derive against, and reporting both would name one defect twice and point the fix at the wrong file. The walk skips `index.md`, which is generated from its siblings rather than authored.
|
|
52
52
|
|
package/docs/agents/scripting.md
CHANGED
|
@@ -35,6 +35,12 @@ declares nothing a parser can read. A consumer mapping a file to its governing
|
|
|
35
35
|
standards reads this rather than holding a table of its own, and reports an empty
|
|
36
36
|
array rather than skipping the standard behind it.
|
|
37
37
|
|
|
38
|
+
Each standard also carries `source`, the labeled path of the copy `canon standards
|
|
39
|
+
<name>` would read: `standards/<name>.md` for the working root and
|
|
40
|
+
`<canon>/standards/<name>.md` for the package corpus. A project's own standard
|
|
41
|
+
lists beside the package's, and one sharing a package name lists once, as the
|
|
42
|
+
project's copy.
|
|
43
|
+
|
|
38
44
|
`canon claude seeds list` reads the same plan `canon claude init` applies, so the
|
|
39
45
|
listing and the install cannot disagree. It now reports
|
|
40
46
|
`canon/context/index.md`, which `init` has always installed and the listing
|
|
@@ -30,11 +30,13 @@ The task carries its plan with it. When the closing task is the last live one wh
|
|
|
30
30
|
|
|
31
31
|
Every other relative link in the task is re-resolved for the deeper folder too, whether its target moved or not: `Groundwork:`, `Intake:`, a `Plan:` or `Ready:` line whose target stayed put, and a link in the body. Fenced blocks, inline code spans, absolute URLs, `#` anchors, and rooted paths are left alone. `canon tasks decline` applies the same rebase, since `declined/` sits at the same depth. The plan retarget runs before the rebase and writes each moved link relative to the live board, so the rebase carries it into the deeper folder like every other link, while the ready retarget runs after it.
|
|
32
32
|
|
|
33
|
+
Every other task file linking to the moved task is repointed at its new path too. The pass reads each markdown file in `.canon/tasks/`, `archive/`, and `declined/` except the moved file, `index.md`, `priority.md`, `backlog.md`, and the session handoffs, and resolves each relative link against the citing file's own folder. Only a link resolving to the old path is rewritten, with its `#` anchor kept, so an `archive/<stem>.md` link already correct, or the same stem in another folder, stays as written. Inline links and reference-style definitions (`[label]: path`) are both rewritten. Fenced blocks and inline code spans are skipped. `priority.md` and `backlog.md` are left alone because `canon tasks validate` reads a blocker there only while its target carries no `/`. Links broken before the move stay broken, and citations from plans, groundwork, and intake records are not read. The `relinked` array on the success record carries a root-relative `file` and a `links` count per file written, and is empty when nothing cited the task. The pass runs after the move, so a file it cannot read or write never stops the ordering-row removal or the index regen that follow. Each such file is left as it was and reported in `relinkFailed` as a root-relative `file` and a `message`, with a warning on stderr, and the verb still exits `0` because the move itself landed. A caller reading the record should treat a non-empty `relinkFailed` as links still pointing at the old path. `canon hooks post-merge`, which `.husky/post-merge` calls, reads the field and prints one `Links not repointed in:` line naming the files, since it discards the archive's stderr and nothing else on that unattended path would show the failure.
|
|
34
|
+
|
|
33
35
|
The ready folder the task's `Ready:` line names moves with the plan to `.canon/ready/archive/`, falling back to a `.canon/ready/<nn>-<slug>/` path in the plan when the line is absent. The `Ready:` line and the plan's literal path to the folder are retargeted, and the `ready` field on the success record carries `from` and `to`, or `null` when nothing moved. A folder that resolves to nothing, sits already archived, or meets a taken destination moves nothing and refuses nothing.
|
|
34
36
|
|
|
35
37
|
A plan several tasks share stays where it is, and its ready folder with it, and the task archives anyway. Moving it on the first task to close strands every sibling's pointer at a path that has gone, and `.canon/plans/` is gitignored so no history recovers the target. A `Plan:` line resolving to no file leaves the plan alone too, since a pointer somebody typed wrong is not a plan to move and holding the whole archive over it would park the board behind a repair the merge cannot make.
|
|
36
38
|
|
|
37
|
-
The merge is what settles a plan. `.husky/post-merge` reads the pull request number off the squash subject and calls this verb, so both halves close in one act with nobody naming a file. That is why the move sits inside this verb rather than in a second call the hook would make after it, which could leave the task archived and the plan live.
|
|
39
|
+
The merge is what settles a plan. `canon hooks post-merge`, which `.husky/post-merge` calls, reads the pull request number off the squash subject and calls this verb, so both halves close in one act with nobody naming a file. That is why the move sits inside this verb rather than in a second call the hook would make after it, which could leave the task archived and the plan live.
|
|
38
40
|
|
|
39
41
|
`bad-input` covers a malformed command line, which all three task verbs answer the same way. It is separate from `ambiguous` and `no-match` because those describe the board, and a caller that passed two selectors would otherwise be sent to repair a task citation that is fine.
|
|
40
42
|
|
|
@@ -72,7 +74,7 @@ Exit codes: `0` declined, `1` refused. The `reason` field carries which gate fir
|
|
|
72
74
|
|
|
73
75
|
The decision is written onto the task as a `Declined: <reason>, <who> on <YYYY-MM-DD>` line, anchored the same way `Pull request:` is, after the last origin line the task carries.
|
|
74
76
|
|
|
75
|
-
The task carries its plans with it the same way archive does, each one the declining task is the last live citation of, and the success record carries the same `plans` array. A declined task's plan lands in `.canon/plans/archive/`, indistinguishable from a shipped one by folder alone. The task file under `.canon/tasks/declined/` is what records which it was.
|
|
77
|
+
The task carries its plans with it the same way archive does, each one the declining task is the last live citation of, and the success record carries the same `plans` array. Inbound links to the declined task are repointed at `declined/<stem>.md` and reported in `relinked`, as the archive section describes. A declined task's plan lands in `.canon/plans/archive/`, indistinguishable from a shipped one by folder alone. The task file under `.canon/tasks/declined/` is what records which it was.
|
|
76
78
|
|
|
77
79
|
Skills branch on the reason rather than on the exit code, the same rule `canon tasks archive` states:
|
|
78
80
|
|
package/docs/agents/tasks.md
CHANGED
|
@@ -35,7 +35,7 @@ canon tasks next-label --json | jq -r '.label'
|
|
|
35
35
|
|
|
36
36
|
## Archive and decline
|
|
37
37
|
|
|
38
|
-
`canon tasks archive` moves a shipped task into `.canon/tasks/archive/`, selected by its stem or by the pull request it carries, and `canon tasks decline` moves one decided against into `.canon/tasks/declined/`. Both carry the plan and the ready folder along when the task is their last citation. The selectors, the gates, and the refusal reasons are in `tasks-archive.md`.
|
|
38
|
+
`canon tasks archive` moves a shipped task into `.canon/tasks/archive/`, selected by its stem or by the pull request it carries, and `canon tasks decline` moves one decided against into `.canon/tasks/declined/`. Both carry the plan and the ready folder along when the task is their last citation, and both repoint every link to the moved task from another task file. The selectors, the gates, and the refusal reasons are in `tasks-archive.md`.
|
|
39
39
|
|
|
40
40
|
## Plan verbs
|
|
41
41
|
|
package/docs/agents/worktrees.md
CHANGED
|
@@ -53,9 +53,9 @@ Field order is part of the contract. A shell reads this record with a pattern ra
|
|
|
53
53
|
|
|
54
54
|
## What calls it
|
|
55
55
|
|
|
56
|
-
`.husky/post-merge` runs `canon worktrees reclaim --json` on every merge, between the records push and the upgrade
|
|
56
|
+
`canon hooks post-merge`, which `.husky/post-merge` calls, runs `canon worktrees reclaim --json` on every merge, between the records push and the upgrade, so a merged branch's worktree goes without a person remembering. `CANON_SKIP_RECLAIM=1` turns that step off. `hooks.md` covers the verb.
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
That call carries no `--root` and runs from the hook's own working directory, unlike the two steps before it. Git runs a hook from the top level of the worktree the pull happened in, and the verb reads its own working directory to refuse that worktree as `current-worktree`, so a root argument would turn the running worktree into an ordinary candidate and let a pull inside a linked worktree delete the ground under itself.
|
|
59
59
|
|
|
60
60
|
## What makes a worktree reclaimable
|
|
61
61
|
|
package/docs/target/scenarios.md
CHANGED
|
@@ -46,11 +46,11 @@ canon tooling sync vite-react ./frontend --skip base --write
|
|
|
46
46
|
canon tooling sync python ./backend --skip base --write
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
`--skip base` drops the `base` layer from each subtree sync, so husky, prettier, cspell, commitlint, and CI stay single at the repo root. Without it, every subtree re-drops husky, and since git honors only one `core.hooksPath` the extra hook dirs silently break. Each subtree still gets its own framework configs (eslint, vitest, tsconfig, vite), and its own stack reference reads through `canon tooling reference <stack>`.
|
|
49
|
+
`--skip base` drops the `base` layer from each subtree sync, so husky, prettier, cspell, commitlint, and CI stay single at the repo root. The one exception is base's verify runner, `scripts/verify.sh`, `scripts/verify.ts`, and `scripts/verify.json`, which every subtree still receives so it can verify on its own. Without it, every subtree re-drops husky, and since git honors only one `core.hooksPath` the extra hook dirs silently break. Each subtree still gets its own framework configs (eslint, vitest, tsconfig, vite), and its own stack reference reads through `canon tooling reference <stack>`.
|
|
50
50
|
|
|
51
51
|
A sync whose target sits below the git root knows it is writing into a subfolder:
|
|
52
52
|
|
|
53
53
|
- It withholds `.github/`, since GitHub reads workflows only at the repository root, and names each withheld file. Run the subtree from a job in the root workflow instead, with `working-directory` set to the subtree path the sync prints, such as `working-directory: frontend`.
|
|
54
54
|
- It writes a nested `cspell.json` that registers the subtree's word lists, so a root `cspell '**'` reads them for files under that folder. It leaves a spell config the subtree already has alone.
|
|
55
55
|
- It installs no dependencies and adds no scripts to a subtree without a `package.json`. It names what it skipped and ends on `run 'bun init' in <path>, then sync again`.
|
|
56
|
-
- The subtree's
|
|
56
|
+
- The subtree carries no `check` script, since that lives in the root's base layer, so run `./scripts/verify.sh` there. It skips the format, spell, and shell phases a subtree does not declare, since the root runs them, and still fails on a missing lint, typecheck, or test script, the phases its stack's `scripts/verify.json` requires.
|
package/package.json
CHANGED
|
@@ -34,6 +34,7 @@ export const CORPORA: readonly string[] = [
|
|
|
34
34
|
export const PRACTICE_SKILLS: readonly string[] = [
|
|
35
35
|
join(SHIPPED_CORPUS, 'codebase-layout'),
|
|
36
36
|
join(SHIPPED_CORPUS, 'test-craft'),
|
|
37
|
+
join(SHIPPED_CORPUS, 'review-craft'),
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
/** The closing H2s a practice skill carries, matched exactly outside fences. */
|
package/src/cli.ts
CHANGED
|
@@ -30,6 +30,7 @@ import { register as migrate } from '@/commands/migrate'
|
|
|
30
30
|
import { register as records } from '@/commands/records'
|
|
31
31
|
import { register as sessions } from '@/commands/sessions'
|
|
32
32
|
import { register as worktrees } from '@/commands/worktrees'
|
|
33
|
+
import { register as hooks } from '@/commands/hooks'
|
|
33
34
|
import { register as audits } from '@/commands/audits'
|
|
34
35
|
import { register as gate } from '@/commands/gate'
|
|
35
36
|
import { register as secrets } from '@/commands/secrets'
|
|
@@ -91,6 +92,7 @@ migrate(program)
|
|
|
91
92
|
sessions(program)
|
|
92
93
|
targets(program)
|
|
93
94
|
worktrees(program)
|
|
95
|
+
hooks(program)
|
|
94
96
|
secrets(program)
|
|
95
97
|
deps(program)
|
|
96
98
|
labels(program)
|
package/src/commands/claude.ts
CHANGED
|
@@ -111,7 +111,7 @@ interface PluginUpdateOptions {
|
|
|
111
111
|
/**
|
|
112
112
|
* `no-claude` and `no-plugin` name permanent conditions on a machine that
|
|
113
113
|
* never carries the marketplace plugin at all, matching the `gh-missing` and
|
|
114
|
-
* `no-repository` reasons
|
|
114
|
+
* `no-repository` reasons the post-merge hook's other two steps already stay
|
|
115
115
|
* quiet on forever rather than nagging a project that will never fix them.
|
|
116
116
|
* Every other reason is a real defect and prints.
|
|
117
117
|
*/
|