@erclx/canon 4.36.1 → 4.37.1

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.36.1",
4
+ "version": "4.37.1",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -154,11 +154,15 @@ A hit the closed set cannot separate from correct prose is the case with no thir
154
154
 
155
155
  ### Where the rules are enforced
156
156
 
157
- Four surfaces apply the ban sets and three of them go through this verb. `.claude/hooks/standards-audit.sh` runs it against a single file after each markdown edit, the seed copy a project installs does the same, and the `Markdown bans` stage in `canon gate run` runs it across the whole corpus before a push. Each hook parsed its own copy of the word bans in awk before that, which left a British spelling passing at edit time and failing the push with nothing in between explaining the difference.
157
+ Five surfaces apply the ban sets and four of them go through this verb. `.claude/hooks/standards-audit.sh` runs it against a single file after each markdown edit, the seed copy a project installs does the same, the `Markdown bans` stage in `canon gate run` runs it across the whole corpus before this repository's own push, and the same-named stage in `tooling/base/configs/scripts/verify.sh` runs it across a scaffolded project's tracked markdown before its own push. Each hook parsed its own copy of the word bans in awk before that, which left a British spelling passing at edit time and failing the push with nothing in between explaining the difference.
158
158
 
159
159
  The seed copy moved onto the verb when the sets became data, since its awk had nothing left to parse. It resolves one runner where the toolkit copy resolves two, looking for no checkout source, and a machine carrying no `canon` gets a report naming the binary to install rather than a silent pass. `scripts/core/check-seed-independence.sh` scopes its walk to markdown and leaves the seed hooks outside it, which its own comment records as deliberate.
160
160
 
161
- The fourth surface reads the standards directly and is not a consolidation left half done. `claude/skills/claude-standards-audit/SKILL.md` greps the banned tokens agent-side, which is a session reading prose rather than a process it can shell out to, and it ships to every target. It is the likeliest place for the next drift, since nothing compares it against the verb.
161
+ The target push stage reads the same exit codes `canon gate run`'s own stage reads: 0 passes, 1 is a refusal logged as a skip, 2 is a finding, and 3 is a shipped-empty ban set. It skips and logs rather than failing when no `canon` binary is on the machine's PATH, since a scaffolded project is not guaranteed the toolkit is installed, and its skip line names the install command.
162
+
163
+ The target push stage excludes `CHANGELOG.md`, unlike every other surface here. A changelog a generator builds from commit subjects carries prose nobody wrote against the ban set, where this repository's own passes only because `release-please` builds it from subjects sessions already wrote to the rule. The stage passes an explicit file list rather than the bare invocation the other surfaces use, so a corpus with no file left once the changelog is set aside reads as a pass rather than falling back to the whole tree.
164
+
165
+ The fifth surface reads the standards directly and is not a consolidation left half done. `claude/skills/claude-standards-audit/SKILL.md` greps the banned tokens agent-side, which is a session reading prose rather than a process it can shell out to, and it ships to every target. It is the likeliest place for the next drift, since nothing compares it against the verb.
162
166
 
163
167
  The hook prefers a checkout's own `src/cli.ts` over a globally installed binary, so it and the push stage read one build. A published binary lags a branch by whatever has not been released, which would put a ban kind added on the branch into the push and not into the edit. It reads its findings out of the `--json` record rather than off the exit code, so an older binary still reports where the fallback applies. It reads `bans.emptySets` out of the same record, so a set the verb shipped empty reaches the author as a check narrowed to what it could measure rather than as a clean pass.
164
168
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "4.36.1",
4
+ "version": "4.37.1",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -43,6 +43,7 @@ export interface RenamePlan {
43
43
  const TOOLKIT_OWNED: readonly string[] = [
44
44
  '.claude/aitk/',
45
45
  '.claude/canon/',
46
+ '.claude/hooks/',
46
47
  '.claude/rules/',
47
48
  '.claude/tooling/',
48
49
  ]
@@ -23,6 +23,50 @@ check_dependencies() {
23
23
  command -v bun >/dev/null 2>&1 || log_error "bun is not installed"
24
24
  }
25
25
 
26
+ check_markdown_bans() {
27
+ if ! command -v canon >/dev/null 2>&1; then
28
+ log_info "Skipped: no canon binary on PATH. Install with \`bun install --global @erclx/canon\`."
29
+ return 0
30
+ fi
31
+
32
+ local files=()
33
+ while IFS= read -r file; do
34
+ files+=("$file")
35
+ done < <(git ls-files --cached --others --exclude-standard -- '*.md' ':(exclude)CHANGELOG.md' ':(exclude)**/CHANGELOG.md')
36
+
37
+ if [ ${#files[@]} -eq 0 ]; then
38
+ log_info "No markdown file to check outside the exclusion set."
39
+ return 0
40
+ fi
41
+
42
+ local output code
43
+ if output=$(canon markdown audit "${files[@]}" 2>&1); then
44
+ code=0
45
+ else
46
+ code=$?
47
+ fi
48
+
49
+ case "$code" in
50
+ 0)
51
+ log_info "No banned character, word, or spelling"
52
+ ;;
53
+ 1)
54
+ log_info "Skipped: canon markdown audit refused and measured nothing."
55
+ ;;
56
+ 2)
57
+ echo "$output" | pipe_output
58
+ log_error "Markdown prose carries a banned character, word, or spelling, or a relative link resolves to nothing on disk"
59
+ ;;
60
+ 3)
61
+ echo "$output" | pipe_output
62
+ log_error "The markdown audit shipped an empty ban set, so the corpus was walked and nothing was looked for"
63
+ ;;
64
+ *)
65
+ log_error "canon markdown audit exited $code, which is neither a pass nor a finding"
66
+ ;;
67
+ esac
68
+ }
69
+
26
70
  run_check() {
27
71
  local cmd=$1
28
72
  local err_msg=$2
@@ -55,6 +99,9 @@ main() {
55
99
  run_check "bun run check:shell" "Shell check failed"
56
100
  log_info "Shell check passed"
57
101
 
102
+ log_step "Markdown bans"
103
+ check_markdown_bans
104
+
58
105
  if [ "$NESTED" = false ]; then
59
106
  echo -e "${GREY}└${NC}\n"
60
107
  echo -e "${GREEN}✓ Verification passed${NC}"
@@ -96,3 +96,5 @@ Sticky negative knowledge. Do not relearn.
96
96
  | `bun run prepare` | Initializes husky hooks, run automatically on `bun install` |
97
97
 
98
98
  `bun run check` repairs a checkout rather than gating one. It runs `format` first to auto-fix drifted code, then asserts only what the formatters could not fix. The pull request workflow calls `check:format`, `check:spell`, and `check:shell` directly, and those three are the gate.
99
+
100
+ `scripts/verify.sh`'s `Markdown bans` stage is the exception: it gates rather than repairs. It runs `canon markdown audit` over every tracked markdown file except `CHANGELOG.md`, since a generated changelog carries commit subjects nobody wrote against the ban set, and exits `2` on a finding or `3` on a shipped-empty ban set, failing `bun run check` and the `pre-push` hook that calls it. A refusal to measure exits `1` and is logged as a skip rather than a failure, as does a corpus with no file left to check once `CHANGELOG.md` is set aside. The stage needs `canon` on PATH. `docs/target-projects.md` names the install command. Without it, the stage is skipped and logged, and coverage falls back to the three checks above.
@@ -34,4 +34,4 @@ All `.sh` files live under `scripts/`. Do not place shell scripts outside `scrip
34
34
 
35
35
  - `pre-commit` runs `lint-staged` (prettier, cspell, shfmt, shellcheck on staged files).
36
36
  - `commit-msg` runs `commitlint` against the conventional commit format.
37
- - `pre-push` runs `bun run check`. After pushing, run `git status`. If files changed, commit the diff as `style(<scope>):` and push again.
37
+ - `pre-push` runs `bun run check`. When a markdown-bans audit tool is on PATH, it also gates on banned characters, words, and spellings across every tracked markdown file except `CHANGELOG.md`. After pushing, run `git status`. If files changed, commit the diff as `style(<scope>):` and push again.