@erclx/canon 5.57.0 → 5.58.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.58.0](https://github.com/erclx/canon/compare/v5.57.0...v5.58.0) (2026-10-24)
4
+
5
+
6
+ ### Features
7
+
8
+ * **governance:** widen design-taste paths to component files ([#2645](https://github.com/erclx/canon/issues/2645)) ([01b1445](https://github.com/erclx/canon/commit/01b144514eced1150c90b18acc78f0be33990a23))
9
+ * **rules:** route interface changes to api-design ([#2647](https://github.com/erclx/canon/issues/2647)) ([09a073c](https://github.com/erclx/canon/commit/09a073ca38324a399de5c2c2e702fbcede6bb38c))
10
+ * **skills:** offer a restructure before document-repair rewrites ([#2648](https://github.com/erclx/canon/issues/2648)) ([7c2776a](https://github.com/erclx/canon/commit/7c2776a95b86a6626c44e399056b9eaa22418d65))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **context:** count table rows toward no folder paragraph ([#2646](https://github.com/erclx/canon/issues/2646)) ([a99b020](https://github.com/erclx/canon/commit/a99b02064105b81bc3a4f6e4820c73198942785e))
16
+
3
17
  ## [5.57.0](https://github.com/erclx/canon/compare/v5.56.0...v5.57.0) (2026-10-24)
4
18
 
5
19
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
- "version": "5.57.0",
4
+ "version": "5.58.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -71,7 +71,7 @@ An entry carries three H2 sections and no other, being `Overview`, `Layout`, and
71
71
 
72
72
  Layout gives each folder doing real work its own H3, titled with the folder path in a code span, such as ``### `src/design/` ``.
73
73
 
74
- - Open the H3 with a one-paragraph lead stating what the folder does, then at most five bullets on what it does now
74
+ - Open the H3 with a one-paragraph lead stating what the folder does, then at most five bullets on what it does now. A table after the lead counts toward neither, with one sentence to lead the bullets
75
75
  - Put every trivial folder under `### Other folders` as a one-line bullet in owns-X form
76
76
  - Name folders only, never files, and write no decisions and no history under any H3
77
77
  - Give each folder under the code roots one H3 or `Other folders` line, since a second entry may cite it but never claims it
@@ -13,8 +13,8 @@ Length, placement, and staleness answer to different evidence, and one list hand
13
13
 
14
14
  ## Must
15
15
 
16
- - Report per document across the length, placement, and staleness axes, marking each finding with its evidence class
17
- - Read length and placement through the verb owning each measurement, and take every checkpoint from the record the verb returns
16
+ - Report per document across the length, placement, and staleness axes, marking each finding with its evidence class, read through the verb owning each measurement and its record's checkpoints
17
+ - Propose a split or an owner as a structure line for each overgrown entry and doubled or unnamed coverage finding, with the headings and the reason
18
18
  - Ship the staleness axis as a reading rather than cutting it for lacking a verb
19
19
  - Report a refusal, an absent key, or a missing subcommand as an unread axis naming the cause, and say which layer answered the classifier sweep
20
20
  - Run standalone, read the requirements record for identity drift when the scope reaches it, and print the `canon records stale canonical` reminder
@@ -47,6 +47,12 @@ The first names the backend, the model, and the source that decided both. Report
47
47
 
48
48
  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.
49
49
 
50
+ ### The structure line
51
+
52
+ The context audit record already carries what a restructure needs, so the placement axis gains a structure line per area folder and runs no further verb. `${CLAUDE_SKILL_DIR}/references/axes.md` holds how to read `length` and `coverage` into a proposed split or owner.
53
+
54
+ Emit the line only for a folder with an entry past the line checkpoint whose Layout headings group into two or more domains, or with a doubled or unnamed coverage finding. A folder with neither gets no line, and `document-repair` goes straight to its rewrite.
55
+
50
56
  ### An unread axis is not a healthy one
51
57
 
52
58
  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.
@@ -106,4 +112,12 @@ When Step 3 read the canonical records, add one reminder line per doc the verb r
106
112
 
107
113
  Write `reviewed value is not a date: <value>` for a doc carrying `invalidReviewed`, and `no release tags to count` in place of the count when `tagged` is false. Name the unread cause instead when the verb refused or the binary carries no `canonical` kind.
108
114
 
115
+ Under the table, add one structure line per area folder that earned one, naming the entry and the split, or the finding and its owner:
116
+
117
+ ```markdown
118
+ Structure `canon/context/surfaces/`: `cli.md` (300 lines) splits into `cli.md` taking <headings> and `cli-<domain>.md` taking <headings>, because <reason>.
119
+ ```
120
+
121
+ The line is a proposal for the operator, with the headings each entry takes and why. Health applies nothing, and `document-repair` offers the proposal before it rewrites.
122
+
109
123
  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.
@@ -54,6 +54,18 @@ The sweep classifies each section as keep, replace or rewrite, history, or move.
54
54
  - A low-confidence `move` is a prompt to look rather than an instruction to cut
55
55
  - **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.
56
56
 
57
+ ### The structure line
58
+
59
+ The audit record's `length` array lists each entry past the line checkpoint, and `coverage.findings` lists the `doubled` and `unnamed` ones. The structure line reads both, so it carries the `measured` class for the finding and `read` for the proposal built on it.
60
+
61
+ - **Overgrown entry:** read its `causes` in `length` first, since `history: yes` points at cutting provenance and one domain at a trim, so neither earns a split
62
+ - **Split:** group the Layout headings by domain, one new entry per group beyond the first, naming each entry's headings and one reason
63
+ - **Domain boundaries:** no verb measures one, so state the grouping as a judgment.
64
+ - **Doubled coverage:** two entries describe one source area. Propose the one that owns it, from which entry already holds the area's traps, and the other points at it.
65
+ - **Unnamed coverage:** no entry names the area. Propose an owner among the existing entries before a new one, since a new entry is the larger change.
66
+
67
+ A proposal names headings and a reason, so an operator judges a concrete split. A split nobody can act on names none of those.
68
+
57
69
  ### When a verb resolves nothing
58
70
 
59
71
  Both verbs resolve their folders by surface root rather than by a path written into them, so a project laid out differently resolves nothing. The two degrade differently.
@@ -15,14 +15,19 @@ A folder repaired by hand also drifts back, since a target gates nothing until a
15
15
 
16
16
  - Each batch takes one folder and leaves it at zero findings or reports it ungated
17
17
  - A folder gates only through `canon markdown gated add`, which refuses one with a finding left
18
- - A rewrite keeps every stated reason, command, and path the original carried
18
+ - A rewrite keeps every stated reason, command, and path the original carried, and leaves a context entry no more shape findings than it started with
19
19
  - The body names the verbs and states no limit number
20
20
  - A requirement gates by its own path, never through a folder prefix
21
21
 
22
+ ## Restructure first
23
+
24
+ An overgrown context folder is offered the restructure `document-health` proposes before any rewrite, and the restructure applies only on the operator's word.
25
+
22
26
  ## Must not
23
27
 
24
28
  - Edit `canon/config/markdown.toml` by hand
25
29
  - Report a folder gated that the verb refused
30
+ - Reach zero density findings by growing the context shape findings
26
31
  - Change what a document claims to pass the audit
27
32
  - Be reachable only by its name, since `document-health` names it for every length finding
28
33
 
@@ -18,6 +18,15 @@ The CLI holds every number. `canon markdown audit` measures, and `canon markdown
18
18
  - `gated add` refusing with `canon-checkout`: stop. Canon's own checkout gates through a constant in its source, which a code change edits.
19
19
  - Branch on each record's own fields rather than on an exit status a shell wrapper can flatten. A call with no terminal on stdin already runs headless, so no command takes a prefix.
20
20
 
21
+ ## Step 0: offer the restructure
22
+
23
+ Restructure an overgrown context folder before rewriting it, so the effort is spent once. Run this step only when `canon/context/` is a candidate, meaning it is not yet listed in `canon/config/markdown.toml`, and skip it silently otherwise.
24
+
25
+ - Take the structure line `document-health` reports for each area folder, or read it from `canon context audit --json` the way that skill does
26
+ - A folder with no overgrown entry and no doubled or unnamed coverage finding has no line, so go straight to Step 1 with no prompt
27
+ - Show the operator each proposal and ask before applying it, since a split moves files other entries cite. Re-run the audit afterward to confirm the citations still resolve.
28
+ - Take a declined proposal as a decision and rewrite the entry as it stands
29
+
21
30
  ## Step 1: read the audit
22
31
 
23
32
  Run the audit over the whole tree and take the density and reference shape findings from its record:
@@ -26,6 +35,12 @@ Run the audit over the whole tree and take the density and reference shape findi
26
35
  canon markdown audit --json
27
36
  ```
28
37
 
38
+ When `canon/context/` is a candidate, read the context shape beside it and keep each entry's `shapeFindings` issues as the baseline Step 3 holds the rewrite to:
39
+
40
+ ```bash
41
+ canon context audit --json
42
+ ```
43
+
29
44
  Take the candidate folders from the order table in `${CLAUDE_SKILL_DIR}/references/batches.md` rather than from the findings, since a clean folder carries no finding and would never be gated. Drop every folder `canon/config/markdown.toml` already lists, since a listed folder already gates.
30
45
 
31
46
  Sort what is left in two. A candidate the findings never name needs no rewrite, so gate it through Step 4 before the first batch. Group the findings under the rest, which Steps 2 and 3 rewrite.
@@ -46,6 +61,12 @@ canon markdown audit <prefix> --json
46
61
 
47
62
  Read `${CLAUDE_SKILL_DIR}/references/batches.md` for what a rewrite may cut. A stated reason survives every rewrite, and the audit reaching zero cannot see one that went missing.
48
63
 
64
+ ### Hold the context shape
65
+
66
+ For a file under a folder the context audit resolves, also re-run `canon context audit --json` and compare the entry's `shapeFindings` issues with the baseline. A density fix that splits a Layout paragraph into several can grow `layout-paragraphs`.
67
+
68
+ A rewrite leaving the entry with more shape issues than it started with is not done. Undo it, or fold the split into a table or a linked page, and report the entry as stopped when neither reaches the baseline. Zero density findings reached by growing shape findings only moves the failure.
69
+
49
70
  ## Step 4: gate the folder
50
71
 
51
72
  ```bash
@@ -84,7 +84,7 @@ What this skill adds on top of the standard:
84
84
  Every `- Suggested:` line takes senior judgment, picking the best option in one line with its reason or main tradeoff, and naming alternatives only when they change the pick.
85
85
 
86
86
  - Suggest a real default when best practice, the codebase, or prior context points to one
87
- - Load `canon:codebase-layout` before `## Files to touch` when an entry names a path that does not exist yet, and give each new path its placement reason. Report it if it does not resolve.
87
+ - Before `## Files to touch`, load `canon:codebase-layout` for a new path (its reason) and `canon:api-design` for a changed verb, flag, output, record shape, or endpoint. Report either if missing.
88
88
  - Fill `## Design references` with the `canon/design/<surface>/` folder for each surface a UI file in `## Files to touch` renders, and write `None identified.` in a project with no folder
89
89
  - Write `## Verification` as one bullet per outcome naming the test or command that proves it
90
90
  - Write `## Review focus` as the inputs or states that would break the change, carrying the risk you understood to the executor and the reviewer
@@ -46,7 +46,7 @@ The shape kinds are `retired-section`, `gotcha-count`, `gotcha-heading`, `gotcha
46
46
 
47
47
  - A `## Decisions` H2, or a `Gotchas` section past five bullets or holding a heading or paragraph
48
48
  - A `Layout` H3 titled neither a code-spanned path nor `Other folders`, or a heading below H3 under `Layout`
49
- - A folder H3 past five bullets or its one-paragraph lead
49
+ - A folder H3 past five bullets or its one-paragraph lead, where table rows and the sentence leading the bullets after a table count toward neither
50
50
 
51
51
  The development entry may carry a `Scripts` H3 exempt from both folder caps. `SHAPE_GATED_FOLDERS` lives in `src/context/shape-gated.ts`.
52
52
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Routes code placement and shape decisions to the skills that carry them, and library calls to the official docs
2
+ description: Routes code placement, shape, and interface decisions to the skills that carry them, and library calls to the official docs
3
3
  ---
4
4
 
5
5
  # Code standards
@@ -12,6 +12,10 @@ description: Routes code placement and shape decisions to the skills that carry
12
12
 
13
13
  - Load the `canon:code-craft` skill before deciding the shape of a new function, class, or module, and report it rather than proceeding silently when it does not resolve.
14
14
 
15
+ ## Interfaces
16
+
17
+ - Load `canon:api-design` before shaping a public function, flag or output, record format, endpoint, or caller-handled error, and report it rather than proceeding silently when it does not resolve.
18
+
15
19
  ## Libraries
16
20
 
17
21
  - Before calling a library API the codebase does not already use, fetch the official docs for the installed version and match the call to them, since recall of an API goes stale.
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  description: Route edits that decide how a surface looks to the design-taste skill for the layer ordering, the coherence locks, and the defaults a model reaches for
3
3
  paths:
4
+ - '**/*.tsx'
5
+ - '**/*.jsx'
6
+ - '**/*.astro'
7
+ - '**/*.html'
4
8
  - '**/*.css'
5
9
  - '**/*.scss'
6
10
  - 'canon/DESIGN.md'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "5.57.0",
4
+ "version": "5.58.0",
5
5
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -616,8 +616,16 @@ function shapeIssues(
616
616
  let inItem = false
617
617
  let inParagraph = false
618
618
  let afterBlank = false
619
+ let afterTable = false
620
+ let deferred = false
621
+
622
+ const settleDeferred = (): void => {
623
+ if (deferred && folder) folder.paragraphs++
624
+ deferred = false
625
+ }
619
626
 
620
627
  const closeFolder = (): void => {
628
+ settleDeferred()
621
629
  if (folder && !exempt.has(folder.text)) {
622
630
  if (folder.paragraphs > LAYOUT_PARAGRAPH_CAP) {
623
631
  issues.push({
@@ -661,6 +669,7 @@ function shapeIssues(
661
669
  inItem = false
662
670
  inParagraph = false
663
671
  afterBlank = false
672
+ afterTable = false
664
673
 
665
674
  if (level <= 2) {
666
675
  closeFolder()
@@ -697,11 +706,22 @@ function shapeIssues(
697
706
  inItem = true
698
707
  inParagraph = false
699
708
  afterBlank = false
709
+ afterTable = false
710
+ deferred = false
700
711
  if (gotchas) gotchas.bullets++
701
712
  if (folder) folder.bullets++
702
713
  continue
703
714
  }
704
715
 
716
+ if (folder && TABLE_ROW.test(line.text)) {
717
+ settleDeferred()
718
+ inItem = false
719
+ inParagraph = false
720
+ afterBlank = false
721
+ afterTable = true
722
+ continue
723
+ }
724
+
705
725
  const continues = inItem && (!afterBlank || INDENTED.test(line.text))
706
726
  afterBlank = false
707
727
  if (continues) continue
@@ -717,7 +737,12 @@ function shapeIssues(
717
737
  text: line.text.trim(),
718
738
  })
719
739
  }
720
- if (folder) folder.paragraphs++
740
+ if (folder) {
741
+ settleDeferred()
742
+ if (afterTable) deferred = true
743
+ else folder.paragraphs++
744
+ }
745
+ afterTable = false
721
746
  }
722
747
 
723
748
  closeFolder()