@erclx/canon 4.107.0 → 4.108.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "4.107.0",
4
+ "version": "4.108.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: document-health
3
+ description: What the document health check is for, the gap it closes, and why it reports three axes rather than one number
4
+ ---
5
+
6
+ # Document health requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a project learns which documents are missing and never learns which have rotted. `target-check` reports per domain what a target holds against what the toolkit ships, and every one of its domains asks a structural question: does the file exist, does it hold the required sections, do the bytes match what shipped. A document that exists, holds every section, and describes a tree that moved six months ago passes every one of them.
11
+
12
+ The second half of the gap is the axes being read as one thing. Length, placement, and staleness answer to different evidence, and a run that reports them in one undifferentiated list hands the reader a judgment wearing the authority of a measurement. The operator's own complaint was staleness, which is the axis with nothing behind it, so the flattening runs in the direction that overstates the weakest finding.
13
+
14
+ The third is silence on an unread axis. Both measured verbs resolve their folders by surface root, so a project whose layout differs resolves nothing and the verb refuses. A run reading that refusal as an empty finding set reports a healthy corpus for documents nothing looked at.
15
+
16
+ ## Must
17
+
18
+ - Report per document across three named axes, so a reader can tell which question a finding answers
19
+ - Read length and placement through the verb that already owns each measurement, rather than re-deriving either by hand
20
+ - Mark every finding with its evidence class, so a judgment is never read as a measurement
21
+ - Ship the staleness axis, and mark it as a reading rather than cutting it for lacking a verb
22
+ - Report a refusal, an absent key, and a missing subcommand as an unread axis, naming the cause
23
+ - Say which layer answered the classifier sweep, since its model layer being off narrows the read without refusing it
24
+ - Take every checkpoint from the record the verb returns rather than from a number written into the skill
25
+ - Run standalone, and stay callable from the structural check rather than reachable only through it
26
+
27
+ ## Must not
28
+
29
+ - Repair a finding, write a file in the project, or open a pull request. A check that repaired as it read leaves the reader unable to tell a finding from a fix.
30
+ - Emit a verdict across the three axes, or a score across the documents. Two axes answer from verbs and one answers from a reading, so any roll-up is a figure that lies about the third.
31
+ - Report a finding against a file nobody hand-authors. A generated index, a gitignored file, and a copied reference each name a file an author cannot fix.
32
+ - Answer an axis by file inspection where a verb answers it. The verb is the surface under test and a hand-rolled read of the same files is not.
33
+ - Read a document's modification time as its staleness. A file untouched beside code untouched is current, and the timestamp answers a different question.
34
+
35
+ ## Guards
36
+
37
+ - `canon` absent from the path: stop, since neither measured axis can be read
38
+ - No tracked markdown under the working root: stop, since there is no corpus to read
39
+
40
+ ## Out of scope
41
+
42
+ - Whether a document exists or holds its required sections, which is the structural question `target-check` answers across six domains
43
+ - Conformance of changed markdown to the authoring standards declaring jurisdiction over its paths, which is `standards-audit`. That skill is diff-scoped and standard-declared, taking a changed file and reporting which stated rule it breaks with a line number. This one is corpus-scoped and reads rot in documents nobody changed, which is the case a diff-scoped audit never reaches. The two sit beside each other rather than one absorbing the other.
44
+ - Rewriting a stale claim once this run has named it, which is `markdown-propose`
45
+ - Running the toolkit's own checks as one gating set over its own corpus, which is `canon audits run`. That set gates a push and this reports to a reader.
46
+ - Repairing anything an axis names. Each routes to the command or skill that owns it.
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: document-health
3
+ description: Reports which of a project's documents have rotted, one section per document, across length, placement, and staleness. Reads each axis through the verb that owns it and marks the evidence class each answer carries. Use when asked to "check document health", "which docs have gone stale", "audit the docs for rot", "are these documents still accurate", "which docs are too long", or after a target check reports the structural side. Do NOT use to repair a finding, which the owning command or skill does, and do NOT use to check whether a document exists or holds the right sections, which is `target-check`.
4
+ ---
5
+
6
+ # Document health
7
+
8
+ Reports which documents have rotted rather than which are missing. `target-check` answers whether a project's documents exist and hold the right kind of content, and this answers whether the ones that exist still earn their place. It reports and never repairs, matching the structural check beside it.
9
+
10
+ Three axes carry the report. Two answer from a verb and the third answers from a reading, which it says rather than hiding. `${CLAUDE_SKILL_DIR}/references/axes.md` holds what each axis measures, the checkpoint it reads against, and where a finding routes. This body holds the order, the guards, and the report shape.
11
+
12
+ ## Guards
13
+
14
+ - `canon` absent from the path: stop. `❌ canon is not on PATH, so neither measured axis can be read.`
15
+ - `git ls-files '*.md'` listing nothing: stop. `❌ No tracked markdown here, so there is no corpus to read.`
16
+ - Run every command under `CANON_NON_INTERACTIVE=1` and prompt at no point. A caller reaching this from a rollout wave has nobody present to answer.
17
+ - Branch on each record's own fields rather than on an exit status. An operator's shell profile may wrap `canon` in a function whose status comes from a trailing command, which reports every refusal as success.
18
+ - Repair nothing, write no file in the project, and open no pull request. Whoever read the report decides what to fix.
19
+
20
+ ## Step 1: scope the corpus
21
+
22
+ Take the documents the project hand-authors, defaulting to every tracked markdown file the two measured verbs reach. Narrow to a path the caller named when one was given, and say which scope the run took.
23
+
24
+ Drop what nobody hand-authors before reporting a finding against it: a generated `index.md`, a file under a gitignored folder, and a bundled reference a generator copied out to its consumers. A finding against a copy names a file an author cannot fix, since the next regeneration overwrites it.
25
+
26
+ ## Step 2: read the two measured axes
27
+
28
+ Run these from the project root, in parallel:
29
+
30
+ ```bash
31
+ CANON_NON_INTERACTIVE=1 canon markdown audit --json
32
+ CANON_NON_INTERACTIVE=1 canon context audit --json
33
+ ```
34
+
35
+ The first answers length, carrying the bullet, paragraph, and depth checkpoints and the per-file weights read against them. The second answers placement for the folders it resolves, carrying entry length, reference form, and index drift.
36
+
37
+ Then read which classifier would run, and deepen placement with the verdicts per section:
38
+
39
+ ```bash
40
+ CANON_NON_INTERACTIVE=1 canon context classifier show
41
+ CANON_NON_INTERACTIVE=1 canon context classify sweep --json
42
+ ```
43
+
44
+ The first names the backend, the model, and the source that decided both. Report which layer answered, since a project with the model layer off reads from the regex layer alone and the sweep says nothing about which one it used.
45
+
46
+ Take the checkpoints from the record each verb returns rather than from a number written here. A checkpoint the CLI moves reaches this report with no edit, and a number held in this body goes stale on its own cadence.
47
+
48
+ ### An unread axis is not a healthy one
49
+
50
+ A refusal, an absent key, and a subcommand the project's binary does not carry each leave an axis unread. Report it as unread against the documents it would have covered, name the read that could not be taken, and name the cause.
51
+
52
+ `canon context audit` refuses with `reason: "no-folders"` in a project whose folders resolve to nothing, which is the ordinary shape where the layout differs from the one the verb looks for. That refusal is the honest answer rather than a failure, and reading it as an empty finding set reports a healthy corpus for documents nothing looked at.
53
+
54
+ A narrower read is not an unread one. The sweep running on its regex layer alone still answered, so it is reported with the layer named rather than as an axis nothing reached.
55
+
56
+ ## Step 3: read the staleness axis
57
+
58
+ No verb answers whether a document still describes the tree it was written about. Read each document against the surfaces it names and report where the two have parted, taking the file paths, commands, and counts it states as the claims to test.
59
+
60
+ Mark every staleness finding as a reading. It is this session's judgment rather than a measurement, and a report that sets it beside two verb-backed axes without marking it implies evidence the axis does not carry.
61
+
62
+ ## Step 4: report per document
63
+
64
+ Give each document its three axes, and give each axis three things: the state, the evidence class, and the command or skill that repairs it. Naming the repair is not running it.
65
+
66
+ The three classes are what keep the axes apart:
67
+
68
+ - `measured`: a verb returned a number read against a stated checkpoint
69
+ - `classified`: a verb returned a verdict carrying its own confidence
70
+ - `read`: this session's judgment, backed by no verb
71
+
72
+ Emit no verdict across the three. Two answer from verbs and one answers from a reading, so a roll-up is a figure that lies about the third. Emit no score across the documents either, for the same reason one level up.
73
+
74
+ ## Output
75
+
76
+ Lead with the table, then one detail block per document carrying a finding:
77
+
78
+ ```markdown
79
+ Document health: `<scope>`, <n> documents read.
80
+
81
+ | Document | Length | Placement | Staleness |
82
+ | ---------------- | ------------------- | ------------------- | ---------------- |
83
+ | `path/to/doc.md` | ok / heavy / unread | ok / moved / unread | ok / stale / n/a |
84
+
85
+ Length and placement are measured. Staleness is a reading.
86
+ ```
87
+
88
+ Keep the evidence line under the table on every run. A table read without it flattens a judgment into a measurement, which is the one shape the three classes exist to prevent.
@@ -0,0 +1,78 @@
1
+ ---
2
+ title: Document health axes
3
+ description: What each axis measures, the verb behind it, the evidence class it carries, and where a finding routes
4
+ ---
5
+
6
+ # Document health axes
7
+
8
+ One section per axis. Each states what it measures, the verb that answers it, the evidence class the answer carries, and the surface a finding routes to.
9
+
10
+ The skill body holds the order, the guards, and the report shape. This holds the detail a run dereferences once it has a finding to place.
11
+
12
+ ## Length
13
+
14
+ **Measures:** whether a document, or a passage inside it, has grown past what a reader takes in one pass.
15
+
16
+ **Verb:** `canon markdown audit --json`.
17
+
18
+ **Evidence class:** `measured`.
19
+
20
+ The record carries the checkpoints it read against under `checkpoints` and the per-file weights under `entries`. Read both from the record rather than from a number stated here, since the CLI moves a checkpoint on its own cadence and a copy of one goes stale in place.
21
+
22
+ Four weights answer this axis, each naming a different shape of overgrowth:
23
+
24
+ - `heavyBullets`: a top-level bullet past the character checkpoint, which is a paragraph wearing a dash
25
+ - `heavyParagraphs`: a paragraph past the sentence or character checkpoint
26
+ - `longestRun`, against `longestRunLine`: unbroken rendered lines between headings, which is the depth checkpoint
27
+ - `cadence`, carrying `measured`, `flat`, and `repeating`: sentence-length spread and repeated openers
28
+
29
+ Weight is a judgment a reader settles rather than a defect, which the verb states for itself and which this axis carries forward. A finding here is an argument for a split or a trim rather than a rule broken.
30
+
31
+ **Routes to:** a split of the document, or a move of the overgrown passage to the surface that owns it. Where the project states what a given document may hold, that statement decides whether the passage belongs in it at all, and the finding routes there rather than to a trim.
32
+
33
+ **Caveat:** the cadence numbers are drawn from prose a person reads straight through. Terse reference prose sits below that range correctly, so a flat paragraph in a table-heavy or bullet-heavy file is not a finding.
34
+
35
+ ## Placement
36
+
37
+ **Measures:** whether a document's content sits in the file that owns it, and whether the catalogs pointing at it still resolve.
38
+
39
+ **Verbs:** `canon context audit --json` for the structural half, `canon context classify sweep --json` for the per-section verdicts.
40
+
41
+ **Evidence class:** `measured` for the audit's findings, `classified` for the sweep's verdicts.
42
+
43
+ The audit answers required sections, entry length, citation resolution, reference form, catalog tables, provenance, and index drift. A citation resolving to nothing is a fact and gates. Entry length, reference form, table shape, and provenance are judgments under every flag.
44
+
45
+ The sweep classifies each section as keep, replace or rewrite, history, or move. A `move` verdict is the placement finding proper, naming content sitting in the wrong document. Each verdict carries its own confidence, which is why the class is `classified` rather than `measured`, and a low-confidence `move` is a prompt to look rather than an instruction to cut.
46
+
47
+ **Routes to:** the document the verdict names as the owner. Where the move crosses a tier rather than a file, the project's own content-ownership rules decide the destination.
48
+
49
+ **Caveat:** both verbs resolve their folders by surface root rather than by a path written into them, so a project whose layout differs from the one they look for resolves nothing. The audit says so, refusing with `reason: "no-folders"` and naming the folders it looked for. Report that refusal as an unread axis. The sweep degrades differently: with the model layer off it answers from its regex layer alone, which is a narrower read rather than a refusal, so say which layer answered.
50
+
51
+ ## Staleness
52
+
53
+ **Measures:** whether a document still describes the tree it was written about.
54
+
55
+ **Verb:** none.
56
+
57
+ **Evidence class:** `read`.
58
+
59
+ This is the axis the operator names first and the one nothing measures. A document goes stale by the tree moving under it rather than by anything changing in the document, so no property of the file answers it and a timestamp answers a different question. A file untouched for a year beside code untouched for a year is current, and one edited yesterday against a refactor from this morning is stale.
60
+
61
+ Read each document against the surfaces it names. Four claim shapes are testable without leaving the project:
62
+
63
+ - A file or folder path the document states, against whether it resolves
64
+ - A command or verb the document names, against whether it exists and carries the flags shown
65
+ - A count the document states over a tree, against the tree
66
+ - A cross-reference to another document, against what that document now says
67
+
68
+ A claim resting on none of the four is unverifiable from here. Report it as unread rather than as passing, which is the same rule the measured axes take when a verb refuses.
69
+
70
+ **Routes to:** the skill that rewrites a stale claim against an answer, rather than a direct edit from here. A document health run reports and never repairs.
71
+
72
+ **Caveat:** a reading is the weakest evidence in the report and the most consequential finding in it, which is the tension this axis ships with. Marking the class is what keeps the two facts together in the reader's hands.
73
+
74
+ ## Why the three stay apart
75
+
76
+ The axes are not equally actionable and a report flattening them implies they are. A length finding is a number against a checkpoint, a placement finding is a verdict carrying a confidence, and a staleness finding is a judgment with nothing behind it. Each is worth reporting and each is worth a different response, so the class travels beside the state on every row rather than being stated once in a preamble a reader skims.
77
+
78
+ Shipping staleness on weaker evidence than the other two is the trade taken deliberately. Cutting it would leave the report answering the two questions that were never the operator's complaint.
@@ -240,6 +240,7 @@ This section is the corpus the coverage claim is measured against: every name `c
240
240
  | ------------------------ | --------------------------------------------------------------------------------------- |
241
241
  | `canon:review-branch` | On the local branch diff, before anything is pushed |
242
242
  | `canon:standards-audit` | When changed markdown has to answer to the authoring standards |
243
+ | `canon:document-health` | When the documents themselves have to answer for length, placement, and staleness |
243
244
  | `canon:markdown-propose` | When a markdown claim needs rewriting and the change should wait for an answer per file |
244
245
  | `canon:ux-audit` | To read UI source for missing states, edge cases, and inconsistencies |
245
246
  | `canon:ux-measure` | To start the interface and measure paint, processor, and layout cost |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "4.107.0",
4
+ "version": "4.108.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -117,6 +117,16 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
117
117
  'Check whether the markdown I changed violates any authoring standards.',
118
118
  expect: 'standards-audit',
119
119
  },
120
+ // Rot across documents nobody changed is what separates this from
121
+ // `standards-audit`, which is diff-scoped and reports a stated rule broken on
122
+ // a given line. It is also not `target-check`, which asks whether a document
123
+ // exists and holds its sections rather than whether it still describes the
124
+ // tree.
125
+ {
126
+ prompt:
127
+ 'Which of our docs have gone stale, grown too long, or ended up in the wrong file?',
128
+ expect: 'document-health',
129
+ },
120
130
  {
121
131
  prompt: 'Open a new entry on the task board for this piece of work.',
122
132
  expect: 'task-board',