@erclx/canon 5.39.0 → 5.40.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/README.md +5 -5
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/canvas-board/SKILL.md +1 -1
- package/claude/skills/draft-slides/SKILL.md +2 -3
- package/claude/skills/draft-slides/references/deck.md +183 -0
- package/claude/skills/git-pr/references/evidence.md +2 -2
- package/claude/skills/git-pr/references/final-command.md +1 -1
- package/claude/skills/git-pr/references/labels.md +1 -1
- package/claude/skills/git-pr/references/publish.md +1 -1
- package/claude/skills/review-ui/SKILL.md +1 -0
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
- package/claude/skills/task-board/references/board.md +1 -1
- package/claude/skills/task-board/references/tasks.md +1 -1
- package/claude/skills/ux-measure/SKILL.md +1 -1
- package/docs/agents/audits.md +61 -60
- package/docs/agents/context-audit.md +40 -58
- package/docs/agents/index.md +5 -51
- package/docs/agents/install-and-sync.md +1 -0
- package/docs/agents/markdown-audit.md +53 -99
- package/docs/agents/records.md +37 -79
- package/docs/agents/sessions-transfer.md +26 -70
- package/docs/target/index.md +0 -1
- package/docs/target/migrations.md +57 -66
- package/docs/target/projects.md +36 -78
- package/docs/target/scenarios.md +9 -19
- package/docs/target/sync.md +27 -68
- package/docs/workflow/ai-workflow.md +22 -68
- package/docs/workflow/operating-model.md +32 -186
- package/docs/workflow/parallel-features.md +29 -66
- package/docs/workflow/skill-map-on-request.md +22 -24
- package/docs/workflow/skill-map.md +72 -76
- package/docs/workflow/visual-design-workflow.md +25 -118
- package/docs/workflow/zshrc-aliases.md +18 -37
- package/package.json +1 -1
- package/src/canvas/client/inspector/fields.css +4 -0
- package/src/canvas/index.html +6 -0
- package/src/claude/skills-reach.ts +2 -2
- package/src/cli/ui.ts +3 -1
- package/src/commands/canvas.ts +23 -0
- package/src/commands/capture.ts +14 -0
- package/src/commands/claude.ts +13 -0
- package/src/commands/demo.ts +22 -0
- package/src/commands/deps.ts +6 -0
- package/src/commands/driver.ts +28 -2
- package/src/commands/gov.ts +52 -18
- package/src/commands/intake.ts +17 -0
- package/src/commands/labels.ts +38 -5
- package/src/commands/pr.ts +160 -43
- package/src/commands/records.ts +8 -0
- package/src/commands/secrets.ts +6 -0
- package/src/commands/sessions.ts +8 -0
- package/src/commands/tasks.ts +52 -0
- package/src/commands/teach.ts +13 -0
- package/src/commands/upstream.ts +25 -1
- package/src/commands/worktrees.ts +5 -0
- package/src/context/audit.ts +22 -4
- package/src/context/folders.ts +1 -1
- package/src/context/shape-gated.ts +4 -2
- package/src/counts/scan.ts +1 -1
- package/src/design/components.ts +1 -1
- package/src/design/document.ts +1 -1
- package/src/design/tokens.ts +1 -1
- package/src/gate/boundaries.ts +4 -1
- package/src/gate/measures.ts +1 -1
- package/src/gate/stages.ts +7 -7
- package/src/git/env.ts +6 -0
- package/src/markdown/scope.ts +5 -17
- package/src/pr/lift.ts +153 -19
- package/src/pr/painted.ts +87 -0
- package/src/roots/record.ts +18 -1
- package/src/sandbox/census.ts +1 -1
- package/src/sandbox/headless/run.ts +3 -2
- package/src/sandbox/headless/watch.ts +3 -4
- package/src/slides/convert/master.ts +3 -0
- package/src/sync/format.ts +46 -0
- package/src/sync/stamp.ts +6 -0
- package/tooling/astro/reference.md +2 -2
- package/tooling/base/reference.md +1 -1
- package/tooling/go/reference.md +1 -1
- package/tooling/nextjs/reference.md +1 -1
- package/tooling/php/reference.md +1 -1
- package/tooling/python/reference.md +1 -1
- package/tooling/vite-react/reference.md +1 -1
- package/tooling/web/reference.md +1 -1
- package/docs/agents/canvas.md +0 -86
- package/docs/agents/capture.md +0 -65
- package/docs/agents/census.md +0 -23
- package/docs/agents/comments.md +0 -34
- package/docs/agents/context-audit-checks.md +0 -108
- package/docs/agents/context-classify.md +0 -101
- package/docs/agents/counts.md +0 -74
- package/docs/agents/demo.md +0 -140
- package/docs/agents/driver.md +0 -103
- package/docs/agents/gate.md +0 -90
- package/docs/agents/hooks.md +0 -78
- package/docs/agents/intake.md +0 -81
- package/docs/agents/key-changes.md +0 -116
- package/docs/agents/label-coverage.md +0 -86
- package/docs/agents/markdown-audit-density.md +0 -39
- package/docs/agents/markdown-audit-weight.md +0 -83
- package/docs/agents/pr-evidence-layout.md +0 -70
- package/docs/agents/pr-evidence.md +0 -273
- package/docs/agents/pr-frames.md +0 -131
- package/docs/agents/pr-lift.md +0 -132
- package/docs/agents/pr-local.md +0 -111
- package/docs/agents/pr-preview.md +0 -155
- package/docs/agents/pr-reads.md +0 -111
- package/docs/agents/pr-tick.md +0 -72
- package/docs/agents/records-push.md +0 -61
- package/docs/agents/records-stale.md +0 -40
- package/docs/agents/records-validate.md +0 -57
- package/docs/agents/restated.md +0 -83
- package/docs/agents/routing.md +0 -61
- package/docs/agents/rule-citations.md +0 -90
- package/docs/agents/sandbox.md +0 -126
- package/docs/agents/sessions.md +0 -128
- package/docs/agents/skills-audit.md +0 -120
- package/docs/agents/skills-reach.md +0 -64
- package/docs/agents/slides.md +0 -176
- package/docs/agents/state-scoped-risk.md +0 -105
- package/docs/agents/superseded.md +0 -85
- package/docs/agents/tasks-archive.md +0 -83
- package/docs/agents/tasks-plans.md +0 -152
- package/docs/agents/tasks-validate.md +0 -133
- package/docs/agents/tasks.md +0 -123
- package/docs/agents/teach-authoring.md +0 -118
- package/docs/agents/teach.md +0 -157
- package/docs/agents/test-order.md +0 -58
- package/docs/agents/upstream.md +0 -94
- package/docs/agents/worktrees.md +0 -118
- package/docs/target/standards-moved.md +0 -34
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Key changes bijection
|
|
3
|
-
description: Comparing the files a pull request body's Key changes names against its own diff, the two directions and the split inside each that decides what is worth raising, the span rules the extractor was measured into, and the three refusals that separate a clean pass from a read that produced nothing
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Key changes bijection
|
|
7
|
-
|
|
8
|
-
`canon pr key-changes` reads the `## Key changes` section of a pull request body, lifts the paths its bullets claim, and compares that set against the files the pull request actually changed. This repository squash-merges, so the body becomes the commit message and the record on the trunk once the branch is gone.
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
canon pr key-changes
|
|
12
|
-
canon pr key-changes 1265 --json
|
|
13
|
-
canon pr key-changes --body .canon/tmp/pr/body.md --base origin/main
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
The positional is the pull request to read, defaulting to the one open on this branch.
|
|
17
|
-
|
|
18
|
-
| Option | Behavior |
|
|
19
|
-
| --------------- | ------------------------------------------------------------------ |
|
|
20
|
-
| `--body <path>` | Read the body from a file, taking the changed set from git instead |
|
|
21
|
-
| `--base <ref>` | Ref the range runs back to when `--body` supplies the body |
|
|
22
|
-
| `--root <path>` | Repository to read, defaulting to the cwd |
|
|
23
|
-
| `--json` | Add a machine-readable record on stdout, keeping the frame |
|
|
24
|
-
|
|
25
|
-
`--body` decides where both halves come from, so a number passed beside it is never read. The body comes off disk and the changed set from the local range, which is the shape a fixture and a body still being drafted both need. A relative `--body` path resolves against the working directory the command runs from, never against `--root`. `--base` and the changed-file read still resolve against `--root`.
|
|
26
|
-
|
|
27
|
-
`--base` names the far side of that range and never the commit the comparison runs against directly. The reader resolves the merge base between `HEAD` and the ref, so `--base origin/main` measures what the branch wrote rather than what the trunk gained while the branch was open. Passing the trunk as it stands today is therefore safe on a branch of any age, and a ref sharing no history with `HEAD` refuses as `bad-base`.
|
|
28
|
-
|
|
29
|
-
Without `--body` the body and the head commit come off the REST pull read and the file list off the paginated files endpoint. Every read runs on REST, since a cloud session's GitHub proxy refuses the GraphQL that `gh pr view` runs on. A push landing between the two reads compares a body against the next head's files, a window the verb accepts for the cloud reach.
|
|
30
|
-
|
|
31
|
-
A failed page refuses as `gh-failed` rather than comparing against a set known to be short. A set silently one file short is the worst input this takes, since the missing file is exactly what a correct bullet would then be accused of inventing. With no number named, the verb reads the one pull request open on the checkout's branch, and refuses as `ambiguous-pull` when two are open against different bases.
|
|
32
|
-
|
|
33
|
-
## The two directions
|
|
34
|
-
|
|
35
|
-
They are reported apart because they want different tolerances.
|
|
36
|
-
|
|
37
|
-
- **`unmet`** is a whole path the body claims ahead of its bullet's first comma and the diff does not carry. This is the graded direction and it sets the exit code. A bullet naming an untouched file is wrong more often than not, and it corrupts the record that reaches the trunk.
|
|
38
|
-
- **`unnamed`** is a changed file no bullet reached that a reader might have wanted one for. Reported with no grade, since a change can be too small to describe and still be correctly absent. Grading it would fire on nearly every branch.
|
|
39
|
-
- **`incidental`** is a changed file no bullet reached that owes none: a test beside its subject, anything under a fixture or snapshot folder, and a lockfile a package manager writes. Held apart so the count above reads, and reported rather than dropped so a run still says what it set aside.
|
|
40
|
-
- **`unresolved`** is a path the reading could not judge either way. Three causes reach it. A path written partially, such as `role-worker/SKILL.md` for a file under `claude/skills/`. A path past its bullet's first comma. And, for a live pull request, a claim landing here rather than `unmet` because the rename or `.gitignore`-addition evidence that might have credited it could not be read, which `evidenceUnread` on the record names.
|
|
41
|
-
|
|
42
|
-
Each direction splits on one question, which is whether the evidence is strong enough to raise with a person. A partial path and a trailing path can each credit a changed file and can never accuse one, because nothing separates a path written short from a path written wrong, or a second claim from a file cited for context. Neither split drops anything: what comes out of `unmet` lands in `unresolved` and what comes out of `unnamed` lands in `incidental`, so a count a reader can act on never costs a file the run stayed silent about.
|
|
43
|
-
|
|
44
|
-
A generated asset and a regenerated index belong in the incidental class and are deliberately absent from it, because neither has a spelling that holds outside one project. Guessing at one would set aside a file that did owe a bullet, which hides a real omission, where leaving them out only leaves the count where it already was.
|
|
45
|
-
|
|
46
|
-
## What counts as a claim
|
|
47
|
-
|
|
48
|
-
Only `## Key changes` is read. `## Technical context` legitimately names files a branch never touched, such as an install stamp inside a target, so widening the read manufactures a finding out of every argument an author made for the change.
|
|
49
|
-
|
|
50
|
-
Inside the section, every backticked span in a bullet is read, and the bullet's first comma outside a span divides the ones that can accuse from the ones that can only credit. That one lever was chosen by measurement. Over the 23 merged pull requests in this repository carrying the section, reading whole bullets reported 16 paths as claimed-but-untouched and every one was a file the body named for context. Cutting at the comma left 110 claims of the original 149 and took the false reports to 2. A list of sixteen clause-opening words tried beside it removed nothing the comma had not already removed, because this corpus punctuates every one of them.
|
|
51
|
-
|
|
52
|
-
The cut used to decide whether a span was read at all, and the paths past the comma fell out of the claim set into `unnamed`. That was accepted on the ground that the ungraded direction does no damage, and it did: `review-pr` read `unnamed` as a question to put to the branch author, so on 2026-09-01 the question went to three pull requests over bullets that had named the files all along. Reading the whole bullet as claims outright is the obvious repair and the corpus refuses it, taking `unmet` from 10 to 19 over the 40 most recent merged pull requests carrying the section, with all nine additions in the context class the cut exists to exclude. Reading the whole bullet and gating the accusation on the cut gives claims 243 to 314 and unnamed 1124 to 1058 with `unmet` identical entry for entry.
|
|
53
|
-
|
|
54
|
-
A span anywhere in the bullet has to survive all of these:
|
|
55
|
-
|
|
56
|
-
| Rule | What it keeps out |
|
|
57
|
-
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
58
|
-
| No whitespace, `<`, `>`, `$`, `*`, `\|`, `?`, `^` | A backticked command, a placeholder, a glob, a pattern |
|
|
59
|
-
| No `://` and no leading `/`, `~`, `@`, `#`, `!` | A URL, an absolute path, a module alias |
|
|
60
|
-
| Contains `/` | A bare filename with no folder around it |
|
|
61
|
-
| Not a dotted-decimal segment, or ends with `/` | A dotted number such as an address |
|
|
62
|
-
| Not a single top-level folder | `src/`, which nobody claims to have rewritten whole |
|
|
63
|
-
| A `file:line` span leads its bullet, or the bullet carries no bare line-only companion | A citation into a file the bullet already named, such as `verify.sh:634` beside `:642` |
|
|
64
|
-
| The region carries no no-change marker | A bullet recording a file it deliberately left alone |
|
|
65
|
-
|
|
66
|
-
The last three rows earn their place from the corpus rather than from a rule. Every body that spelled a lone top-level folder was naming where something lives, and a line citation that follows another claim in the same bullet points into the file being described only when a bare line-only span sits beside it, such as `:642` following `verify.sh:634`. With no such companion the citation names a second, genuinely distinct file and stays a claim. A line citation leading its bullet stays a claim either way, which is how a body names the exact line it rewrote.
|
|
67
|
-
|
|
68
|
-
The no-change marker is the one rule the region cut cannot substitute for. A body writes "Leave `x` untouched, since the decision keeps it" to record a change it declined, and the path sits ahead of the first comma, so a stricter cut would not reach it and a looser one would find more. Since `keep` and `leave` each open a real claim often enough, the marker decides it rather than the leading verb. Three words carry the set: `untouched`, `unchanged`, and `as written`. `in place` was measured and dropped, because rewriting a file in place is an ordinary claim, and `no other line` was dropped because a correct bullet writes "as one insertion that touches no other line" about a change it did make. `alone` shipped in the set too, until review found every corpus occurrence sitting past the first comma, where the cut already excludes it, so the word voided no true claim there. Restrictive use is the more common one in this repository's own prose, and a comma-free bullet exposed it: "Move the threshold read into `src/gate/stages.ts` alone" asserts an edit, and the marker voided it while the word was still in the set. Over the 40-pull-request corpus the rule still voids no true claim.
|
|
69
|
-
|
|
70
|
-
A folder claim covers every changed file beneath it. A bare filename drops outright: resolved as a sibling of a path earlier in its bullet it produced seven wrong paths across this corpus against two right ones, because a compound bullet names a sibling folder as often as a sibling file.
|
|
71
|
-
|
|
72
|
-
## Anchoring
|
|
73
|
-
|
|
74
|
-
A claim is anchored when its first segment names a folder the tree holds. The roots come from the tracked file list joined with the first segment of every changed path, and the second half is what admits a folder the branch created. Reading the tree alone would mark every claim under a new top-level directory unanchored, and an unanchored claim never accuses, so the first branch to open one would lose the graded direction with nothing reporting it.
|
|
75
|
-
|
|
76
|
-
## Exit codes and refusals
|
|
77
|
-
|
|
78
|
-
| Code | Meaning |
|
|
79
|
-
| ---- | ------------------------------------------------- |
|
|
80
|
-
| `0` | every claimed path is in the diff |
|
|
81
|
-
| `1` | refused, with `reason` naming the cause |
|
|
82
|
-
| `2` | at least one claimed path is absent from the diff |
|
|
83
|
-
|
|
84
|
-
Branch on the record rather than on the exit code. A shell function wrapping `canon` takes its status from whatever it runs last, so every non-zero exit can reach a caller as zero.
|
|
85
|
-
|
|
86
|
-
Three refusals separate a clean pass from a read that produced nothing:
|
|
87
|
-
|
|
88
|
-
- `no-section`. The body carries no such heading, so it claims nothing.
|
|
89
|
-
- `no-claims`. The heading is there and no span resolved, which is the extractor failing over prose rather than the body being wrong. Nothing is raised.
|
|
90
|
-
- `no-changes`. The pull request changed no files, so no claim has anything to answer.
|
|
91
|
-
|
|
92
|
-
An empty extraction read as a clean pass is the failure shape this repository has recorded twice, which is why the middle one is its own reason rather than a zero count.
|
|
93
|
-
|
|
94
|
-
`gh-missing`, `gh-failed`, `ambiguous-pull`, `unreadable-body`, `unreadable-tree`, `no-base`, `bad-base`, and `unreadable-changes` cover the reads that never reached a comparison.
|
|
95
|
-
|
|
96
|
-
## What it was measured against
|
|
97
|
-
|
|
98
|
-
Driven over the 40 most recent merged pull requests in this repository at the time, 23 of which carry the section, the shipped verb reported zero unmet paths and one `no-claims` refusal. The naive extractor it replaced reported 16.
|
|
99
|
-
|
|
100
|
-
The re-measurement behind the trailing-path split is a wider corpus and its numbers do not compare to those. It takes the 40 most recent merged pull requests that carry the section, which reaches back through 90 merges rather than 40, and the reader reports 10 unmet over it. Those 10 predate the split and survive it entry for entry, which is the check that the widening moved nothing into the graded direction. Three name a gitignored path a diff can never carry and one is the definition-site class named below. The remaining six were not run down, so the residual over that corpus is 10 rather than zero and how much of it is the reader rather than the bodies is unmeasured.
|
|
101
|
-
|
|
102
|
-
The first body written after that corpus closed produced two, which is what the no-change marker and the open class above come from. One was the disclaiming bullet and the rule now covers it at no cost to the corpus. The other is the definition-site class, left open with the report naming it.
|
|
103
|
-
|
|
104
|
-
The bound is that the corpus is one repository writing to one house style. A project that punctuates differently loses claims to the comma cut rather than gaining false ones, since every rule above drops rather than invents, so the check degrades toward silence rather than toward noise.
|
|
105
|
-
|
|
106
|
-
What it cannot see is a bullet claiming a change to a file the branch did touch for another reason, and a path written without backticks. A claim in a second coordinated clause after the comma is read and credited, and it stays out of the graded direction, since nothing here separates it from a file the same clause cites for context.
|
|
107
|
-
|
|
108
|
-
Two shapes credit against evidence the changed-file list itself never carries. A bullet naming the path a git rename moved away from, such as "Move `claude/skills/identity/` to `claude/skills/draft-identity/`", is credited against a local rename read (`git diff --name-status -M`) or, for a live pull request, `gh api repos/{owner}/{repo}/pulls/{number}/files`, keyed on the old path git's own detection reports. A bullet naming a pattern newly added to `.gitignore`, such as "Ignore `web/screenshots/` and `web/evidence/`", is credited against the added, non-comment lines of that file's own diff, with the trailing slash a directory claim carries normalized against the raw pattern. Both credits reach past the anchored-and-leading split that decides `unmet`, and the ignore-addition credit marks `.gitignore` itself as named, so the file also stops reading as `unnamed`.
|
|
109
|
-
|
|
110
|
-
Neither credit can tell a claim that is genuinely stale from one the evidence would have credited had the read gone through, since a failed fetch and a confirmed absence both produce the same empty result. `evidenceUnread` on the record separates the two: when the read that could have credited a claim fails, that claim lands in `unresolved` instead of `unmet`, rather than risk the exact false accusation this section exists to remove.
|
|
111
|
-
|
|
112
|
-
One class stays open and is named rather than closed. A bullet can cite where something is defined while claiming an edit somewhere else, as `erclx/canon#1274` does with "Name the slug transform in `standards/slug.md` inline in the same step". The path is a definition site, the edit target is the step, and separating the two needs the sentence parsed rather than cut. A finding on such a bullet is this class rather than a stale claim, and a reader weighing an `unmet` path checks whether the bullet's real target is a locative the path does not name. <!-- canon-allow-reference: illustrates the input shape the rule reads -->
|
|
113
|
-
|
|
114
|
-
## Where it runs
|
|
115
|
-
|
|
116
|
-
`review-pr` Step 3 calls it through its `references/key-changes.md` and files an `unmet` path as a `should-fix` finding under the `**PR body**` block the stale ticked box already takes, since what both corrupt is the merge record rather than a file in the diff. A body is edited between pushes, so a finding names the head the comparison ran at. `unnamed` is read there and raised off no count, since the step's own history is a question sent over bullets that already answered it. `unresolved` and `incidental` are reported nowhere.
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Label coverage
|
|
3
|
-
description: Reading a changed set against the pull request label map, the two tables it matches, how a gap is separated from a decision, and why an absent map is an answer
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Label coverage
|
|
7
|
-
|
|
8
|
-
`canon labels audit` resolves the paths a branch changed against the label map a project declares, reports the labels the set earns, and names every path no row reaches. It closes a gap the map's own comment had predicted since the map shipped: nothing detected a surface added after the rows were written, so such a branch merged carrying no label and nobody heard about it.
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
canon labels audit
|
|
12
|
-
canon labels audit --json
|
|
13
|
-
canon labels audit --base origin/main
|
|
14
|
-
canon labels audit src/cli.ts docs/index.md --json
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
| Option | Behavior |
|
|
18
|
-
| --------------- | ---------------------------------------------------------- |
|
|
19
|
-
| `--base <ref>` | Ref the range runs back to, defaulting to the trunk |
|
|
20
|
-
| `--root <path>` | Repository to read, defaulting to the current directory |
|
|
21
|
-
| `--json` | Add a machine-readable record on stdout, keeping the frame |
|
|
22
|
-
|
|
23
|
-
`--base` names the far side of the range rather than the commit the diff runs against. The audit resolves the merge base between `HEAD` and the ref before reading anything, so `--base origin/main` on a branch the trunk has moved past still earns labels for the paths that branch wrote. Reading the ref literally is what once handed a long-open branch the labels for somebody else's merge, and it named those paths as uncovered surfaces on top.
|
|
24
|
-
|
|
25
|
-
Positional paths replace the range entirely. A caller holding a changed set already passes it rather than paying for a second git read, and the record then omits `base` to say the range was never resolved.
|
|
26
|
-
|
|
27
|
-
Under `--json` the record holds stdout alone and the frame still renders on stderr, refusals included, which is the split `output-shape.md` fixes for every mode.
|
|
28
|
-
|
|
29
|
-
## The map it reads
|
|
30
|
-
|
|
31
|
-
The map sits at `canon/config/pr-labels.toml`, falling back to `.claude/canon/pr-labels.toml` for a project that has not moved, and carries two tables. `[domains]` keys a label name to the path prefixes that earn it, and `[declined]` keys a reason to the prefixes that earn no label on purpose.
|
|
32
|
-
|
|
33
|
-
```toml
|
|
34
|
-
[domains]
|
|
35
|
-
api = ["services/api/"]
|
|
36
|
-
web = ["apps/web/", "packages/ui/"]
|
|
37
|
-
|
|
38
|
-
[declined]
|
|
39
|
-
release-managed = ["CHANGELOG.md", "package.json"]
|
|
40
|
-
generated = ["build/manifest.json"]
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Matching is prefix-anchored, so a row written for an authoring root reaches nothing under the copy a project consumes. A path claimed by rows in both tables takes the label, since it already has a subject and reporting it as deliberately unlabelled would contradict the label the same run applies.
|
|
44
|
-
|
|
45
|
-
## A gap and a decision are different findings
|
|
46
|
-
|
|
47
|
-
The verb sorts every changed path into three buckets and keeps two of them apart:
|
|
48
|
-
|
|
49
|
-
- **Labelled.** At least one `[domains]` row reaches the path. It contributes its label and nothing else.
|
|
50
|
-
- **Declined.** A `[declined]` row reaches it. Somebody already decided the path earns no label, so the run names it and moves no exit code.
|
|
51
|
-
- **Uncovered.** Neither table reaches it. This is the finding, and it wants either a prefix on the row that owns its subject or a `[declined]` row carrying the reason.
|
|
52
|
-
|
|
53
|
-
Folding the last two together was the shape this replaced. A report that cannot tell a surface nobody has gotten to from one somebody refused is useful about neither, which is why the declined half moved out of the map's trailing comment and became data the command reads.
|
|
54
|
-
|
|
55
|
-
## The range it reads
|
|
56
|
-
|
|
57
|
-
The default range is the branch against the trunk, resolved as the merge base against `origin/main` and then local `main`. The changed set is that base diffed against the working tree, plus untracked files git does not ignore, rather than against `HEAD`.
|
|
58
|
-
|
|
59
|
-
Reading the working tree is what lets the check run before the branch commits, which is the moment a session adds the surface nobody covered. The set is a superset of `base..HEAD`, so a caller running after the commits still sees the whole branch.
|
|
60
|
-
|
|
61
|
-
## Exit codes and refusals
|
|
62
|
-
|
|
63
|
-
| Code | Meaning |
|
|
64
|
-
| ---- | ---------------------------------------------- |
|
|
65
|
-
| `0` | every changed path is labelled or declined |
|
|
66
|
-
| `1` | refused, with `reason` naming the cause |
|
|
67
|
-
| `2` | at least one changed path is reached by no row |
|
|
68
|
-
|
|
69
|
-
It reports and never gates. Whether an uncovered surface deserves a label is a judgment only whoever owns that surface can make, and a push failing on one teaches a contributor to route around the check while nothing about the surface has changed.
|
|
70
|
-
|
|
71
|
-
A project declaring no map refuses with `no-map`, which is an answer rather than a fault. Such a project is labelled silently by design, and treating the absence as a break would make the map mandatory for every target. `canon audits run` reads that one reason as an expected absence and every other refusal as a measure that did not run.
|
|
72
|
-
|
|
73
|
-
## Applying the set
|
|
74
|
-
|
|
75
|
-
`canon labels apply <number>` reads the same earned set and adds each label the open pull request lacks, so a pull request opened outside `git-pr` takes one command to label.
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
canon labels apply 123 --json
|
|
79
|
-
canon labels apply 123 --check
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
It binds to the pull request before anything is read or written, and refuses with `head-mismatch` unless the head is the current branch and the state is open, under `--check` as well. It adds and never removes, so a label a person applied stays. A label the remote does not carry lands in `missingOnRemote` and is never created. `--check` writes nothing and exits 2 when labels are owed. A project declaring no map adds nothing and exits 0.
|
|
83
|
-
|
|
84
|
-
## What it does not measure
|
|
85
|
-
|
|
86
|
-
A prefix reaching no path is invisible here. A row left behind by a deleted folder stays in the map forever, and this verb has nothing to say about it. That is the map going stale from the other side and a second measure rather than this one.
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Markdown audit density
|
|
3
|
-
description: The six layout limits the markdown audit measures, what each one counts, which paths take a finding, and how a folder's gate turns on
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Markdown audit density
|
|
7
|
-
|
|
8
|
-
`canon markdown audit` measures six layout limits beside its bans. Each one is a count against a fixed number, so a finding is a fact rather than a judgment, and a gated path fails the run on it.
|
|
9
|
-
|
|
10
|
-
## The limits
|
|
11
|
-
|
|
12
|
-
Six limits report on the paths that take them, under `density` in each `--json` entry.
|
|
13
|
-
|
|
14
|
-
| Limit | Finding |
|
|
15
|
-
| ------------------- | ---------------------------------------------------------- |
|
|
16
|
-
| Paragraph length | a prose paragraph past 350 characters |
|
|
17
|
-
| Paragraphs in a row | more than 2 paragraphs with nothing else between them |
|
|
18
|
-
| Bullet length | a top-level bullet past 200 characters, marker excluded |
|
|
19
|
-
| Bullets in a row | more than 5 plain top-level bullets |
|
|
20
|
-
| Heading depth | any heading past H3 |
|
|
21
|
-
| H3 opening | an H3 whose first block is neither a paragraph nor a table |
|
|
22
|
-
|
|
23
|
-
## What counts as a block
|
|
24
|
-
|
|
25
|
-
A paragraph indented under a list item counts toward that bullet's length and never as a paragraph. A table, a fence, a list, a heading, and a bold section marker each break a paragraph run. Each finding carries `kind`, `line`, `value`, `limit`, and `gating`, and the H3 opening carries `null` for both numbers.
|
|
26
|
-
|
|
27
|
-
## Which paths take a finding
|
|
28
|
-
|
|
29
|
-
Only context entries, docs pages, skill bodies and references, tooling seeds and stack references, the wiki, `README.md`, and `CONTRIBUTING.md` take a finding. A rule, `CLAUDE.md`, a requirement or design record, the changelog, `examples/`, and every `.canon/` record take none, so a plan never reports one.
|
|
30
|
-
|
|
31
|
-
## When a finding gates
|
|
32
|
-
|
|
33
|
-
A finding gates when its path sits under a prefix in `GATED_PATHS` in `src/markdown/scope.ts`, and each rewrite batch adds its folders at zero, or one file per entry where it covers part of a folder. A run of more than 5 plain bullets gates in context, docs, and skills from the start, since it failed the push there before this measure existed.
|
|
34
|
-
|
|
35
|
-
Outside a canon checkout every density finding reports and none gates, since the list describes this repository's own rewrite.
|
|
36
|
-
|
|
37
|
-
## What reads the finding
|
|
38
|
-
|
|
39
|
-
A gating finding sets exit `2`, the same code a ban hit takes, and `markdown-audit.md` carries the full exit contract. The `Markdown` stage of `canon gate run` reads that exit over the whole corpus, and the edit-time hook hands every finding back, gating or not.
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Markdown audit weight
|
|
3
|
-
description: The advisory sections of the markdown audit, being bullet and paragraph weight, run depth, the two cadence numbers, and the length ceiling, with the samples behind each checkpoint and which of those numbers gate through density
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Markdown audit weight
|
|
7
|
-
|
|
8
|
-
## Bullets, paragraphs, and depth
|
|
9
|
-
|
|
10
|
-
Bullet weight and depth are the checks that moved off `canon context audit`, carrying what they measured at the time. A top-level bullet reports past roughly 200 characters with continuation lines folded in and nested items left out. A run reports past roughly 40 rendered lines, measured at 80 columns, where a heading breaks one and so does a bold section marker taking the whole line at column zero, either ending in a colon, or holding one whole code span at any width, or running to 20 characters or fewer, skipping fenced blocks and exempting a flat peer list averaging under 130 characters a bullet and a run that is entirely table rows. Each file reports its longest run alone, so a second run past the checkpoint in the same file is never named.
|
|
11
|
-
|
|
12
|
-
Every weight and depth measure counts the text a reader is shown. A link reduces to its anchor text and an autolink drops whole, since no reader is shown either destination. A backticked path stays counted, which is where these measures part from the ban scan `markdown-audit.md` describes: that one blanks a code span so a standard quoting its own banned character does not report itself, and discounting the same span here would under-report a paragraph carrying several. One file holds both span sets and each answers its own question.
|
|
13
|
-
|
|
14
|
-
A code span is walked around rather than through, so a path quoting link or angle-bracket syntax keeps the width the page gives it. Masking inside one takes back the decision to count it, and the placeholders this toolkit writes are where that shows.
|
|
15
|
-
|
|
16
|
-
The paragraph check measures both halves of one rule. `markdown.md` caps a paragraph at four sentences, and a sentence cap on its own is satisfied by writing fewer and longer ones: 15 paragraphs in this corpus sit inside four sentences and past the weight checkpoint, and the heaviest of those runs 886 characters. The standard therefore states a weight beside the sentence cap, and the verb reads it as its own checkpoint.
|
|
17
|
-
|
|
18
|
-
A sentence boundary closes on terminal punctuation ahead of a capital or a code span. The capital is what keeps a version pin and a decimal from each reading as two sentences, and the code span is admitted beside it because a command name opening a sentence carries no capital to find.
|
|
19
|
-
|
|
20
|
-
The paragraph weight sits at 350 and the bullet weight at 200, the two density lengths in `markdown-audit.md`, so this advisory warning and the density finding that gates read one number. Both shipped at 400 and have moved twice, each time against a sample of its own. They are separate checkpoints in the standard and separate patterns in the parser, so a read that moves one leaves the other where it is.
|
|
21
|
-
|
|
22
|
-
### The sample behind the paragraph number
|
|
23
|
-
|
|
24
|
-
The checkpoint shipped at 400 as a borrowed number and was decided against a read of the prose it reports. Thirty-six findings were sampled, six from each of six weight bands, drawn at even spacing through each band ordered by path and line, and each was classed as prose a reader wants split or prose the checkpoint should not have reported.
|
|
25
|
-
|
|
26
|
-
Every band below was measured before the scan stopped counting link syntax as prose, so a paragraph sitting in one of these bands is heavier than a paragraph reported at the same number today. The re-sample in the section below re-reads the same range against the corrected measure and reaches the opposite verdict on it, which is the measure moving rather than the reader.
|
|
27
|
-
|
|
28
|
-
| Band | Wants the split | Reads as written |
|
|
29
|
-
| --------- | --------------- | ---------------- |
|
|
30
|
-
| 400 - 425 | 1 | 5 |
|
|
31
|
-
| 425 - 450 | 2 | 4 |
|
|
32
|
-
| 450 - 500 | 2 | 4 |
|
|
33
|
-
| 500 - 600 | 2 | 4 |
|
|
34
|
-
| 600 - 750 | 6 | 0 |
|
|
35
|
-
| Past 750 | 6 | 0 |
|
|
36
|
-
|
|
37
|
-
Precision is what moved the number rather than the finding count. Below 600 the checkpoint was right about seven of twenty-four sampled paragraphs, and past 600 it was right about all twelve. A checkpoint is a prompt to look, and a prompt wrong three times in four teaches a reader to stop looking. The distribution offers no seam to place the number against, with a median of 487 and a seventy-fifth percentile of 563, so the read is the whole of the evidence.
|
|
38
|
-
|
|
39
|
-
Nothing inside the 500 to 600 band separated the two classes by length, which is the reason the number did not land there. The two paragraphs wanting a split ran 543 and 590 characters against four reading well at 515, 532, 555, and 569.
|
|
40
|
-
|
|
41
|
-
The sample is thirty-six paragraphs against a reported population in the hundreds, and one reader classed all of them. Treat a band's rate as the order of magnitude it is rather than as a measured precision, and re-sample before moving the number again.
|
|
42
|
-
|
|
43
|
-
### The re-sample that moved the number to 700
|
|
44
|
-
|
|
45
|
-
That re-sample ran once the scan stopped counting link syntax as prose. Findings at 604, 633, and 677 characters each read as an ordinary four-sentence paragraph on one topic, density arrived around 760 and was plain by 860, and the move cut the weight half of the report roughly in half while leaving the sentence cap untouched.
|
|
46
|
-
|
|
47
|
-
### The read that moved it to 350
|
|
48
|
-
|
|
49
|
-
The operator read sample rewrites at 400 and 350 side by side and picked 350, the length of the reference sample, in groundwork `154`. The bullet number moved to 200 in the same read, since no rewritten one-fact bullet passed 151. Both now gate as density limits, so the earlier verdict that a long paragraph reads well as written no longer holds.
|
|
50
|
-
|
|
51
|
-
A bullet, a heading, a table row, a blockquote, a blank line, and a fence each end a paragraph, so a heavy bullet is reported by the bullet check alone and never counted twice.
|
|
52
|
-
|
|
53
|
-
## Bullet stacks
|
|
54
|
-
|
|
55
|
-
A run of more than 5 plain bullets is one of the six density limits in `markdown-audit.md`, which reports and gates it. This section keeps what counts as a run. A run of consecutive top-level bullets reports past 5. A blank line, an indented continuation, and a nested item do not break the run, and a paragraph, a heading, or any other column-zero line does. A fenced line is skipped without breaking it. The count is of items rather than weight, so it catches a catalog of short bullets the 200-character checkpoint above passes.
|
|
56
|
-
|
|
57
|
-
Two shapes are exempt. A run whose every item opens with a bold label or a link names its own subject per item and reads as a table of entries, and a numbered list states an order, so numbered items never count. Governance rules and `REQUIREMENT.md` sit outside the density scope rather than being exempted here, since a rule is one imperative per bullet.
|
|
58
|
-
|
|
59
|
-
## Cadence
|
|
60
|
-
|
|
61
|
-
Uniform cadence is the failure a ban list cannot express. A ban set states negatives, and fragments, verbless clauses, and sentences that all run one length are each the absence of something, so no word added to a ban list reaches any of them. The shape layer already measured a bullet, a paragraph, and a run, and stopped one level above where that failure lives.
|
|
62
|
-
|
|
63
|
-
Cadence measures a paragraph on two numbers. The spread is the words between its longest and shortest sentence, and the opener count is the times one word opens a sentence in it. A spread of five words or under reads as one cadence, and a word opening more than two sentences is a pattern rather than a coincidence. Both come from `## Rhythm` in the `write-human` skill, which states them about prose a person reads, and this measures against that statement rather than setting a threshold of its own.
|
|
64
|
-
|
|
65
|
-
Words are counted off the text a reader is shown. A link contributes its anchor text, an autolink contributes nothing, and the sentence boundaries do not move under that masking, since the boundary pattern requires whitespace after the terminal punctuation and no destination carries any. An opening word is lowercased and stripped of punctuation, so a sentence opening on a backticked command name reports the command.
|
|
66
|
-
|
|
67
|
-
A paragraph carrying fewer than three sentences is skipped rather than scored. A two-sentence configuration note has no spread worth reading, and the opener rule is written about a third sentence turning a coincidence into a pattern, so neither number says anything before the floor. That is the cheap form of a wider exemption: a shape-aware one, exempting a short reference block by what it is rather than by how many sentences it holds, waits on a second case.
|
|
68
|
-
|
|
69
|
-
The unit is the paragraph and each file names its worst on each measure, which follows the depth check rather than setting a precedent. A file's flattest paragraph and its most repetitive one are named only when each crosses its checkpoint, so a file reading healthy names nothing rather than offering its least healthy paragraph as a finding.
|
|
70
|
-
|
|
71
|
-
Neither number gates and neither names a file wrong. This is a weaker claim than the one the weight checkpoints make, because a healthy range differs by surface: a catalog entry is several short sentences carrying one fact each, and a page arguing a decision is not, so one range applied across the corpus would report the surfaces that are correct. The run therefore states where the numbers came from beside them, and the counts are what a reader compares against.
|
|
72
|
-
|
|
73
|
-
A reading of that spread across the whole corpus is what backs the range above, taken once against this repository's own tracked markdown rather than against a target project's.
|
|
74
|
-
|
|
75
|
-
That reading travels with the command rather than staying here. `BASELINE` in `src/markdown/structure.ts` carries the overall share, the per-file range, and the ten-paragraph floor beneath which a file's own rate says nothing, and the run prints all four in the legend beside the rate it measured. A count with no range beside it reads as a finding, and naming that a healthy range differs by surface states that a range exists rather than what it looks like. This page is toolkit-internal, so a reader running the command in a project that installed no standards would otherwise have two counts and nothing to place them against.
|
|
76
|
-
|
|
77
|
-
Two of the rules `write-human` states are deliberately not implemented. A sentence's grammatical shape and whether it carries a finite verb each need a parse rather than a match, and an imperative would read as a defect under a pattern that approximated either. The verbless share is the measure closest to the reported symptom, which is exactly why shipping it wrong would discredit the two that hold.
|
|
78
|
-
|
|
79
|
-
The condition on that was something identifying a finite verb rather than guessing at one, and two parsers have now been run against it over 11,389 paragraph sentences. They disagree by a factor of four. `compromise` reports 2 percent and reads a fronted past participle as a finite verb, so `Measured at <sha> on <date>.` counts as carrying one. `wink-pos-tagger` reports 9 percent, fixes that class, and is still wrong on roughly three in four, because an imperative's verb tags as a proper noun and a noun-ambiguous predicate tags as a noun, which makes `Each maps to a skill.` read verbless. Separating those needs to know which token is the predicate, and that is syntax rather than a tag. The measure stays unimplemented, now against a mechanism rather than against the idea of one.
|
|
80
|
-
|
|
81
|
-
## Length
|
|
82
|
-
|
|
83
|
-
The Length step sums rendered lines over the whole source, frontmatter and fenced blocks included, and lists each document past the 300-line ceiling, longest first. A changelog and a document carrying a whole-line `<!-- canon-length-exempt: <reason> -->` outside a fence are counted in one line rather than listed. Each `--json` entry carries `renderedLines` and `exempt`, the stated reason or `null`, and `checkpoints` carries `ceiling`.
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: The pull request evidence layout
|
|
3
|
-
description: How canon pr evidence groups screenshots into one block per view with a row per width and a column per theme, when a base column appears, where a name outside the form lands, how the commit line links, and when the blocks open
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# The pull request evidence layout
|
|
7
|
-
|
|
8
|
-
`canon pr evidence` renders the screenshots of a pull request as blocks a
|
|
9
|
-
reviewer reads by view. The record behind them, and the reasons a run can
|
|
10
|
-
refuse, are in `canon docs pr-evidence`.
|
|
11
|
-
|
|
12
|
-
## One block per view
|
|
13
|
-
|
|
14
|
-
A case name that ends in `-dark-<width>` or `-light-<width>` belongs to the
|
|
15
|
-
view named by everything before that suffix, so `menu-open-light-1440` is the
|
|
16
|
-
`menu-open` view. The name is split from the right because a view may carry
|
|
17
|
-
hyphens of its own.
|
|
18
|
-
|
|
19
|
-
Each view is one `<details>` block. Its summary carries the view and its case
|
|
20
|
-
count, a row holds one width in ascending number order, and a column holds one
|
|
21
|
-
theme:
|
|
22
|
-
|
|
23
|
-
```markdown
|
|
24
|
-
| Width | Dark | Light |
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
A view with any case already at the base takes a base and a head column per
|
|
28
|
-
theme, and a case the pull request added shows `*(new)*` in its base cell:
|
|
29
|
-
|
|
30
|
-
```markdown
|
|
31
|
-
| Width | Dark base | Dark head | Light base | Light head |
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
A cell with no case in it stays empty. A theme no case of the view carries has
|
|
35
|
-
no column. A state under a named directory titles its blocks
|
|
36
|
-
`<state> / <view>`, so two states holding one view never repeat a name.
|
|
37
|
-
|
|
38
|
-
## Names outside the form
|
|
39
|
-
|
|
40
|
-
A case name that does not split into a view, a theme, and a width lands in a
|
|
41
|
-
flat `Case | Base | Head` block for its state, or `Case | Head` when every case
|
|
42
|
-
in it is new. Nothing is dropped, so `board.png` or a name with no width still
|
|
43
|
-
shows.
|
|
44
|
-
|
|
45
|
-
## The commit line
|
|
46
|
-
|
|
47
|
-
Under `## Evidence`, the Base and Head line names each commit as a short sha
|
|
48
|
-
that links to the full commit:
|
|
49
|
-
|
|
50
|
-
```markdown
|
|
51
|
-
**Base:** [`<base>`](https://github.com/<repo>/commit/<base-sha>) · **Head:** [`<head>`](https://github.com/<repo>/commit/<head-sha>)
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
A reader opens what the screenshots were taken against in one click. The base
|
|
55
|
-
is the merge base the compare endpoint returns, not trunk's tip.
|
|
56
|
-
|
|
57
|
-
## When the blocks open
|
|
58
|
-
|
|
59
|
-
Every block opens while the comment carries six images or fewer, counting one
|
|
60
|
-
per case for the head plus one per case that is not new. Past that every block
|
|
61
|
-
closes, so the whole comment is open or closed together.
|
|
62
|
-
|
|
63
|
-
## What reads the body back
|
|
64
|
-
|
|
65
|
-
The case count that `would-empty` compares is the number of head images in the
|
|
66
|
-
body. It holds for the grouped rows, where one row carries up to two cases, and
|
|
67
|
-
for every layout posted before, so a comment rendered under an older shape
|
|
68
|
-
still counts. The bold `**Base:**` label and the `<details>` blocks are
|
|
69
|
-
unchanged, which keeps the address reader and the review skills working on both
|
|
70
|
-
layouts. An older comment keeps its shape until the next run edits it in place.
|