@erclx/canon 5.5.0 → 5.6.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/codebase-layout/REQUIREMENT.md +3 -0
- package/claude/skills/codebase-layout/SKILL.md +28 -0
- package/claude/skills/codebase-layout/references/adopted.md +6 -0
- package/claude/skills/plan-feature/REQUIREMENT.md +4 -0
- package/claude/skills/plan-feature/SKILL.md +4 -0
- package/claude/skills/teach-workspace/REQUIREMENT.md +3 -0
- package/claude/skills/teach-workspace/SKILL.md +2 -1
- package/claude/skills/teach-workspace/references/lesson-craft.md +8 -1
- package/claude/skills/test-craft/REQUIREMENT.md +4 -1
- package/claude/skills/test-craft/SKILL.md +20 -2
- package/claude/skills/test-craft/references/adopted.md +3 -1
- package/claude/skills/ui-checklist/REQUIREMENT.md +1 -0
- package/claude/skills/ui-checklist/SKILL.md +11 -1
- package/docs/agents/index.md +1 -1
- package/docs/agents/skills-audit.md +6 -3
- package/package.json +1 -1
- package/src/claude/skills-audit.ts +81 -1
- package/src/commands/claude.ts +33 -0
- package/src/gate/measures.ts +43 -1
- package/src/gate/stages.ts +9 -0
- package/standards/index.md +1 -0
- package/standards/skill-practice.md +51 -0
- package/standards/skill.md +3 -1
|
@@ -13,6 +13,8 @@ The failure is measured on a real target. Two source folders grew from 11 files
|
|
|
13
13
|
|
|
14
14
|
Sharpening the rules does not reach the decision. Both were loaded and neither was applied, since an always-loaded line competes with everything else a session holds and a path-scoped rule fires on a read of an existing file rather than on a path written into a plan.
|
|
15
15
|
|
|
16
|
+
A placement rule in front of the session is also one it can argue past. Each flat file in that target landed for a reason that sounded fine in the moment, such as the neighbors already being flat or a split reading as scope creep, and nothing answered the reason before the path was written.
|
|
17
|
+
|
|
16
18
|
## Must
|
|
17
19
|
|
|
18
20
|
- Be loaded by `plan-feature` whenever a plan names a path that does not exist yet, so the guidance is in front of the planner at the moment it writes the path
|
|
@@ -23,6 +25,7 @@ Sharpening the rules does not reach the decision. Both were loaded and neither w
|
|
|
23
25
|
- State that a file is named for what it covers inside its folder, never repeating the folder's name as its stem or its prefix, since a name placed beside its neighbor copies the neighbor's prefix along with its folder
|
|
24
26
|
- Hold the stack-neutral core in the body and defer category specifics to a reference loaded only when a new path falls in that category
|
|
25
27
|
- Record the external layouts adopted and declined with the reason for each, so a later session extends the position instead of re-deriving it
|
|
28
|
+
- Close with a table of the excuses a session gives for placing a file beside its neighbor, each with its rebuttal, the red flags a session can see while writing a path, and a checklist each new path passes before the plan is handed over
|
|
26
29
|
- Fire on a direct question about placement as well, such as where a file goes or whether a flat folder should split, since a session outside any plan still places files
|
|
27
30
|
|
|
28
31
|
## Must not
|
|
@@ -55,6 +55,34 @@ Read the reference matching what the new paths hold, and skip the rest.
|
|
|
55
55
|
|
|
56
56
|
Read `${CLAUDE_SKILL_DIR}/references/adopted.md` only when extending this guidance or arguing against a rule in it. It records which external layouts were adopted, which were declined, and why.
|
|
57
57
|
|
|
58
|
+
## Excuses and rebuttals
|
|
59
|
+
|
|
60
|
+
| Excuse | Rebuttal |
|
|
61
|
+
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| The neighbors are flat, so one more flat file fits | The neighbors are how the folder got to 46. Ask whether this file brings a second role. |
|
|
63
|
+
| The folder is under ten files, so it does not need a look | Ten is a prompt, not a gate. A second role splits a folder at six. |
|
|
64
|
+
| Splitting now is a refactor outside this plan's scope | Splitting before the file lands touches a handful of paths. Splitting later touched 98. |
|
|
65
|
+
| A `utils` folder is where shared code goes | Shared code needs a second consumer. Until then the file sits with the one it serves. |
|
|
66
|
+
| The worker can decide where the file goes | The worker copies the path the plan gives it. A path left loose in the plan is placed beside its neighbor. |
|
|
67
|
+
|
|
68
|
+
## Red flags
|
|
69
|
+
|
|
70
|
+
- A new path's only reason is that a similar file sits in the same folder.
|
|
71
|
+
- A new file's name repeats its folder as its stem or its prefix.
|
|
72
|
+
- A new folder is named for a technical kind alone, such as `helpers/` or `common/`.
|
|
73
|
+
- A shared module is about to import from a feature.
|
|
74
|
+
- The plan writes a file into a folder that does not exist without naming the folder.
|
|
75
|
+
|
|
76
|
+
## Before handing over
|
|
77
|
+
|
|
78
|
+
Check every new path the plan or the change names against each line, and fix the path rather than noting it.
|
|
79
|
+
|
|
80
|
+
- It follows the strategy the project already uses, or the plan proposes the change as its own decision.
|
|
81
|
+
- It carries a one-clause reason naming its role, its consumer, or the strategy it follows.
|
|
82
|
+
- Its folder holds one role after it lands.
|
|
83
|
+
- Its stem names what it covers without repeating its folder.
|
|
84
|
+
- A file with one consumer sits beside it, and a file with two sits in shared code.
|
|
85
|
+
|
|
58
86
|
## What this delegates
|
|
59
87
|
|
|
60
88
|
- Which layer a test belongs at: `test-craft`. This skill decides where the test file sits once its layer is chosen.
|
|
@@ -9,6 +9,8 @@ External sources were read and filtered rather than imported. This file is the r
|
|
|
9
9
|
|
|
10
10
|
## Adopted
|
|
11
11
|
|
|
12
|
+
### Placing files
|
|
13
|
+
|
|
12
14
|
**Group what changes together.** Robert Martin's restatement of the single responsibility principle: "Gather together the things that change for the same reasons. Separate those things that change for different reasons" ([The Single Responsibility Principle](https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html)). It gives "split by role" a test a planner can apply to a folder: would these files change together?
|
|
13
15
|
|
|
14
16
|
**Name folders for the domain rather than the framework.** Martin's Screaming Architecture argues the top level should tell readers "about the system, not about the frameworks you used in your system" ([Screaming Architecture](https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html)). It is the root of naming a folder for what it holds rather than its technical kind.
|
|
@@ -21,6 +23,10 @@ External sources were read and filtered rather than imported. This file is the r
|
|
|
21
23
|
|
|
22
24
|
**Fixtures in modules, page objects on their own.** Playwright defines fixtures in dedicated modules that extend the base test, merges them into one module the specs import, and keeps page objects as their own modules ([fixtures](https://playwright.dev/docs/test-fixtures)). Adopted in `tests.md` as the split between specs and support.
|
|
23
25
|
|
|
26
|
+
### Closing the body
|
|
27
|
+
|
|
28
|
+
**Excuses, red flags, and a closing checklist.** From `addyosmani/agent-skills@2686b620`, [`docs/skill-anatomy.md`](https://github.com/addyosmani/agent-skills/blob/2686b620fc1fed2e8f60c704839c766b8594c6b6/docs/skill-anatomy.md): Common Rationalizations, Red Flags, and Verification. Adopted as the three closing sections every practice skill carries. Each excuse row rebuts a reason a flat folder stayed flat in the target this skill's requirement measures, rather than an objection nobody raised.
|
|
29
|
+
|
|
24
30
|
## Declined
|
|
25
31
|
|
|
26
32
|
**Deep modules and seams.** Matt Pocock's codebase-design skill covers a lot of behavior behind a small interface placed at a clean seam ([codebase-design](https://github.com/mattpocock/skills/blob/main/skills/engineering/codebase-design/SKILL.md)). It answers where a boundary goes in code, not where a file goes on disk, and absorbing it doubles the skill's scope before the placement problem is shown solved. It becomes a reference only when a plan places files well and still draws a boundary badly.
|
|
@@ -12,6 +12,9 @@ Without this skill, implementation starts before anyone knows what it touches. A
|
|
|
12
12
|
## Must
|
|
13
13
|
|
|
14
14
|
- Read the project's own Claude setup before scanning source, so the plan inherits decisions already made instead of reopening them
|
|
15
|
+
- Reframe a vague ask as observable, checkable success criteria before listing any file, and carry each criterion into the plan's verification, so a plan never ships a check with nothing measurable to prove
|
|
16
|
+
- Slice batches by a user-visible path rather than by layer, and sequence the riskiest batch first while a sweep stays last, so the batch most likely to change the plan lands while later ones can still move
|
|
17
|
+
- Predict the operator's answers to the next three questions about the ask's success criteria, proceed on the ones it can predict, and ask when it can predict none, since a plan built on a guessed goal proves the wrong thing
|
|
15
18
|
- Name every file the work touches with the reason it is touched
|
|
16
19
|
- Place each new path against the layout guidance with a one-clause reason, loading that guidance only when the plan creates a file or folder, since the planner decides placement and the worker copies whatever path it is given
|
|
17
20
|
- Name how each outcome is proven and which input or state would break the change, so the executing session has a check to run and the reviewer has a place to look
|
|
@@ -30,6 +33,7 @@ Without this skill, implementation starts before anyone knows what it touches. A
|
|
|
30
33
|
## Guards
|
|
31
34
|
|
|
32
35
|
- No feature description: stop and ask for one
|
|
36
|
+
- No predictable success criterion: ask before planning rather than guessing the goal
|
|
33
37
|
|
|
34
38
|
## Out of scope
|
|
35
39
|
|
|
@@ -10,6 +10,7 @@ description: Plans a feature by reading the project's Claude setup and scanning
|
|
|
10
10
|
- If no feature description is provided, stop: `❌ No feature description. Describe what you want to add.`
|
|
11
11
|
- Do not implement anything. Output the plan and stop.
|
|
12
12
|
- When the feature description spans two or more independent concerns, write one plan file per concern. Do not bundle them under a single slug.
|
|
13
|
+
- Before Step 3, name the next three questions you would ask about what the ask must achieve, and predict the operator's answer to each from the ask and the project context. An answer you can predict settles its criterion, which Step 3 writes as a `**Verification:**` bullet rather than a question. Only a prediction still open goes under `**Questions:**` with a `- Suggested:` line, so a fully specified ask carries none and Small mode stays reachable. When you can predict none of the three, so no checkable success criterion can be stated, ask before Step 3 through whatever route reaches the operator, including a message to a dispatching session. The test reads the ask's success criteria, never the plan's open questions, which a `- Suggested:` line answers.
|
|
13
14
|
|
|
14
15
|
## Step 1: read the Claude setup
|
|
15
16
|
|
|
@@ -43,6 +44,7 @@ The section list, what each section holds, the suggested-and-answer contract, an
|
|
|
43
44
|
|
|
44
45
|
What this skill adds on top of the standard:
|
|
45
46
|
|
|
47
|
+
- Reframe a vague ask as success criteria before writing `**Files to touch:**`. Each criterion names something observable and a check that decides it, such as a measure and the threshold it must stay under, drawn from the ask or the project context. "Make it faster" names no criterion, while a stated budget for one path does. Each criterion becomes one `**Verification:**` bullet.
|
|
46
48
|
- Apply senior judgment to every `- Suggested:` line. Pick the best option and state it in one line with its reason or main tradeoff. No padding, no alternatives unless they change the pick.
|
|
47
49
|
- Suggest a real default when best practice, the codebase, or prior context points to one.
|
|
48
50
|
- Load the `canon:codebase-layout` skill before writing the `**Files to touch:**` entries when any of them names a file or folder that does not exist yet, and give each new path its placement reason in its entry. Skip the load when every entry edits an existing file. Report it rather than proceeding silently when the skill does not resolve.
|
|
@@ -57,6 +59,8 @@ A plan sequencing its work into batches declares where one pull request ends, he
|
|
|
57
59
|
- One batch is one pull request, and one pull request is one plan file, named for its own concern rather than a numeric suffix. `canon tasks plan-branch` derives one branch from one plan filename, so a `**Batch N**` sub-heading inside one file's `**Files to touch:**` shares that one branch across every batch, and the batch that merges first strands every batch behind it with nothing left to open a pull request against. Write a plan opening five batches as five files before the first line of any of them.
|
|
58
60
|
- State a dependent batch's dependency on the ones before it in its own `**Constraints:**`, naming the earlier batch's slug and stacking rather than merging into it. The batches are built in order and depend on each other in that order, which is what a stack expresses, so the dependency argues for stacking and never for collapsing two batches into one file or one review.
|
|
59
61
|
- Mark each batch dependent or independent by comparing the file sets rather than the descriptions. A batch sharing no file with another is independent and earns its own branch even where both arrived in one request. One run measured at 68 files carried three such batches into a single review because nothing drew the boundary.
|
|
62
|
+
- Slice a batch by one user-visible path end to end rather than by layer, so each batch ships something a person can exercise. The slice decides what a batch contains, and the file-set comparison above still decides whether two batches depend on each other, so two slices sharing a file stay marked dependent and stacked.
|
|
63
|
+
- Sequence the riskiest batch first, so the batch most likely to change the plan is built while every later batch can still move. A sweep is the one exception and stays last even when it is the riskiest, per the next bullet.
|
|
60
64
|
- Sequence a sweep last. A batch deliberately rewriting files earlier batches touched is coherent as the final one and forces every batch behind it into one review anywhere else.
|
|
61
65
|
- Keep each batch's commits contiguous once the work starts. A later fix to an earlier batch belongs on that batch's own commits, since a batch interrupted by another cannot be lifted onto its own branch afterward.
|
|
62
66
|
|
|
@@ -13,6 +13,8 @@ It picks the next topic from the subject's own order rather than from what the l
|
|
|
13
13
|
|
|
14
14
|
It also produces one output where two are needed. A page written to be worked through once and a page written to be looked up later have different lifetimes and different readers, and merging them yields material that is disposable and gets kept, or durable and carries a quiz nobody can promote. Writing the durable half in a format the authoring gates do not read costs a conversion at the moment it matters most, which is the moment someone tries to promote it.
|
|
15
15
|
|
|
16
|
+
A lesson on a structure can also point at one and draw none. Nothing asks the author whether the subject is a relationship, boundary, path, or before-and-after, so a lesson on layers and ports tells the learner to draw arrows and ships none, and nothing records whether prose was a choice or an omission.
|
|
17
|
+
|
|
16
18
|
A session that does record something records the wrong thing. It writes what was taught rather than what the learner retrieved, and a tally of errors carries none of the misconception a later session would work against.
|
|
17
19
|
|
|
18
20
|
The lesson then reaches nobody. It is a page carrying a stylesheet and a script, and the only thing a session hands over is a file path, which an editor preview opens with neither. The learner reads unstyled markup and takes it for the lesson, or opens nothing at all, and either way the session reports the lesson as delivered. A path is also the wrong unit once a workspace holds several pages, since the learner wants the one they are on rather than the folder it sits in.
|
|
@@ -31,6 +33,7 @@ Two failures land specifically on where the folder sits. A workspace resolved ag
|
|
|
31
33
|
- Place each lesson from the learning records rather than from the subject's order, and open with retrieval on the last wrong answer
|
|
32
34
|
- Split the output by lifetime, sending the worked-through half to lessons and the looked-up half to reference pages in the format the authoring gates read
|
|
33
35
|
- Record the wrong answer itself rather than the count, since that is what the next session places the learner from
|
|
36
|
+
- Decide each lesson's figure before writing its body, drawing it or recording in the reply the one-line reason prose carries it, so a structure lesson never ships with neither
|
|
34
37
|
- Report progress against the mission's success lines, so a mission can be called finished
|
|
35
38
|
- Hand the learner a link that opens the workspace in a real browser on every run, reading it back from the verb that served it rather than composing one, since the port a preview lands on is not the port it asked for
|
|
36
39
|
- Propose a destination for each durable page by who owns its subject, and wait for the operator rather than routing on the session's own reading
|
|
@@ -105,7 +105,7 @@ Write the correct option first, then present the options in the order `order` re
|
|
|
105
105
|
|
|
106
106
|
### The quiz, the teach-back block, and the lesson body
|
|
107
107
|
|
|
108
|
-
Read `${CLAUDE_SKILL_DIR}/references/lesson-craft.md` before writing the lesson. `## Quiz construction` and `## Teach back` there fix the markup `canon teach nav`'s stepper gates on, so a quiz in any other shape shows every question at once and nothing reports it. `## Building the lesson body` carries the four marker pairs, the render, nav, stylesheet, and glossary verbs, and the rule against composing any of them by hand when a verb does not resolve. Keep every quiz answer the same length, so formatting leaks no clue about which one is correct.
|
|
108
|
+
Read `${CLAUDE_SKILL_DIR}/references/lesson-craft.md` before writing the lesson. `## Quiz construction` and `## Teach back` there fix the markup `canon teach nav`'s stepper gates on, so a quiz in any other shape shows every question at once and nothing reports it. `## Building the lesson body` carries the four marker pairs, the render, nav, stylesheet, and glossary verbs, and the rule against composing any of them by hand when a verb does not resolve. Keep every quiz answer the same length, so formatting leaks no clue about which one is correct. Decide the figure first, by `## Diagrams` there.
|
|
109
109
|
|
|
110
110
|
### Hand over a link, never a path
|
|
111
111
|
|
|
@@ -158,6 +158,7 @@ Read `${CLAUDE_SKILL_DIR}/references/promotion.md` first. It carries what may be
|
|
|
158
158
|
Lesson: .canon/teach/<nn>-<topic>/lessons/<nnnn>-<slug>.html
|
|
159
159
|
Reference: .canon/teach/<nn>-<topic>/reference/<slug>.md
|
|
160
160
|
Record: .canon/teach/<nn>-<topic>/learning-records/<nnnn>-<slug>.md
|
|
161
|
+
Figure: <drawn through draft-figure, or the one-line reason prose carries it>
|
|
161
162
|
Progress: <n> of <m> success lines met
|
|
162
163
|
Open: [<the url the serve verb reported>](<the same url>)
|
|
163
164
|
```
|
|
@@ -36,7 +36,14 @@ Every element competes with the material for attention.
|
|
|
36
36
|
|
|
37
37
|
## Diagrams
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Decide the figure once per lesson, before writing the body.
|
|
40
|
+
|
|
41
|
+
1. Name the one relationship, boundary, path, or before-and-after the lesson is about. A lesson about none ends here, with `Figure: none, <why it names no structure>` on the reply.
|
|
42
|
+
2. Hold it against the bar in `${CLAUDE_SKILL_DIR}/../../standards/figures.md`, which also carries the drawing rules.
|
|
43
|
+
3. Draw it through `canon:draft-figure` into a `raw` block in the body, before the quiz entry and never inside it or after its feedback, which breaks the stepper's gating. Report it, never hand-draw, when that skill does not resolve.
|
|
44
|
+
4. Or write one line on why prose carries it better, such as a structure an earlier lesson gave.
|
|
45
|
+
|
|
46
|
+
A structure lesson takes step 3 or 4, never neither. Put the outcome on the reply's `Figure:` line, off the lesson page. Never draw to satisfy the step, or on every section (`## Restraint`).
|
|
40
47
|
|
|
41
48
|
## Quiz construction
|
|
42
49
|
|
|
@@ -13,6 +13,8 @@ The one surface that did pick a layer picked the expensive one. The UI test skil
|
|
|
13
13
|
|
|
14
14
|
The failure is measured on a real target. A single-page portfolio site carried 219 end to end tests against 4 unit test files, an inverted pyramid. Its suite could not raise its worker count without failing between 11 and 24 tests on every retry, because timing-sensitive assertions sat at the layer where CI load decides the result. One copy edit failed three engines because the copy was asserted in a browser run.
|
|
15
15
|
|
|
16
|
+
A stated rule is also one a session can talk itself out of. Each test in that suite sat where it did for a reason that sounded fine at the time, such as the end to end spec already being open or a pause quieting a run that flaked under load, and nothing in front of the session answered the reason.
|
|
17
|
+
|
|
16
18
|
Moving the guidance into a path-scoped rule does not reach the session that needs it. A path-scoped rule loads when a session reads a matching file and not when it creates one, measured in three headless runs, so a rule scoped to test files is silent on the first test of every feature.
|
|
17
19
|
|
|
18
20
|
## Must
|
|
@@ -25,6 +27,7 @@ Moving the guidance into a path-scoped rule does not reach the session that need
|
|
|
25
27
|
- Promise a middle layer for behavior a simulated DOM cannot render, naming the real-browser component runners, so a static site is not left choosing between a simulated DOM that computes no layout and a whole-site run
|
|
26
28
|
- Repeat in the end to end reference the two floors a session writing its first spec never loads through the path-scoped rule, being the `scrollend` settle and motion reduced by default
|
|
27
29
|
- Record the external patterns adopted and declined with the reason for each, so a later session extends the position instead of re-deriving it
|
|
30
|
+
- Close with a table of the excuses a session gives for the wrong layer or a weak test, each with its rebuttal, and the red flags a session can see mid-work, so the reasons that produced the inverted pyramid meet an answer before the test lands
|
|
28
31
|
- Trigger on writing or changing any test, after implementing as well as before, since regression tests and end to end tests are mostly written after the code exists
|
|
29
32
|
|
|
30
33
|
## Must not
|
|
@@ -34,7 +37,7 @@ Moving the guidance into a path-scoped rule does not reach the session that need
|
|
|
34
37
|
- Restate the structure floors the testing rule already carries, such as arrange, act, assert, one behavior per test, isolation, and no snapshots
|
|
35
38
|
- Restate the settle, guard, and run-scope rules the end to end rules carry, beyond the two floors named above
|
|
36
39
|
- Run the red, green, refactor procedure or decide its order, which `test-first` owns
|
|
37
|
-
- Import an external skill's tone, such as a law stated as absolute or
|
|
40
|
+
- Import an external skill's tone, such as a law stated as absolute, or an excuse row rebutting an objection no session has raised
|
|
38
41
|
- Be reached only by an author typing its name. The intended non-author callers are `test-first`'s first step and a description matching any request to write or change a test. A test written in a session that never loaded this skill is the evidence the trigger is too weak, and nothing answers that before the skill has run.
|
|
39
42
|
|
|
40
43
|
## Guards
|
|
@@ -57,9 +57,27 @@ Read the one reference matching the layer the table picked, before writing the f
|
|
|
57
57
|
|
|
58
58
|
Read `${CLAUDE_SKILL_DIR}/references/adopted.md` only when extending this guidance or arguing against a rule in it. It records which external patterns were adopted, which declined, and why.
|
|
59
59
|
|
|
60
|
-
##
|
|
60
|
+
## Excuses and rebuttals
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
| Excuse | Rebuttal |
|
|
63
|
+
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
64
|
+
| The end to end spec is already open, so add the case there | An open file is not a layer. Read the row off the behavior, and a loading, empty, or error state is a component. |
|
|
65
|
+
| The test is small, so its layer does not matter | A small test at the wrong layer is run by every later pass, and moving it costs a second test. |
|
|
66
|
+
| The copy is what the user sees, so assert it in a browser | Copy asserted in a browser run fails every engine on one wording change. Assert copy at unit. |
|
|
67
|
+
| It only flakes under CI load, so add a pause | A pause hides the condition the run is waiting on. Settle on the condition, or move the assertion down a layer. |
|
|
68
|
+
| A mock is quicker to set up than the real collaborator | A mock proves how the code is built. Take a real collaborator or a fake first. |
|
|
69
|
+
|
|
70
|
+
## Red flags
|
|
71
|
+
|
|
72
|
+
- You are opening an end to end spec to add a state rather than a journey.
|
|
73
|
+
- The expected value comes from calling the function under test.
|
|
74
|
+
- A sleep, a timeout, or a retry count is going in to make a run pass.
|
|
75
|
+
- The only assertion is that a double was called.
|
|
76
|
+
- You picked the layer before naming what production change makes the test fail.
|
|
77
|
+
|
|
78
|
+
## Before handing over
|
|
79
|
+
|
|
80
|
+
Run the final filter over every test written this session, checking each line. A test that fails one is rewritten or cut, never kept with a note.
|
|
63
81
|
|
|
64
82
|
- **Mirror assertion.** The expected value is computed by the code under test. Write the literal the behavior should produce.
|
|
65
83
|
- **Change detector.** The assertion pins a constant, a message's wording, or a markup shape no caller depends on.
|
|
@@ -25,6 +25,8 @@ External sources were read and filtered rather than imported. This file is the r
|
|
|
25
25
|
|
|
26
26
|
**Kent Beck's desiderata as background.** The twelve properties at [testdesiderata.com](https://testdesiderata.com/) trade against each other. "Behavioral" and "structure-insensitive" together are the whole of "test behavior, not implementation", which is why the body states that pair as asserting through the public surface.
|
|
27
27
|
|
|
28
|
+
**Excuses, red flags, and a closing checklist.** From `addyosmani/agent-skills@2686b620`, [`docs/skill-anatomy.md`](https://github.com/addyosmani/agent-skills/blob/2686b620fc1fed2e8f60c704839c766b8594c6b6/docs/skill-anatomy.md): Common Rationalizations, Red Flags, and Verification. Adopted as the three closing sections every practice skill carries, with the final filter as the closing checklist. This reverses an earlier decline of the excuses table. That decline objected to arguments against imagined objections, so each row here rebuts an excuse this skill's requirement records a session acting on, such as extending the open end to end spec or adding a pause for a run that flakes under load.
|
|
29
|
+
|
|
28
30
|
### Per layer
|
|
29
31
|
|
|
30
32
|
**Component query and wait rules.** Role queries first, user events over synthetic ones, one assertion per wait, no side effects inside a wait ([Common mistakes](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)). Adopted in `component.md` with the library names removed from the rules.
|
|
@@ -37,7 +39,7 @@ External sources were read and filtered rather than imported. This file is the r
|
|
|
37
39
|
|
|
38
40
|
**Delete the code written before its test.** `superpowers`'s test-driven skill tells a session to delete implementation written ahead of a failing test. Declined: this toolkit audits the order after the fact with its test-order verb and reports rather than destroys, since deleting working code punishes the session without restoring the evidence the red run would have given.
|
|
39
41
|
|
|
40
|
-
**
|
|
42
|
+
**An iron law.** Both external test-driven skills carry an "Iron Law" stated as absolute. Declined for tone: this toolkit states a rule once with its reason.
|
|
41
43
|
|
|
42
44
|
**One skill per layer.** A unit skill, a component skill, and an end to end skill each loaded on its own trigger. Declined because the layer has to be chosen before the right skill can load, so the choice ends up restated in all three or in a fourth skill that is this one.
|
|
43
45
|
|
|
@@ -21,6 +21,7 @@ The automatable half has its own gap, and it is no longer this skill's to close
|
|
|
21
21
|
|
|
22
22
|
- Split every change into visual-only and automatable before writing anything
|
|
23
23
|
- Write the checklist for the visual-only half, each item an action and the result a person is looking for
|
|
24
|
+
- Give each item what a driver needs to run it without guessing: the route, the width where the change can differ by width, an action verb to open on, a `(taste)` marker on a judgment of look or feel, and the setup or a `(local only)` marker for a state the page does not open in
|
|
24
25
|
- Name every automatable change shipping with no test, with the layer `test-craft` would place it at, as a section of the same file
|
|
25
26
|
- Write the file to the `.canon/tmp/handoff/ui-checklist/<slug>.md` handoff at the main worktree root, overwriting
|
|
26
27
|
- Report that there is nothing to verify rather than manufacturing a checklist to show work
|
|
@@ -28,9 +28,19 @@ Group visual items by feature area. Use `- [ ]` checkbox syntax, and write each
|
|
|
28
28
|
|
|
29
29
|
**<Feature area>**
|
|
30
30
|
|
|
31
|
-
- [ ] <
|
|
31
|
+
- [ ] <route>[, <width>]: <verb> <target> → <expected visual result>
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
Write each box so a session driving the running app can run it without guessing:
|
|
35
|
+
|
|
36
|
+
- **Route.** Name where the box is driven, as a path or the control that reaches it. A box naming no route is driven from the address the driver opens, so a single-page app with no routing needs no invented path. Write "from the page the app opens on" when a state is reached by a sequence rather than a URL.
|
|
37
|
+
- **Width.** Name the width a box is driven at only when the change can make it differ by width, the same test the widths block below uses for omission. A box about copy or color renders the same everywhere, so leave the width off rather than inventing one.
|
|
38
|
+
- **Verb.** Open on an action a driver performs, such as "scroll to", "click", "resize to", or "tab to". Never open on "read" or "look at", which name no action.
|
|
39
|
+
- **Taste.** End the line with `(taste)` when the result is a judgment of look or feel rather than a fact a driver can observe. A driver reports such a box as needing a person's eyes instead of passing it.
|
|
40
|
+
- **Setup.** A box needing a state the page does not open in states the setup inline. When a hosted preview cannot reach that state, such as a stopped service or a seeded record, end the line with `(local only)` instead.
|
|
41
|
+
|
|
42
|
+
The checkbox stays `- [ ]` followed by the action and the result, since the evidence comment carries ticked boxes forward by that shape.
|
|
43
|
+
|
|
34
44
|
## The widths block
|
|
35
45
|
|
|
36
46
|
A screenshot shows one width, so a reviewer cannot tell from the picture alone whether any other width was looked at. When a change touches layout, spacing, or anything responsive, put this block above the visual items, since it frames what the screenshots beside it can support:
|
package/docs/agents/index.md
CHANGED
|
@@ -50,7 +50,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
|
|
|
50
50
|
- [Scripting](scripting.md): The runtime catalogs that replace hardcoded names, what each carries, and a headless invocation per domain
|
|
51
51
|
- [Sessions transfer](sessions-transfer.md): Moving a session to another machine, the export bundle and its manifest, placing it with import, the refusal reasons, and what a resume on the target depends on
|
|
52
52
|
- [Sessions](sessions.md): Resolving live peer sessions to the worktree and branch each holds, reading which row is the caller, the liveness confidence field, what each session surface can see, and where moving a session to another machine is described
|
|
53
|
-
- [Skill audit](skills-audit.md): Measuring both skill corpora against standards
|
|
53
|
+
- [Skill audit](skills-audit.md): Measuring both skill corpora against the skill standards, the checks it reads, the requirement gate that is the only failing one, and the drift verb that names bodies rewritten since a ref
|
|
54
54
|
- [Citation reach](skills-reach.md): Reporting the skill bodies that cite a path no target project receives, which corpus the verb reads, the ownership key that decides what counts, the one-word qualifier that marks a citation as decided, and why the verb reports instead of gating
|
|
55
55
|
- [Standard success criteria](standards-audit.md): Reading the corpus against the success-criterion gate, the two headings it accepts, why the check scopes to arrival rather than the whole corpus, and the exit codes it sets
|
|
56
56
|
- [State-scoped risk](state-scoped-risk.md): Reading committed state rather than an arriving change, the shipped-tree corpus the secret scan reads, what it keys on and how a deliberate value is exempted, the advisory check and its network failure mode, and why one gates while the other reports
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Skill audit
|
|
3
|
-
description: Measuring both skill corpora against standards
|
|
3
|
+
description: Measuring both skill corpora against the skill standards, the checks it reads, the requirement gate that is the only failing one, and the drift verb that names bodies rewritten since a ref
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Skill audit
|
|
7
7
|
|
|
8
|
-
`canon claude skills audit [path]` reports both skill corpora against the rules `standards/skill.md` and `standards/skill-
|
|
8
|
+
`canon claude skills audit [path]` reports both skill corpora against the rules `standards/skill.md`, `standards/skill-requirement.md`, and `standards/skill-practice.md` state mechanically. It reads and reports. Fixing what it finds is separate work.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
canon claude skills audit
|
|
@@ -36,6 +36,7 @@ Each check traces to a line in the standard.
|
|
|
36
36
|
- Folder name in kebab-case
|
|
37
37
|
- Each `REQUIREMENT.md` declaring `Gap` and `Must`, matched at any heading level
|
|
38
38
|
- No ISO date in `SKILL.md` or any file under `references/`, reported as `datedProvenance` in the JSON record's `findings`
|
|
39
|
+
- Each listed practice skill closing with `## Excuses and rebuttals`, `## Red flags`, and `## Before handing over`, and carrying `references/adopted.md`, reported as `practiceShape`
|
|
39
40
|
|
|
40
41
|
A body whose frontmatter does not parse reports as declaring neither field rather than ending the run, so one malformed file cannot hide the corpus behind it. A key present with an empty value reads as absent, since a blank `name` would otherwise report as a name disagreeing with every folder. A folder carrying no requirement is reported once under presence rather than counted again for the sections it therefore lacks.
|
|
41
42
|
|
|
@@ -43,6 +44,8 @@ The date check reads prose alone. A date inside a fence or a code span is exampl
|
|
|
43
44
|
|
|
44
45
|
Each finding names the file and the line, such as `claude/skills/<name>/references/<file>.md line 12: <date>`. A date after "measured" or "verified" reports like any other, unlike the context audit, because a skill body states the rule a session follows today rather than a reading of the tree.
|
|
45
46
|
|
|
47
|
+
The practice check reads only the skills the audit lists, which the JSON record carries as `checkpoints.practiceSkills`. The list names folders under the toolkit's shipped `claude/skills/` corpus alone, so a project's own skill is never read as a practice skill and a same-named folder under `.claude/skills/` produces no finding. Where `claude/skills/` resolves, a listed folder it does not hold reports as a missing skill, so a renamed or misspelled entry cannot pass unread. Each closing section matches only as an exact H2 outside a fence, so an H3 or a heading carrying more words reports as missing.
|
|
48
|
+
|
|
46
49
|
## What it leaves alone
|
|
47
50
|
|
|
48
51
|
The report names its own blind spots on every run, including the run where everything passed. Whether each `Must` traces to a stated gap is the rule in the requirement standard worth the most and no parser reads it. Whether a gap states an observed failure rather than an intent, and whether a description routes, are the same kind of judgment. The 150-line body checkpoint is the one mechanical rule still absent here, and adding it would print a count rather than a defect, since the standard makes it a prompt to look with nothing enforcing it.
|
|
@@ -53,7 +56,7 @@ A check with no rule behind it prints an opinion as a defect, which is where the
|
|
|
53
56
|
|
|
54
57
|
## Exit codes
|
|
55
58
|
|
|
56
|
-
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a skill folder carrying no `REQUIREMENT.md`. Only presence sets a failing code. Name, description, folder, requirement-section,
|
|
59
|
+
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a skill folder carrying no `REQUIREMENT.md`. Only presence sets a failing code. Name, description, folder, requirement-section, dated-provenance, and practice shape findings print and return `0`, because each is a judgment a reader settles and failing a push on one would make the check something to route around. In the toolkit, two `bun run check` stages read the same report in-process and fail a push on dated provenance and on practice shape, since its own corpus is held at zero on both.
|
|
57
60
|
|
|
58
61
|
## The requirement gate
|
|
59
62
|
|
package/package.json
CHANGED
|
@@ -12,16 +12,40 @@ export const DESCRIPTION_LIMIT = 1024
|
|
|
12
12
|
/** The two headings the requirement template declares, matched at any level. */
|
|
13
13
|
export const REQUIREMENT_SECTIONS: readonly string[] = ['Gap', 'Must']
|
|
14
14
|
|
|
15
|
+
/** The one tree that ships to a target, and the only one the practice list names. */
|
|
16
|
+
export const SHIPPED_CORPUS = join('claude', 'skills')
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* Both trees the standard governs. `claude/skills/` ships to a target and
|
|
17
20
|
* `.claude/skills/` stays here, and every rule measured below applies to each,
|
|
18
21
|
* so a corpus reading one of them reports a pass over half the subject.
|
|
19
22
|
*/
|
|
20
23
|
export const CORPORA: readonly string[] = [
|
|
21
|
-
|
|
24
|
+
SHIPPED_CORPUS,
|
|
22
25
|
join('.claude', 'skills'),
|
|
23
26
|
]
|
|
24
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The practice skills `standards/skill-practice.md` describes, keyed by corpus-relative
|
|
30
|
+
* folder rather than by name, so a target's own `.claude/skills/` folder that
|
|
31
|
+
* shares a name is never swept in. The list lives here rather than in a skill's
|
|
32
|
+
* frontmatter because a skill declaring its own kind could exempt itself.
|
|
33
|
+
*/
|
|
34
|
+
export const PRACTICE_SKILLS: readonly string[] = [
|
|
35
|
+
join(SHIPPED_CORPUS, 'codebase-layout'),
|
|
36
|
+
join(SHIPPED_CORPUS, 'test-craft'),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
/** The closing H2s a practice skill carries, matched exactly outside fences. */
|
|
40
|
+
export const PRACTICE_SECTIONS: readonly string[] = [
|
|
41
|
+
'Excuses and rebuttals',
|
|
42
|
+
'Red flags',
|
|
43
|
+
'Before handing over',
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
/** The source ledger every practice skill carries beside its body. */
|
|
47
|
+
export const PRACTICE_LEDGER = join('references', 'adopted.md')
|
|
48
|
+
|
|
25
49
|
/**
|
|
26
50
|
* The one reason this audit refuses. A project carrying neither corpus is the
|
|
27
51
|
* ordinary state of a target that has not adopted either skills convention,
|
|
@@ -34,6 +58,8 @@ const KEBAB_CASE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
|
|
|
34
58
|
|
|
35
59
|
const HEADING = /^#{1,6}\s+(.+?)\s*$/
|
|
36
60
|
|
|
61
|
+
const SECTION = /^##\s+(.+?)\s*$/
|
|
62
|
+
|
|
37
63
|
/**
|
|
38
64
|
* An ISO date, read as provenance wherever a reader is told it. The context
|
|
39
65
|
* audit's detector clears a date after "measured" or "verified", and a skill
|
|
@@ -69,6 +95,7 @@ export interface SkillsAudit {
|
|
|
69
95
|
readonly longDescription: readonly SkillFinding[]
|
|
70
96
|
readonly requirementSections: readonly SkillFinding[]
|
|
71
97
|
readonly datedProvenance: readonly SkillFinding[]
|
|
98
|
+
readonly practiceShape: readonly SkillFinding[]
|
|
72
99
|
}
|
|
73
100
|
|
|
74
101
|
interface SkillSource {
|
|
@@ -79,6 +106,9 @@ interface SkillSource {
|
|
|
79
106
|
readonly description: string | undefined
|
|
80
107
|
/** Undefined when the folder carries no `REQUIREMENT.md` at all. */
|
|
81
108
|
readonly requirementHeadings: readonly string[] | undefined
|
|
109
|
+
/** Every H2 in `SKILL.md` outside a fence, for the practice shape. */
|
|
110
|
+
readonly sections: readonly string[]
|
|
111
|
+
readonly hasLedger: boolean
|
|
82
112
|
/**
|
|
83
113
|
* `SKILL.md` and every reference below the folder, keyed by the path a
|
|
84
114
|
* finding names. `REQUIREMENT.md` and `EVAL.md` are left out, since the
|
|
@@ -134,6 +164,13 @@ export async function auditSkills(root: string): Promise<SkillsAudit> {
|
|
|
134
164
|
datedProvenance: sources.flatMap((source) =>
|
|
135
165
|
source.bodies.flatMap(dateFindings),
|
|
136
166
|
),
|
|
167
|
+
practiceShape: [
|
|
168
|
+
...missingPracticeSkills(
|
|
169
|
+
present.some((corpus) => corpus.rel === SHIPPED_CORPUS),
|
|
170
|
+
sources,
|
|
171
|
+
),
|
|
172
|
+
...sources.flatMap(practiceFindings),
|
|
173
|
+
],
|
|
137
174
|
}
|
|
138
175
|
}
|
|
139
176
|
|
|
@@ -190,6 +227,11 @@ async function readSkill(
|
|
|
190
227
|
description: declared(readField(fields, 'description')),
|
|
191
228
|
requirementHeadings:
|
|
192
229
|
requirement === undefined ? undefined : headings(requirement),
|
|
230
|
+
sections: bodyLines(body)
|
|
231
|
+
.filter((line) => !line.fenced)
|
|
232
|
+
.map((line) => SECTION.exec(line.text)?.[1])
|
|
233
|
+
.filter((text): text is string => text !== undefined),
|
|
234
|
+
hasLedger: existsSync(join(skillDir, PRACTICE_LEDGER)),
|
|
193
235
|
bodies: [
|
|
194
236
|
{ rel: join(skillRel, 'SKILL.md'), text: body },
|
|
195
237
|
...references.map((path, index) => ({
|
|
@@ -252,6 +294,44 @@ function sectionFindings(source: SkillSource): SkillFinding[] {
|
|
|
252
294
|
]
|
|
253
295
|
}
|
|
254
296
|
|
|
297
|
+
/**
|
|
298
|
+
* A listed folder the shipped corpus does not hold, such as a renamed skill or
|
|
299
|
+
* a misspelled append, would otherwise read as a pass. Silent where the
|
|
300
|
+
* shipped corpus is absent, since a target carries none of the listed skills.
|
|
301
|
+
*/
|
|
302
|
+
function missingPracticeSkills(
|
|
303
|
+
hasShippedCorpus: boolean,
|
|
304
|
+
sources: readonly SkillSource[],
|
|
305
|
+
): SkillFinding[] {
|
|
306
|
+
if (!hasShippedCorpus) return []
|
|
307
|
+
const found = new Set(sources.map((source) => source.rel))
|
|
308
|
+
return PRACTICE_SKILLS.filter((rel) => !found.has(rel)).map((rel) => ({
|
|
309
|
+
rel,
|
|
310
|
+
detail: 'missing skill: no folder under the shipped corpus',
|
|
311
|
+
}))
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Matches each section as an exact H2, so a heading carrying trailing text or
|
|
316
|
+
* sitting at another level reads as missing rather than as a near match the
|
|
317
|
+
* standard never names.
|
|
318
|
+
*/
|
|
319
|
+
function practiceFindings(source: SkillSource): SkillFinding[] {
|
|
320
|
+
if (!PRACTICE_SKILLS.includes(source.rel)) return []
|
|
321
|
+
|
|
322
|
+
const sections = PRACTICE_SECTIONS.filter(
|
|
323
|
+
(section) => !source.sections.includes(section),
|
|
324
|
+
).map((section) => ({
|
|
325
|
+
rel: source.rel,
|
|
326
|
+
detail: `missing section: ${section}`,
|
|
327
|
+
}))
|
|
328
|
+
const ledger = source.hasLedger
|
|
329
|
+
? []
|
|
330
|
+
: [{ rel: source.rel, detail: `missing ledger: ${PRACTICE_LEDGER}` }]
|
|
331
|
+
|
|
332
|
+
return [...sections, ...ledger]
|
|
333
|
+
}
|
|
334
|
+
|
|
255
335
|
/**
|
|
256
336
|
* A date inside a fence or a code span is example data a reader is shown, such
|
|
257
337
|
* as a sample frontmatter block, so only prose outside both is read.
|
package/src/commands/claude.ts
CHANGED
|
@@ -26,7 +26,11 @@ import {
|
|
|
26
26
|
auditSkills,
|
|
27
27
|
CORPORA,
|
|
28
28
|
DESCRIPTION_LIMIT,
|
|
29
|
+
PRACTICE_LEDGER,
|
|
30
|
+
PRACTICE_SECTIONS,
|
|
31
|
+
PRACTICE_SKILLS,
|
|
29
32
|
REQUIREMENT_SECTIONS,
|
|
33
|
+
SHIPPED_CORPUS,
|
|
30
34
|
type SkillFinding,
|
|
31
35
|
type SkillsAudit,
|
|
32
36
|
type SkillsAuditRefusal,
|
|
@@ -1398,6 +1402,7 @@ async function runSkillsAudit(
|
|
|
1398
1402
|
reportFolder(report)
|
|
1399
1403
|
reportRequirementShape(report)
|
|
1400
1404
|
reportProvenance(report)
|
|
1405
|
+
reportPracticeShape(report)
|
|
1401
1406
|
reportUnmeasured()
|
|
1402
1407
|
outro()
|
|
1403
1408
|
}
|
|
@@ -1420,11 +1425,14 @@ async function runSkillsAudit(
|
|
|
1420
1425
|
folderName: report.folderName,
|
|
1421
1426
|
requirementSections: report.requirementSections,
|
|
1422
1427
|
datedProvenance: report.datedProvenance,
|
|
1428
|
+
practiceShape: report.practiceShape,
|
|
1423
1429
|
},
|
|
1424
1430
|
checkpoints: {
|
|
1425
1431
|
descriptionLimit: DESCRIPTION_LIMIT,
|
|
1426
1432
|
requirementSections: REQUIREMENT_SECTIONS,
|
|
1427
1433
|
corpora: CORPORA,
|
|
1434
|
+
practiceSkills: PRACTICE_SKILLS,
|
|
1435
|
+
practiceSections: PRACTICE_SECTIONS,
|
|
1428
1436
|
},
|
|
1429
1437
|
})}\n`,
|
|
1430
1438
|
)
|
|
@@ -1615,6 +1623,31 @@ function reportProvenance(report: SkillsAudit): void {
|
|
|
1615
1623
|
reportFindings(report.datedProvenance)
|
|
1616
1624
|
}
|
|
1617
1625
|
|
|
1626
|
+
/**
|
|
1627
|
+
* Reports without failing the run for the reason provenance does. The list
|
|
1628
|
+
* names shipped folders alone, which the step says so a target reading a pass
|
|
1629
|
+
* knows its own skills were never in scope.
|
|
1630
|
+
*/
|
|
1631
|
+
function reportPracticeShape(report: SkillsAudit): void {
|
|
1632
|
+
logStep('Practice shape')
|
|
1633
|
+
logInfo(
|
|
1634
|
+
`A listed practice skill closes with ${PRACTICE_SECTIONS.map((section) => `## ${section}`).join(', ')}, and carries ${PRACTICE_LEDGER}.`,
|
|
1635
|
+
)
|
|
1636
|
+
logInfo(
|
|
1637
|
+
`The list is kept here and names ${plural(PRACTICE_SKILLS.length, 'shipped skill')}, so a skill outside ${SHIPPED_CORPUS} is never read.`,
|
|
1638
|
+
)
|
|
1639
|
+
|
|
1640
|
+
if (report.practiceShape.length === 0) {
|
|
1641
|
+
logInfo('Every listed practice skill carries its shape.')
|
|
1642
|
+
return
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
logWarn(
|
|
1646
|
+
`${plural(report.practiceShape.length, 'missing part')} across the listed practice skills`,
|
|
1647
|
+
)
|
|
1648
|
+
reportFindings(report.practiceShape)
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1618
1651
|
/**
|
|
1619
1652
|
* Stated on every run, including the run where everything above passed. A
|
|
1620
1653
|
* report that lists only what it measured reads as a verdict on the standard
|
package/src/gate/measures.ts
CHANGED
|
@@ -13,7 +13,12 @@ import {
|
|
|
13
13
|
measureArchitecture,
|
|
14
14
|
missingRevisit,
|
|
15
15
|
} from '@/context/architecture'
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
auditSkills,
|
|
18
|
+
PRACTICE_LEDGER,
|
|
19
|
+
PRACTICE_SKILLS,
|
|
20
|
+
SHIPPED_CORPUS,
|
|
21
|
+
} from '@/claude/skills-audit'
|
|
17
22
|
import { listRepositoryFiles } from '@/git-files'
|
|
18
23
|
import { ceilingFindings } from '@/markdown/ceiling'
|
|
19
24
|
import { resolveMarkdown } from '@/markdown/files'
|
|
@@ -314,6 +319,43 @@ export const skillProvenance: Measure = async (ctx) => {
|
|
|
314
319
|
}
|
|
315
320
|
}
|
|
316
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Every listed practice skill against the closing sections and the ledger
|
|
324
|
+
* `standards/skill-practice.md` requires of one.
|
|
325
|
+
*
|
|
326
|
+
* Read in-process for the reason `skillProvenance` is, so the verb keeps its
|
|
327
|
+
* missing-requirement-only exit. The list names shipped folders alone, so a
|
|
328
|
+
* tree without `claude/skills/` reports unmeasured rather than a vacuous pass.
|
|
329
|
+
*/
|
|
330
|
+
export const skillPracticeShape: Measure = async (ctx) => {
|
|
331
|
+
const report = await auditSkills(ctx.root)
|
|
332
|
+
if (!report.corpora.some((corpus) => corpus.rel === SHIPPED_CORPUS)) {
|
|
333
|
+
return {
|
|
334
|
+
emissions: [],
|
|
335
|
+
unmeasured: `no skill corpus at ${SHIPPED_CORPUS} resolved, so no practice skill was read.`,
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const found = report.practiceShape
|
|
340
|
+
if (found.length > 0) {
|
|
341
|
+
const where = found
|
|
342
|
+
.map((finding) => `${finding.rel} ${finding.detail}`)
|
|
343
|
+
.join(', ')
|
|
344
|
+
return {
|
|
345
|
+
emissions: [],
|
|
346
|
+
failure: `${plural(found.length, 'practice shape finding')}: ${where}. Add the closing H2 exactly as named, or the ledger at ${PRACTICE_LEDGER}.`,
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
emissions: [
|
|
352
|
+
info(
|
|
353
|
+
`${plural(PRACTICE_SKILLS.length, 'practice skill')} carry every closing section and a ledger`,
|
|
354
|
+
),
|
|
355
|
+
],
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
317
359
|
/**
|
|
318
360
|
* Every tracked markdown document against the whole-document ceiling.
|
|
319
361
|
*
|
package/src/gate/stages.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
seedStandards,
|
|
15
15
|
rawFieldFileReference,
|
|
16
16
|
shippedReferences,
|
|
17
|
+
skillPracticeShape,
|
|
17
18
|
skillProvenance,
|
|
18
19
|
standardCriteria,
|
|
19
20
|
unreferencedRules,
|
|
@@ -486,6 +487,14 @@ export const STAGES: readonly Stage[] = [
|
|
|
486
487
|
label: 'Skill provenance',
|
|
487
488
|
checks: [{ kind: 'measure', measure: skillProvenance }],
|
|
488
489
|
},
|
|
490
|
+
{
|
|
491
|
+
// The audit verb only reports a listed practice skill missing a closing
|
|
492
|
+
// section or its ledger, so a target is told without failing. The list
|
|
493
|
+
// names this repository's shipped skills, so the stage fails here alone.
|
|
494
|
+
id: 'skill-practice-shape',
|
|
495
|
+
label: 'Skill practice shape',
|
|
496
|
+
checks: [{ kind: 'measure', measure: skillPracticeShape }],
|
|
497
|
+
},
|
|
489
498
|
{
|
|
490
499
|
id: 'standard-criteria',
|
|
491
500
|
label: 'Standard success criteria',
|
package/standards/index.md
CHANGED
|
@@ -31,6 +31,7 @@ Reference docs for consistent authoring across the toolkit and target projects.
|
|
|
31
31
|
- [Requirements reference](requirements.md): Shape and content rules for canon/REQUIREMENTS.md
|
|
32
32
|
- [Governance rule reference](rule.md): Rule frontmatter, body shape, and voice for .claude/rules files
|
|
33
33
|
- [Session map reference](session.md): Filename and location, the sections a handoff carries, the write and read procedures, and how a role extends it
|
|
34
|
+
- [Practice skill reference](skill-practice.md): Closing sections and source ledger for a skill carrying engineering practice
|
|
34
35
|
- [Skill requirement reference](skill-requirement.md): Shape and content rules for the REQUIREMENT.md beside a skill
|
|
35
36
|
- [Claude skill reference](skill.md): Claude skill structure and authoring rules
|
|
36
37
|
- [Slug reference](slug.md): Transform from a git branch name to a slug, and the three responses to an empty result
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Practice skill reference
|
|
3
|
+
description: Closing sections and source ledger for a skill carrying engineering practice
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Practice skill reference
|
|
7
|
+
|
|
8
|
+
Applies to a practice skill, one carrying engineering judgment a session applies while it works, where the failure is skipping the practice or cutting it short rather than not knowing it. Its body takes the reference shape, and this file fixes how it closes and what it cites.
|
|
9
|
+
|
|
10
|
+
## Scope
|
|
11
|
+
|
|
12
|
+
Governs the closing sections of a practice skill's `skills/<name>/SKILL.md` and the source ledger at `skills/<name>/references/adopted.md` beside it.
|
|
13
|
+
|
|
14
|
+
Does not govern:
|
|
15
|
+
|
|
16
|
+
- The skill folder, its frontmatter, the body rules, and the choice between skill types: `skill.md`
|
|
17
|
+
- The `REQUIREMENT.md` beside the body: `skill-requirement.md`
|
|
18
|
+
- Voice, rhythm, and sentence construction: the `write-human` skill
|
|
19
|
+
- Punctuation, formatting, and word choice: `markdown.md`
|
|
20
|
+
|
|
21
|
+
## What a working practice skill looks like
|
|
22
|
+
|
|
23
|
+
A practice skill works when a session about to cut the practice short meets the answer before it does:
|
|
24
|
+
|
|
25
|
+
- Does every excuse row name a reason a session has actually given to skip the practice?
|
|
26
|
+
- Can a session spot each red flag in its own work while it is still working?
|
|
27
|
+
- Can a session run every line of the closing checklist against what it produced, and get a yes or a no?
|
|
28
|
+
- Can a reader open each source the ledger names and find the version the body states?
|
|
29
|
+
|
|
30
|
+
A practice skill failing any of these is non-conforming even when every heading below is present.
|
|
31
|
+
|
|
32
|
+
## Closing sections
|
|
33
|
+
|
|
34
|
+
- Follow every concern group with three H2s in this order: `## Excuses and rebuttals`, `## Red flags`, and `## Before handing over`. A section naming what the skill delegates comes after the three, and nothing else does.
|
|
35
|
+
- Spell each heading exactly. An H3, or a heading carrying more words such as `## Red flags that mean start over`, reads as missing.
|
|
36
|
+
- Write `## Excuses and rebuttals` as a two-column table pairing a reason a session gives to skip or shortcut the practice with the answer to it. Never write a row rebutting an objection nobody raised, since that row is ceremony. Revisit when a practice skill's table is observed failing to stop an excuse it names.
|
|
37
|
+
- Write `## Red flags` as bullets naming signs a session can observe in its own work mid-task, never a restatement of a rule above.
|
|
38
|
+
- Write `## Before handing over` as the exit checklist, each line a check against what the session produced.
|
|
39
|
+
|
|
40
|
+
## Source ledger
|
|
41
|
+
|
|
42
|
+
- Carry `references/adopted.md` in every practice skill, recording each external source the body draws on.
|
|
43
|
+
- Link each source, or pin it to a commit, so a reader can open the version the body adopted.
|
|
44
|
+
- State what was adopted, what was declined, and why for each, so a later session extends the position rather than re-deriving it.
|
|
45
|
+
- Draw first from the default shelf, [Software Engineering at Google](https://abseil.io/resources/swe-book/html/toc.html) and [Google's engineering practices](https://google.github.io/eng-practices/), and record a source from elsewhere the same way.
|
|
46
|
+
|
|
47
|
+
## Which skills are practice skills
|
|
48
|
+
|
|
49
|
+
- `canon claude skills audit` keeps the list of practice skills and checks each listed one for the three closing sections and the ledger.
|
|
50
|
+
- Never mark a skill as a practice skill in its own frontmatter, since a self-declared kind lets a skill exempt itself.
|
|
51
|
+
- The list names this toolkit's shipped skills alone, so a project's own practice skill takes this shape with no check behind it.
|
package/standards/skill.md
CHANGED
|
@@ -16,6 +16,7 @@ Governs a skill folder under `skills/` as one artifact: `SKILL.md`, its siblings
|
|
|
16
16
|
Does not govern:
|
|
17
17
|
|
|
18
18
|
- What a `REQUIREMENT.md` must answer, its sections, and its template: `skill-requirement.md`
|
|
19
|
+
- A practice skill's closing sections and ledger: `skill-practice.md`
|
|
19
20
|
- Path-scoped coding rules, which load on a file match rather than on a request match: `rule.md`
|
|
20
21
|
- Voice, rhythm, and sentence construction in a skill body: the `write-human` skill
|
|
21
22
|
- Punctuation, formatting, and word choice in a skill body: `markdown.md`
|
|
@@ -45,6 +46,7 @@ Pick the type before writing. It decides the body shape.
|
|
|
45
46
|
|
|
46
47
|
- Reference: conventions, patterns, and domain knowledge Claude applies inline. The body is rule bullets grouped by concern.
|
|
47
48
|
- Task: step-by-step workflows Claude executes as actions. The body is numbered steps plus the rules constraining them.
|
|
49
|
+
- Practice: judgment a session may skip, shaped by `skill-practice.md`.
|
|
48
50
|
|
|
49
51
|
A task skill takes the template below. A reference skill takes the same frontmatter and replaces `## Steps` and `## Rules` with one H2 per concern group, each holding constraint bullets.
|
|
50
52
|
|
|
@@ -105,7 +107,7 @@ allowed-tools: <tools required>
|
|
|
105
107
|
- Group bullets under H2 headings by domain concern. Keep dos and don'ts together under the topic they belong to rather than splitting them into flat rules and constraints sections.
|
|
106
108
|
- One actionable constraint per bullet. Prefer the `X over Y` form for preferences.
|
|
107
109
|
- Do not include code examples unless a one to three line inline snippet captures a pattern the model cannot infer.
|
|
108
|
-
-
|
|
110
|
+
- Keep a statement only when removing it would change what the agent does, naming a principle as a few-word handle rather than explaining it. Revisit when a run shows the model ignoring a principle named only as a handle.
|
|
109
111
|
|
|
110
112
|
### Progressive disclosure
|
|
111
113
|
|