@erclx/canon 4.119.0 → 4.121.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 +2 -2
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/SKILL.md +29 -115
- package/claude/skills/auto-ship/references/draft-mark.md +30 -0
- package/claude/skills/auto-ship/references/failure-recovery.md +27 -0
- package/claude/skills/auto-ship/references/tier-failures.md +26 -0
- package/claude/skills/auto-ship/references/verb-absent.md +37 -0
- package/claude/skills/auto-ship/references/worktree-name.md +22 -0
- package/claude/skills/bash-script/SKILL.md +1 -1
- package/claude/skills/bash-script/references/patterns.md +0 -99
- package/claude/skills/bash-script/references/prompts.md +100 -0
- package/claude/skills/create-skill/SKILL.md +3 -2
- package/claude/skills/create-snippet/SKILL.md +1 -1
- package/claude/skills/create-standard/SKILL.md +1 -1
- package/claude/skills/design-extract/SKILL.md +1 -1
- package/claude/skills/design-taste/REQUIREMENT.md +2 -2
- package/claude/skills/docs-sync/SKILL.md +1 -1
- package/claude/skills/draft-context/SKILL.md +1 -1
- package/claude/skills/draft-docs/SKILL.md +1 -1
- package/claude/skills/draft-figure/SKILL.md +1 -1
- package/claude/skills/draft-readme/SKILL.md +1 -1
- package/claude/skills/draft-wiki/SKILL.md +1 -1
- package/claude/skills/draft-wireframes/SKILL.md +1 -1
- package/claude/skills/git-issue/SKILL.md +1 -1
- package/claude/skills/git-issue/references/issue.md +1 -1
- package/claude/skills/git-pr/REQUIREMENT.md +1 -0
- package/claude/skills/git-pr/SKILL.md +35 -5
- package/claude/skills/git-stage/SKILL.md +1 -1
- package/claude/skills/memory-capture/SKILL.md +1 -1
- package/claude/skills/restate-plainly/REQUIREMENT.md +1 -1
- package/claude/skills/review-address/SKILL.md +4 -3
- package/claude/skills/review-branch/REQUIREMENT.md +1 -0
- package/claude/skills/review-branch/SKILL.md +5 -6
- package/claude/skills/review-craft/REQUIREMENT.md +43 -0
- package/claude/skills/review-craft/SKILL.md +50 -0
- package/claude/skills/review-craft/references/security.md +21 -0
- package/claude/skills/review-craft/references/ui.md +72 -0
- package/claude/skills/review-pr/REQUIREMENT.md +1 -0
- package/claude/skills/review-pr/SKILL.md +14 -243
- package/claude/skills/review-pr/references/close-out.md +30 -0
- package/claude/skills/review-pr/references/post.md +145 -0
- package/claude/skills/review-pr/references/scope.md +82 -0
- package/claude/skills/role-orchestrator/REQUIREMENT.md +9 -16
- package/claude/skills/role-orchestrator/SKILL.md +9 -153
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +4 -193
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +22 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +85 -0
- package/claude/skills/role-orchestrator/references/orchestrator-output.md +61 -0
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +56 -0
- package/claude/skills/role-orchestrator/references/orchestrator-review-fallback.md +18 -0
- package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +1 -1
- package/claude/skills/role-planner/REQUIREMENT.md +2 -2
- package/claude/skills/standards-audit/SKILL.md +1 -1
- package/claude/skills/write-human/REQUIREMENT.md +7 -4
- package/claude/skills/write-human/SKILL.md +4 -2
- package/claude/skills/write-human/references/machine-tells.md +1 -1
- package/claude/skills/write-human/references/source-material.md +5 -2
- package/docs/agents/audits.md +1 -1
- package/docs/agents/commands.md +3 -2
- package/docs/agents/counts.md +1 -1
- package/docs/agents/index.md +8 -3
- package/docs/agents/markdown-audit.md +13 -19
- package/docs/agents/pr-evidence.md +19 -3
- package/docs/agents/pr-local.md +105 -0
- package/docs/agents/pr-preview.md +2 -2
- package/docs/agents/records-push.md +61 -0
- package/docs/agents/records-validate.md +73 -0
- package/docs/agents/records.md +3 -119
- package/docs/agents/sessions-transfer.md +91 -0
- package/docs/agents/sessions.md +3 -82
- package/docs/agents/skills-audit.md +2 -2
- package/docs/agents/teach-authoring.md +112 -0
- package/docs/agents/teach.md +3 -103
- package/docs/target-projects.md +2 -2
- package/docs/workflow/ai-workflow.md +2 -1
- package/docs/workflow/visual-design-workflow.md +1 -1
- package/governance/rules/claude/500-prose.md +1 -1
- package/governance/rules/claude/501-markdown.md +3 -3
- package/governance/rules/claude/570-skill.md +2 -1
- package/package.json +1 -1
- package/scripts/core/check-ignore-parity.sh +1 -1
- package/scripts/lib/sandbox-git.sh +1 -1
- package/src/autoship/paths.ts +3 -2
- package/src/claude/cases/workflow.ts +5 -0
- package/src/commands/audits.ts +2 -2
- package/src/commands/markdown.ts +7 -9
- package/src/commands/pr.ts +331 -2
- package/src/context/classify/extract.ts +2 -2
- package/src/counts/scan.ts +1 -1
- package/src/design/tokens.ts +1 -1
- package/src/gate/measures.ts +4 -4
- package/src/gate/stages.ts +2 -3
- package/src/markdown/bans.ts +11 -68
- package/src/markdown/gate.ts +1 -1
- package/src/markdown/scan.ts +7 -88
- package/src/pr/evidence.ts +42 -2
- package/src/pr/local.ts +125 -0
- package/standards/docs.md +1 -1
- package/standards/index.md +2 -1
- package/standards/markdown.md +5 -7
- package/standards/pr.md +1 -1
- package/standards/publish.md +1 -1
- package/standards/readme.md +2 -2
- package/standards/skill-requirement.md +72 -0
- package/standards/skill.md +34 -152
- package/tooling/astro/reference.md +1 -1
- package/tooling/base/configs/.github/workflows/pr-local-preview.yml +32 -0
- package/tooling/base/configs/scripts/verify.sh +2 -2
- package/tooling/base/seeds/canon/context/development.md +1 -1
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +5 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: write-human
|
|
3
|
-
description: Carries the voice, rhythm, sentence construction, and information density rules a banned-
|
|
3
|
+
description: Carries the voice, word choice, rhythm, sentence construction, and information density rules a banned-character scan cannot express, plus the catalog of machine tells that survive a clean ban scan. Use when writing or revising prose a person will read, when a passage reads flat or uniform, or when asked to "make this read like a person", "fix the cadence", "this reads like AI wrote it", "vary the sentences", or "tighten this without gutting it". Do NOT use for banned characters, which `markdown.md` states and `canon markdown audit` gates, and do NOT use to restate text that already exists in plainer words, which is `restate-plainly`, whose body loads these rules from here.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Write human
|
|
@@ -9,7 +9,7 @@ Write prose that reads as though a person wrote it. A ban list subtracts words a
|
|
|
9
9
|
|
|
10
10
|
Load this before drafting a passage rather than after. A revision pass recovers the words and never recovers the structure the draft already settled.
|
|
11
11
|
|
|
12
|
-
The banned
|
|
12
|
+
The banned characters sit in `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`. They stay in force here. This skill adds to them and never restates them, since `canon markdown audit` reads them from the package and a second copy would drift. The word guidance under `## Voice` is the one copy of that list, and no command reads it.
|
|
13
13
|
|
|
14
14
|
## What yields and what does not
|
|
15
15
|
|
|
@@ -23,6 +23,8 @@ The banned words, characters, and spellings sit in `${CLAUDE_SKILL_DIR}/../../st
|
|
|
23
23
|
- Use active voice, and present tense unless past or future is factually correct.
|
|
24
24
|
- Name the actor in a sentence that has one. Passive voice with an unnamed actor hides who acts and reads as evasion.
|
|
25
25
|
- Prefer direct verbs and plain words. Write `use` over `utilize`, `help` over `facilitate`, `is` over `serves as`.
|
|
26
|
+
- Drop marketing words: `seamless`, `robust`, `powerful`, `revolutionary`, `enhanced`, `allows`, and `leverage`. Each claims a quality without showing it, so state what the thing does and let the reader judge it.
|
|
27
|
+
- Drop vague qualifiers: `simply`, `just`, `easily`, `quickly`, `very`, and `really`. Cut the word where the sentence stands without it, and give the measurement where the degree matters.
|
|
26
28
|
- Be direct on an established fact. Hedge on a genuinely uncertain claim and say which it is.
|
|
27
29
|
- Assume developer-level knowledge. Skip the explanation the reader already carries.
|
|
28
30
|
- Front-load the key information in a paragraph, and give every sentence something the reader did not have.
|
|
@@ -5,7 +5,7 @@ description: Patterns that survive a clean ban scan, why each is invisible to a
|
|
|
5
5
|
|
|
6
6
|
# Machine tells
|
|
7
7
|
|
|
8
|
-
Each pattern below is a shape rather than a token, so no closed word set matches it and `canon markdown audit` reports a clean exit over a passage carrying every one. Read this when revising text a model drafted, or when a passage passes the scan and still reads wrong.
|
|
8
|
+
Each pattern below is a shape rather than a token, so no closed word set matches it and the character scan `canon markdown audit` runs reports a clean exit over a passage carrying every one. Read this when revising text a model drafted, or when a passage passes the scan and still reads wrong.
|
|
9
9
|
|
|
10
10
|
Work top down. The first three account for most of what a reader calls machine-written.
|
|
11
11
|
|
|
@@ -21,10 +21,13 @@ The ambiguity rules travel with that layer for the same reason. Compression is w
|
|
|
21
21
|
|
|
22
22
|
- **The ban on abstract metaphor nouns.** The nouns it names are load-bearing terms in this corpus rather than decoration, and the count below is what decides it.
|
|
23
23
|
- **Document mode as a selector.** The source has the writer pick a mode before drafting. In this corpus a document's shape is already fixed by the standard governing its type, so a mode selector adds a second authority over one question and the two will disagree.
|
|
24
|
-
- **Any restatement of a banned word set.** The bans ship as package data that `canon markdown audit` reads, and a copy here would be a second source for one list with a machine reading the other.
|
|
25
24
|
- **A voice register per surface.** One baseline covers every surface. A surface needing a warmer register states its own voice and claims the yield, which lifts voice alone and leaves the bans, the rhythm rules, and the tells in force.
|
|
26
25
|
|
|
27
|
-
The first of those
|
|
26
|
+
The first of those three rests on a count. The source names `surface` among the nouns it bans, and tracked markdown in the authoring repository used `surface` 593 times, `harness` 95, `scaffolding` 17, and `primitive` 4. Adopting the item either rewrites established vocabulary across the corpus or is ignored in silence, and a reader arriving later cannot tell which happened.
|
|
27
|
+
|
|
28
|
+
## What moved in from the markdown standard
|
|
29
|
+
|
|
30
|
+
The marketing words and vague qualifiers under `## Voice` were a gated word set in the markdown standard until the audit narrowed to characters. A word ban catches the token and misses the habit behind it, so a writer swaps `robust` for a synonym and the claim stays unproven. The list now lives here as guidance a reader applies, and this body is its one copy, since no command reads it and a second copy would drift with nothing comparing the two.
|
|
28
31
|
|
|
29
32
|
## Where this diverges
|
|
30
33
|
|
package/docs/agents/audits.md
CHANGED
|
@@ -36,7 +36,7 @@ Each is invoked as the CLI the caller is running rather than as a global `canon`
|
|
|
36
36
|
|
|
37
37
|
## What gates and what reports
|
|
38
38
|
|
|
39
|
-
Five findings fail the run: an unresolved context citation, a banned character,
|
|
39
|
+
Five findings fail the run: an unresolved context citation, a banned character, a relative link resolving to nothing on disk, a skill folder carrying no `REQUIREMENT.md`, and a credential-shaped value in the tree the package ships. Each is a fact with no false-positive class behind it.
|
|
40
40
|
|
|
41
41
|
Four of the five are the ones `canon gate run` already fails a push on. The secret scan is the one entry gating without a stage behind it, added on that same fact-or-judgment test rather than as a side effect of registering a measure, and the toolkit already ranks content leaving the repository above content that stays.
|
|
42
42
|
|
package/docs/agents/commands.md
CHANGED
|
@@ -75,7 +75,7 @@ Full help: `canon <command> --help`. Bare `canon --help` lists every top-level c
|
|
|
75
75
|
| `canon context classify sweep` | Classify every section of the five canonical doc types, split at H3, as keep, rewrite, or move (`--doc-types`, `--json`) |
|
|
76
76
|
| `canon context classifier show` | Report the resolved classifier backend and model and which source decided them (`--json`) |
|
|
77
77
|
| `canon context classifier set` | Write the project classifier setting to `canon/config/classifier.toml` (`--backend`, `--model`, `--json`) |
|
|
78
|
-
| `canon markdown audit` | Fail any markdown path on a banned character
|
|
78
|
+
| `canon markdown audit` | Fail any markdown path on a banned character or a dead relative link, and report the structural checkpoints |
|
|
79
79
|
| `canon claude skills audit` | Report both skill corpora against the mechanical rules in `standards/skill.md` |
|
|
80
80
|
| `canon standards audit` | Report the corpus against the success-criterion gate `standards/standard.md` states, failing only on a standard new to the branch (`--json`, `--arrivals-only`) |
|
|
81
81
|
| `canon claude skills drift` | Name the shipped skill bodies rewritten between a given ref and `HEAD`, and the installed version against the newest published (`--json`) |
|
|
@@ -96,8 +96,9 @@ Full help: `canon <command> --help`. Bare `canon --help` lists every top-level c
|
|
|
96
96
|
| `canon pr head` | Compare the head a pull request object reports against the branch tip the remote carries, naming both shas (`--root`, `--json`) |
|
|
97
97
|
| `canon pr checks` | Report the check runs belonging to the branch tip, reading pending for a tip carrying none rather than clean, and `conflicted` when the branch conflicts with its base (`--root`, `--json`) |
|
|
98
98
|
| `canon pr review-state` | Report the commit and instant the last review pass covered, off the pass's own marker rather than the fields GitHub stamps at submission (`--root`, `--json`) |
|
|
99
|
-
| `canon pr evidence` | Render a before-and-after comment comparing changed `evidence/` images against the merge base, editing its own prior comment in place rather than posting a second (`--root`, `--json`, `--preview`)
|
|
99
|
+
| `canon pr evidence` | Render a before-and-after comment comparing changed `evidence/` images against the merge base, editing its own prior comment in place rather than posting a second (`--root`, `--json`, `--preview`, `--checklist`, `--local`) |
|
|
100
100
|
| `canon pr preview` | Dispatch the Pages deploy on a pull request's branch and report the preview address the run prints, refusing a deploy that passes no `--branch` (`--root`, `--json`, `--timeout`) |
|
|
101
|
+
| `canon pr local` | Report the `localhost` address of the dev server this worktree is running, never one in another worktree, or replace that line on the evidence comment with a note on `--remove` (`--root`, `--json`, `--remove`, `--note`) |
|
|
101
102
|
| `canon repo metadata propose` | Compare a description, homepage, and topic set computed from the README and `package.json` against what the remote carries, writing nothing (`--root`, `--json`) |
|
|
102
103
|
| `canon repo metadata apply` | Write an explicitly supplied description, homepage, or topic set to the remote through `gh repo edit` (`--description`, `--homepage`, `--topics`, `--root`, `--json`) |
|
|
103
104
|
| `canon census [path]` | Report tracked file count, a breakdown by extension, and a line total that skips whatever reads as binary (`--json`) |
|
package/docs/agents/counts.md
CHANGED
|
@@ -43,7 +43,7 @@ Nothing stands between the trigger and the number, which is the rule an author w
|
|
|
43
43
|
|
|
44
44
|
The verb gate is not the first design tried. A bare number next to a catalog noun anywhere in the tracked corpus returned 290 findings against a repository whose actionable instance count was one. Reading that run showed why: `18 rules citing a standard`, `21 skill bodies`, and `eight internal skills` all pair a number with a catalog noun while naming a subset, an example, or a different catalog than the one matched, and that shape dominates ordinary prose. Every instance this sweep was written against reads the number as the direct object of a verb asserting the catalog's own total, and gating on that verb is what took the false-positive count from 290 to five on the same tree.
|
|
45
45
|
|
|
46
|
-
The article half was added after a first review of the shipped design found a live miss:
|
|
46
|
+
The article half was added after a first review of the shipped design found a live miss: the development domain's gating-stages entry stated a stale audit total while the tree held 20, and the verb gate never reached it, since that sentence puts its verb after the noun rather than ahead of the number. Widening the trigger set to admit an article ahead of the number closed that gap.
|
|
47
47
|
|
|
48
48
|
The quantifier `all` and the verb `authors` joined on the same evidence, two more live misses failing only the trigger test. Allowing words to stand between the trigger and the number was the other candidate, measured and declined: at widths of one, two, and three it reached 77, 104, and 132 sentences against a baseline of 65, missed `took all 81 rules` at every width, and reached `the toolkit authors 81 rules` only at a width admitting the indirect-noun shape below. The two words reach both at 75 sentences and no false positive.
|
|
49
49
|
|
package/docs/agents/index.md
CHANGED
|
@@ -31,16 +31,20 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
|
|
|
31
31
|
- [Output shape](output-shape.md): Two framed shapes every command renders into, how JSON and --names modes keep stdout clean, and the exit discipline that lets piped output drain
|
|
32
32
|
- [Overview](overview.md): What this folder covers, the invocation rules every command inherits, and where domain behavior is documented instead
|
|
33
33
|
- [The pull request evidence comparison](pr-evidence.md): What canon pr evidence compares, the marker that lets it edit its own comment rather than duplicate it, the refusal reasons it names, and why the comparison anchors at the merge base rather than the previous push
|
|
34
|
+
- [The pull request local preview](pr-local.md): How canon pr local finds the server a worktree is running, why a server in another worktree is never reported, the refusal reasons it names, and how a close workflow removes the link with --remove
|
|
34
35
|
- [The pull request preview deploy](pr-preview.md): How canon pr preview finds a fenced Cloudflare Pages deploy, dispatches it on a pull request's branch, reads the alias the run prints, the refusal reasons it names, and why an unfenced deploy is refused before anything runs
|
|
35
36
|
- [Head-sensitive pull request reads](pr-reads.md): Resolving a branch tip from the remote rather than from the pull request object, reading what a review pass covered off its own marker rather than off GitHub's submission stamps, why an empty run list is not a pass, the refusal reasons each verb names, and what the remote read costs
|
|
36
|
-
- [Records](records.md):
|
|
37
|
+
- [Records push](records-push.md): Backing the record folders to a private remote and restoring them, which folders the payload carries, the one-time setup, the refusal reasons, and the hook that runs the push
|
|
38
|
+
- [Records validate](records-validate.md): Validating the session records and the standards corpus, the per-kind checks, which root each kind defaults to, the exit codes, and the refusal reasons
|
|
39
|
+
- [Records](records.md): The two roots a record folder resolves at, migrating a record a frontmatter change orphaned, claiming the ordinal intake and groundwork share, reading each folder's size and growth, pruning stale scratch, and where validation and the private-remote backup are described
|
|
37
40
|
- [Restated instructions](restated.md): Counting the instructions the always-loaded file and every path-scoped rule share with the seed, the shipped skill bodies, and each other, how a match is decided, the three classes, which surface a later edit starts from, and why the sweep reports rather than gates
|
|
38
41
|
- [Review classification](review-classification.md): Deciding whether a changed set needs the review pass, the two tests it runs, why an empty set refuses rather than skipping, and the written fallback a target on an older binary falls back to
|
|
39
42
|
- [Routing report](routing.md): Reading per CLAUDE.md section how many bullets name a path, what counts as naming one, when a rule counts as covering it, the two refusals, and why the verb reports rather than gates
|
|
40
43
|
- [Rule citations](rule-citations.md): Resolving every path a governance rule cites and every frontmatter glob the internal corpus declares, the three forms a citation is written in, the shapes that look like citations and are not, the two classes where an absent path is correct, why the glob half reads one corpus, and why this one gates
|
|
41
44
|
- [Sandbox](sandbox.md): Scenario routing, the expectation scoring surface, and the coverage census over scenarios and skills
|
|
42
45
|
- [Scripting](scripting.md): The runtime catalogs that replace hardcoded names, what each carries, and a headless invocation per domain
|
|
43
|
-
- [Sessions](sessions.md):
|
|
46
|
+
- [Sessions transfer](sessions-transfer.md): Moving a session to another machine, the export bundle and its manifest, placing it with import, the refusal reasons, and what a resume on the target depends on
|
|
47
|
+
- [Sessions](sessions.md): Resolving live peer sessions to the worktree and branch each holds, reading which row is the caller, the liveness confidence field, what each session surface can see, and where moving a session to another machine is described
|
|
44
48
|
- [Skill audit](skills-audit.md): Measuring both skill corpora against standards/skill.md, the checks it reads, the requirement gate that is the only failing one, and the drift verb that names bodies rewritten since a ref
|
|
45
49
|
- [Citation reach](skills-reach.md): Reporting the skill bodies that cite a path no target project receives, which corpus the verb reads, the ownership key that decides what counts, the one-word qualifier that marks a citation as decided, and why the verb reports instead of gating
|
|
46
50
|
- [Standard success criteria](standards-audit.md): Reading the corpus against the success-criterion gate, the two headings it accepts, why the check scopes to arrival rather than the whole corpus, and the exit codes it sets
|
|
@@ -48,6 +52,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
|
|
|
48
52
|
- [Superseded values](superseded.md): Reading where the tree still asserts a value a changed convention no longer produces, why the sweep keys on the value rather than the file, the family stem behind a templated citation, the exemption marker, the blind spots it cannot reach, and why it reports rather than gates
|
|
49
53
|
- [Targets](targets.md): The projects this toolkit installed into, the record the install writes against the sweep that backs it, what bounds each answer, and the cross-target pull request read
|
|
50
54
|
- [Tasks](tasks.md): Selecting a shipped task by stem or pull request, recording a number and closing an outcome, deriving the branch a dispatch and a worker both take, the refusal reasons, the board and backlog checks validate runs, and why the board root defaults to the main worktree
|
|
51
|
-
- [Teach](teach.md):
|
|
55
|
+
- [Teach authoring](teach-authoring.md): Resolving what the next lesson needs before it is written, rendering a lesson body's blocks to HTML, and rewriting the root listing, a contents page, and each lesson's chrome and quiz stepper from its marker regions
|
|
56
|
+
- [Teach](teach.md): Listing learning workspaces with what their records schedule next, opening one with its required files, recording sources and glossary terms, where the lesson authoring verbs are described, the refusal reasons, and why every write here runs through a verb
|
|
52
57
|
- [Test order](test-order.md): Reading where an implementation reached history ahead of its test, how a pair is decided, the three verdicts, the coverage the pairing cannot reach, and why the check reports rather than gates
|
|
53
58
|
- [Worktrees](worktrees.md): Reporting which worktrees are reclaimable, removing the ones that are, the record a hook reads back, why the reading keys on the pull request rather than on git ancestry, the refusals it names, and the two removal shapes
|
|
@@ -27,7 +27,7 @@ A bare run measures every markdown file git lists, tracked plus untracked-and-no
|
|
|
27
27
|
|
|
28
28
|
## Where the rules come from
|
|
29
29
|
|
|
30
|
-
The
|
|
30
|
+
The ban set and all ten checkpoints ship with the `canon` package as data, in `src/markdown/bans.ts` and `src/markdown/structure.ts`. Every project is measured against the same set whether or not it installed any standards, and no file has to resolve for a run to mean something.
|
|
31
31
|
|
|
32
32
|
Seven of the ten are stated in `markdown.md` and the three cadence numbers are stated in the `write-human` skill. That split is the content boundary rather than an accident: `markdown.md` carries the enforced rules a scan can decide, and the skill carries the rhythm rules a ban list cannot express. A cadence number moved in the skill and left in the code drifts the same way, so move both.
|
|
33
33
|
|
|
@@ -35,11 +35,9 @@ Reading them out of the standards per run was the original design. It put a pars
|
|
|
35
35
|
|
|
36
36
|
`markdown.md` still states every ban and every checkpoint, and a reader follows it rather than the code. Nothing compares the two, so a number moved in one place and left in the other drifts silently. Move both in the same change.
|
|
37
37
|
|
|
38
|
-
The
|
|
38
|
+
The set is closed rather than extensible, so a project cannot add a term by editing a file. It holds the two characters alone, since a character is the one class a literal match settles. The set once carried 13 words and 6 British spellings as well. The words moved to the `write-human` skill as guidance, because a word ban catches the token and misses the habit, and `just`, `allows`, and `very` carry honest uses no literal match separates. Spelling went to a project's spell checker, which already reads every word.
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
A set shipped empty is reported rather than passed. It finds nothing and would exit clean, which reports a corpus nobody checked as a corpus carrying no violation, so the run names the empty set and exits `1`. The sets ship with the package, so a defect in the build is the only cause left.
|
|
40
|
+
A set shipped empty is reported rather than passed. It finds nothing and would exit clean, which reports a corpus nobody checked as a corpus carrying no violation, so the run names the empty set and exits `3`. The set ships with the package, so a defect in the build is the only cause left.
|
|
43
41
|
|
|
44
42
|
`canon standards <name>` still resolves a standard at the authoring root, then the package corpus, and prints it, so the human catalog reads without a project copy on disk.
|
|
45
43
|
|
|
@@ -47,17 +45,11 @@ A set shipped empty is reported rather than passed. It finds nothing and would e
|
|
|
47
45
|
|
|
48
46
|
### Bans
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Deriving the spellings rather than pattern-matching a suffix is what keeps `exercises`, `promises`, and `revised` out of the report. A suffix pattern over the same corpus produced 46 false positives from words of that shape, and a closed set of whole words reaches none of them.
|
|
48
|
+
One closed set reports a hit: the characters `markdown.md` bans under `## Punctuation`.
|
|
53
49
|
|
|
54
50
|
Frontmatter, fenced blocks, inline code spans, and link destinations are excluded. Without the code-span exclusion each standard would report its own backticked examples, and without the link exclusion a semicolon in a query string would report as prose no rewrite can fix.
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
A banned spelling keeps the plain word boundary, hyphens included. The two bans target different things: a word ban targets the word, so reading a compound as one word is correct, while a spelling ban targets the orthography inside it, which sits in `behaviour-driven` as plainly as it sits alone.
|
|
59
|
-
|
|
60
|
-
Two ban shapes stay unmeasured and the report says so on every run. A multi-word ban escapes the harvest by width, whether it carries a placeholder standing in for the rest of the sentence or spells the phrase out in full, and every rule under `## Voice` is a judgment. The bans `## Language` states over what a sentence may claim sit in the first group by construction, since a literal match over a pattern reports the compliant text and reaches none of the violations. A report listing hits without naming those would read as a verdict on the whole standard.
|
|
52
|
+
Word choice stays unmeasured and the report says so on every run. Single words travel in the `write-human` skill as guidance a reader applies. A multi-word ban escapes the harvest by width, whether it carries a placeholder standing in for the rest of the sentence or spells the phrase out in full, and every rule under `## Voice` is a judgment. The bans `## Language` states over what a sentence may claim are patterns of the multi-word kind by construction, since a literal match over a pattern reports the compliant text and reaches none of the violations. A report listing hits without naming those would read as a verdict on the whole standard.
|
|
61
53
|
|
|
62
54
|
### Links
|
|
63
55
|
|
|
@@ -108,7 +100,7 @@ A bullet, a heading, a table row, a blockquote, a blank line, and a fence each e
|
|
|
108
100
|
|
|
109
101
|
### Cadence
|
|
110
102
|
|
|
111
|
-
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
|
|
103
|
+
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.
|
|
112
104
|
|
|
113
105
|
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.
|
|
114
106
|
|
|
@@ -134,7 +126,7 @@ The Length step sums rendered lines over the whole source, frontmatter and fence
|
|
|
134
126
|
|
|
135
127
|
## Exit codes
|
|
136
128
|
|
|
137
|
-
Exit codes are `0` for a completed run with no gating finding, `1` for a refusal, `2` for a ban hit or a dead relative link, and `3` for a shipped ban set that arrived empty. A banned character
|
|
129
|
+
Exit codes are `0` for a completed run with no gating finding, `1` for a refusal, `2` for a ban hit or a dead relative link, and `3` for a shipped ban set that arrived empty. A banned character and a relative link resolving to nothing on disk each fail the run, both facts a scan settles rather than a reader judging. Bullet, paragraph, and depth weight are judgments a reader settles, and cadence is a distribution whose healthy range moves with the surface, so all four report under every code. Length reports the same way, since the audit also runs on a plan kept out of version control, and the Document ceiling stage in `canon gate run` owns that verdict instead.
|
|
138
130
|
|
|
139
131
|
`3` is separate from `1` because the two want different responses from a caller. A refusal means no corpus was built, and the `Markdown bans` stage in `canon gate run` is right to report it as unmeasured rather than as a pass. An empty set means the corpus was walked and nothing was looked for, so that stage fails the push on `3` rather than skipping.
|
|
140
132
|
|
|
@@ -154,11 +146,11 @@ Rewrite the sentence rather than swapping the banned token for a near-synonym. T
|
|
|
154
146
|
|
|
155
147
|
A code span clears the report too, since the ban scan walks around one, and it is the answer only where the token is genuinely an identifier under discussion. `## Code and identifiers` in `markdown.md` reserves the span for commands, API names, file paths, and identifiers, so backticking a quoted utterance spends one rule to satisfy another and leaves the corpus no cleaner.
|
|
156
148
|
|
|
157
|
-
A hit the closed set
|
|
149
|
+
A hit the closed set could not separate from correct prose was the case with no third option while the set still carried words. The temporal `just` reported as the vague qualifier it happened to spell, and rewriting the sentence is what the toolkit settled on over building an exemption path, for the reasons below. That class is part of why the words left the gate.
|
|
158
150
|
|
|
159
151
|
### Where the rules are enforced
|
|
160
152
|
|
|
161
|
-
Five surfaces apply the ban
|
|
153
|
+
Five surfaces apply the ban set 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.
|
|
162
154
|
|
|
163
155
|
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.
|
|
164
156
|
|
|
@@ -170,7 +162,7 @@ The fifth surface reads the standards directly and is not a consolidation left h
|
|
|
170
162
|
|
|
171
163
|
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.
|
|
172
164
|
|
|
173
|
-
That field replaced `bans.missingStandards`, which answered a standard resolving under none of three roots. The
|
|
165
|
+
That field replaced `bans.missingStandards`, which answered a standard resolving under none of three roots. The set ships with the package now, so the state it named cannot occur and the narrowed check has one cause left, a defect in the build. The hook keeps reading a field either way, since a reader cannot tell a narrowed check from a clean one without it.
|
|
174
166
|
|
|
175
167
|
Both hooks answer an absent record as well. A completed run always writes the record and a refusal writes none, so an empty one means the verb declined to measure rather than measured and found nothing. The verb needs a git repository to build its corpus and refuses without one, which is a project the seeded hook can be installed into, and reading the findings alone reported that as a clean file.
|
|
176
168
|
|
|
@@ -188,9 +180,11 @@ Masking took 7 of the weight-only paragraphs the checkpoint reported at 400 and
|
|
|
188
180
|
|
|
189
181
|
### How the ban count reached zero
|
|
190
182
|
|
|
183
|
+
The set still carried words and spellings when the gate turned on, so this section and the next are history rather than a description of the current scan.
|
|
184
|
+
|
|
191
185
|
Eight word hits stood between the baseline and a gate, and only three carried the sense the standard bans. `leverage` sat in the requirements worldview, `allows` in the claude stack reference, and one `just` was the vague qualifier in a skill body. Those three lost the qualifier rather than the word.
|
|
192
186
|
|
|
193
|
-
The other five were correct prose the closed set cannot separate from a violation. Four were the temporal `just`, meaning a moment ago, in phrases like the implementation `just` completed and the field the user `just` edited. The fifth quoted an anti-pattern a skill exists to forbid. `markdown.md`
|
|
187
|
+
The other five were correct prose the closed set cannot separate from a violation. Four were the temporal `just`, meaning a moment ago, in phrases like the implementation `just` completed and the field the user `just` edited. The fifth quoted an anti-pattern a skill exists to forbid. `markdown.md` banned vague qualifiers and listed the tokens those qualifiers happen to spell, so the rule as written reached none of the five while the scan reached all of them.
|
|
194
188
|
|
|
195
189
|
### Why they were rewritten rather than exempted
|
|
196
190
|
|
|
@@ -122,14 +122,30 @@ already holds, the same way the preview address is carried. That is what keeps
|
|
|
122
122
|
already ticked, since `git-pr` deletes the handoff once the first post reports
|
|
123
123
|
success and no later call has a file to pass.
|
|
124
124
|
|
|
125
|
-
The option does not decide `no-evidence
|
|
126
|
-
changed evidence image still reports `no-evidence`, and the caller posts the
|
|
127
|
-
checklist on its own, which is what it did before this option existed. A
|
|
125
|
+
The option does not decide `no-evidence` alone. A branch with a checklist and
|
|
126
|
+
no changed evidence image still reports `no-evidence`, and the caller posts the
|
|
127
|
+
checklist on its own, which is what it did before this option existed. A
|
|
128
|
+
checklist passed together with `--local` is the exception, covered below. A path
|
|
128
129
|
that cannot be read, or one holding nothing, refuses as
|
|
129
130
|
`unreadable-checklist` rather than rendering without it: the caller deletes the
|
|
130
131
|
handoff once a post succeeds, so a silently dropped checklist is the only copy
|
|
131
132
|
gone.
|
|
132
133
|
|
|
134
|
+
## The local preview line
|
|
135
|
+
|
|
136
|
+
`--local <url>` adds `**Local preview:** <url>` to the opening block: under the
|
|
137
|
+
hosted `**Preview:**` line when there is one, and as the first line when there
|
|
138
|
+
is not. The hosted line always keeps the first position, since that is the one
|
|
139
|
+
line the carry-forward reads for it. A run without the option carries forward
|
|
140
|
+
the local address the marked comment already holds, the same as the other two.
|
|
141
|
+
|
|
142
|
+
The option does not turn `no-evidence` into `ok` on its own. A branch with a
|
|
143
|
+
dev server running and no rendered change would otherwise get a comment holding
|
|
144
|
+
a link and nothing else. Together with `--checklist` it does, so the link and
|
|
145
|
+
the checklist land in one comment rather than the checklist going out alone.
|
|
146
|
+
`canon docs pr-local` covers where the address comes from and how the line is
|
|
147
|
+
removed at close.
|
|
148
|
+
|
|
133
149
|
## What a collapsed comment still leaves to GitHub
|
|
134
150
|
|
|
135
151
|
GitHub already draws its own before-and-after comparison on the Files Changed
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The pull request local preview
|
|
3
|
+
description: How canon pr local finds the server a worktree is running, why a server in another worktree is never reported, the refusal reasons it names, and how a close workflow removes the link with --remove
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# The pull request local preview
|
|
7
|
+
|
|
8
|
+
`canon pr local` reports the `localhost` address of the dev server the current
|
|
9
|
+
worktree is running. `git-pr` calls it ahead of the evidence comparison and
|
|
10
|
+
passes the address through `canon pr evidence --local`, so a reviewer on the
|
|
11
|
+
same machine gets the running branch to click into beside the screenshots and
|
|
12
|
+
the checklist. A workflow on the pull request's `closed` event calls it again
|
|
13
|
+
with `--remove`, which replaces the line with a note.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
canon pr local --json
|
|
17
|
+
canon pr local 1341 --remove --json
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How it finds the server
|
|
21
|
+
|
|
22
|
+
The verb reads every TCP socket in `LISTEN` with the process that owns it. It
|
|
23
|
+
keeps a socket when that process's working directory sits inside the
|
|
24
|
+
worktree's toplevel, including a subfolder, since some tools change into one
|
|
25
|
+
before they listen. It then requests `/` on each kept port and reports the
|
|
26
|
+
lowest one serving an HTML page.
|
|
27
|
+
|
|
28
|
+
The HTML test is there because a working tree often holds more than one
|
|
29
|
+
listener. A test runner's own server, or a dev server's reload socket, answers
|
|
30
|
+
HTTP too, and a reviewer opening either gets a bare 404 rather than the branch.
|
|
31
|
+
|
|
32
|
+
Sockets are read from `/proc/net/tcp` and `/proc/net/tcp6` wherever they exist,
|
|
33
|
+
matched against each process's `fd` table, and through `lsof` on a machine
|
|
34
|
+
without them. Linux reads `/proc` even with `lsof` installed, since `lsof` 4.95
|
|
35
|
+
drops a process whose truncated name holds an unmatched `(`, which is what
|
|
36
|
+
Next's `next-server (vX.Y.Z)` title is cut down to. A machine with neither
|
|
37
|
+
refuses as `no-listener-reader`.
|
|
38
|
+
|
|
39
|
+
The port is never derived from the worktree. A port helper needs a per-stack
|
|
40
|
+
base port the toolkit cannot know, and a server started by hand or by an
|
|
41
|
+
earlier session would not match it anyway.
|
|
42
|
+
|
|
43
|
+
## Why another worktree's server is never reported
|
|
44
|
+
|
|
45
|
+
A server running in a different worktree shows a different branch, and a link
|
|
46
|
+
to it on this pull request would show a reviewer the wrong code.
|
|
47
|
+
|
|
48
|
+
- A sibling linked worktree sits outside this one's toplevel, so its process
|
|
49
|
+
never matches.
|
|
50
|
+
- The main checkout's server, read from a linked worktree, sits outside the
|
|
51
|
+
linked worktree's toplevel for the same reason.
|
|
52
|
+
- A linked worktree's server, read from the main checkout, sits inside the main
|
|
53
|
+
toplevel. The verb excludes everything under `.claude/worktrees/` for that
|
|
54
|
+
case alone.
|
|
55
|
+
|
|
56
|
+
## What the record carries
|
|
57
|
+
|
|
58
|
+
`reason` on the record is what a caller branches on, not the exit code:
|
|
59
|
+
|
|
60
|
+
| Reason | What it means |
|
|
61
|
+
| -------------------- | --------------------------------------------------------------------- |
|
|
62
|
+
| `ok` | A server answered. `url` carries the address and `port` the port. |
|
|
63
|
+
| `no-server` | Nothing inside this worktree served an HTML page. |
|
|
64
|
+
| `no-listener-reader` | Neither `lsof` nor `/proc` is available, so no socket could be read. |
|
|
65
|
+
| `removed` | `--remove` replaced the local line on the evidence comment. |
|
|
66
|
+
| `no-comment` | `--remove` found no comment carrying the evidence marker. |
|
|
67
|
+
| `no-line` | `--remove` found the comment but no local line on it. |
|
|
68
|
+
| `gh-missing` | `--remove` needs `gh` and it is not on the path. |
|
|
69
|
+
| `gh-failed` | `--remove` could not read the comments or could not edit the comment. |
|
|
70
|
+
|
|
71
|
+
The exit is 0 on `ok` and on every `--remove` outcome that finished, including
|
|
72
|
+
the two no-ops, and 1 on every refusal. A caller on an older binary gets an
|
|
73
|
+
unknown-subcommand error instead of a record, which `git-pr` reads as a skip.
|
|
74
|
+
|
|
75
|
+
## Removing the link at close
|
|
76
|
+
|
|
77
|
+
The link goes dead once the branch's server stops, and nothing on the pull
|
|
78
|
+
request says so. `--remove` runs on the pull request's `closed` event, merged or
|
|
79
|
+
abandoned, from a workflow seeded into every target:
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
on:
|
|
83
|
+
pull_request:
|
|
84
|
+
types: [closed]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
It reads the comment carrying the `pr-evidence` marker, replaces the
|
|
88
|
+
`**Local preview:**` line with the `--note` text, and edits the comment itself
|
|
89
|
+
through `gh api`. The verb writes here rather than handing a body back, because
|
|
90
|
+
its caller is a workflow with no session to post one. Every other byte of the
|
|
91
|
+
comment stays as it was, which keeps the marker, the checklist delimiters, and
|
|
92
|
+
any ticked box intact. A second run finds no line and reports `no-line`.
|
|
93
|
+
|
|
94
|
+
A fork pull request gets a read-only token, so the workflow skips the edit with
|
|
95
|
+
a notice rather than failing the close.
|
|
96
|
+
|
|
97
|
+
## What this does not cover
|
|
98
|
+
|
|
99
|
+
The link can still go dead while the pull request is open, such as when the
|
|
100
|
+
session holding the server retires. Only the close is covered, and a re-render
|
|
101
|
+
after a push carries the line forward whether the server still answers or not.
|
|
102
|
+
|
|
103
|
+
The hosted `**Preview:**` line from `canon pr preview` stays on the comment
|
|
104
|
+
after close, even though its deployments are deleted then. `canon docs
|
|
105
|
+
pr-preview` covers that path.
|
|
@@ -100,5 +100,5 @@ request never deploys.
|
|
|
100
100
|
## What this does not cover
|
|
101
101
|
|
|
102
102
|
The verb builds only the deployed-and-public case. A private repository whose
|
|
103
|
-
previews need access protection
|
|
104
|
-
|
|
103
|
+
previews need access protection waits for a project that needs it. A local
|
|
104
|
+
server stands in where there is no deploy, through `canon docs pr-local`.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Records push
|
|
3
|
+
description: Backing the record folders to a private remote and restoring them, which folders the payload carries, the one-time setup, the refusal reasons, and the hook that runs the push
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Records push
|
|
7
|
+
|
|
8
|
+
`canon records push` commits the backed record folders to a private remote and pushes them. `canon records pull` fetches the other direction and writes them back. Both take `--json` and `--root` the way `validate` does, and both exit `0` on agreement and `1` on a refusal.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
canon records push
|
|
12
|
+
canon records push --json
|
|
13
|
+
canon records pull
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
At the `.canon` root, the backed folders are every top-level directory less three: `tmp`, which is deletable without loss, `ordinal-locks`, whose entries are transient per claim and would race the claim they guard, and `.records.git`, which is the history the rest are pushed into. Nothing bounds the set from outside, since the claude manifest ships one `.canon/` root entry and names no folder, so a record folder added later enters the payload on its own rather than waiting on a name written here. A push names each folder in scope that the records index has never tracked before, so a folder that picked up a name by mistake, such as a misrouted scratch write, is visible in the report rather than entering the payload silently. The report also lists every file new to the history under `added`, since a stray file inside a folder already tracked is visible only by name.
|
|
17
|
+
|
|
18
|
+
The three excluded names bound the records index as well as the disk. A name an older binary committed, such as `tmp`, is removed from the history once and reported under `dropped`, and no later push stages it again. That removes it from the tip going forward and purges nothing from earlier commits the remote already holds. A project's own drafts folder needs no config to be backed: moved to `.canon/<name>/`, it is a top-level directory like any other and enters the payload on the next push. The legacy `.claude` root keeps a fixed allowlist instead, since that root also holds tracked `skills/`, `rules/`, and `hooks/` a push must never carry, and an exclusion set there would stage all three. Each name is a top-level record folder and every archive sits inside the one it archives, so the set stays at one entry per surface however many archives appear, and it deliberately does not match the six record kinds `validate` hardcodes.
|
|
19
|
+
|
|
20
|
+
Records are gitignored by design, so the history lives in a second git directory at `.records.git` inside the record root, with that root as its work tree. Both resolve off the root together rather than folder by folder, since a history opened at one root beside a work tree at the other would stage the deletion of every folder a move relocated. Every path stays where it is, which is what a separate checkout could not do. The verbs stage the backed folders by explicit pathspec with `--force`, so nothing outside them can enter the index however the ignore rules read, and the project working tree and its index are never touched. Each pathspec is a bare folder name and git reads it against the current directory rather than against the work tree the same call names, so the invocation carries `-C` at the work tree beside the other two flags. That is what lets either verb run from a linked worktree under `.claude/worktrees/`, which sits inside the records work tree and would otherwise prefix every name with its own path.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
A person creates the records repository once per machine, and the verbs refuse with the commands when it is absent:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git --git-dir=.canon/.records.git init
|
|
28
|
+
git --git-dir=.canon/.records.git remote add origin <private-repo-url>
|
|
29
|
+
printf '.canon/\n' >> .gitignore
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The ignore line is repeated here rather than left to the install, because the person running these commands is the one who creates the directory and the rule is worth reading beside the command that needs it. The claude manifest ships `.canon/` too, so a project that ran `canon tooling sync` already carries it and this line is a no-op there. One root entry covers the history and every record beside it, which is what makes the rule worth stating once rather than per folder.
|
|
33
|
+
|
|
34
|
+
Point it at a private repository, and at one that is not a remote of the project. Records carry the memory pen, the review reports, and the groundwork trails, so a public project publishes all of it to anyone who fetches all refs. `push` compares the configured origin against every remote of the project and refuses on a match. A read of that list which fails refuses as well, since an empty list clears the comparison for every origin and a gate that passes on its own failure is no gate.
|
|
35
|
+
|
|
36
|
+
## Refusals
|
|
37
|
+
|
|
38
|
+
| Reason | What fired |
|
|
39
|
+
| ------------------- | -------------------------------------------------------------------------------------- |
|
|
40
|
+
| `split-roots` | Record folders sit under both roots, so the resolved work tree is not the whole set |
|
|
41
|
+
| `no-repository` | No `.canon/.records.git`, answered with the two setup commands |
|
|
42
|
+
| `no-remote` | The records history has no `origin` |
|
|
43
|
+
| `remote-unreadable` | The project's own remotes could not be read, so the shared-origin gate could not run |
|
|
44
|
+
| `remote-shared` | The records origin is also a remote of the project |
|
|
45
|
+
| `no-remote-records` | `pull` found no branch on the records origin |
|
|
46
|
+
| `local-changes` | `pull` found records on disk that the history does not carry |
|
|
47
|
+
| `local-ahead` | `pull` found local commits that never reached the origin |
|
|
48
|
+
| `unsafe-payload` | `push` found a pending file over 25 MB or carrying a credential, named under `blocked` |
|
|
49
|
+
| `git-failed` | A git call failed, with its stderr in the message |
|
|
50
|
+
|
|
51
|
+
`split-roots` runs ahead of every gate below it and fires on a half-migrated tree, which is what a `canon migrate records` run that failed partway leaves. `recordRoot` answers for the whole tree on the first root that exists, so a folder left at the old root is absent from the work tree while the records index still names it, and an unguarded `add -A` would stage its deletion and drop it from the remote on the next push. Finish the move, or put the stranded folders back beside the others.
|
|
52
|
+
|
|
53
|
+
`unsafe-payload` runs before anything is staged, so a refusal leaves the index, the log, and the object store as it found them. It reads every new or changed file the push would stage and blocks one over 25 MB, which is read off the file's size without opening it, or one carrying a value `canon secrets scan` would report, through the same patterns and the same `canon-allow-secret` marker. It refuses the whole push rather than skipping the file, and `--json` lists each path with its cause and a detail that names the credential's kind and line, never its value. Move the file out of the record folders, such as into `.canon/tmp/`, or remove the credential, then push again. The guard reads only what this push would add, so a credential committed before it existed stays in the history unreported.
|
|
54
|
+
|
|
55
|
+
A push the remote rejects undoes the commit that run made and leaves the records on disk, so the next run does not send a payload the remote already refused. A history an older binary committed and left unpushed is outside that undo, and the guard never reads it either, so every push sends the same blob again. Reset it by hand from the project root to the last commit the origin holds, `git --git-dir=.canon/.records.git --work-tree=.canon reset <commit>`, and push again. The `--work-tree` is required, since the setup `init` marks the history bare and git refuses that reset on a bare repository.
|
|
56
|
+
|
|
57
|
+
The two `pull` refusals exist because the directions are not symmetric. A push only adds, while a pull onto a machine holding work that never left it would discard that work. Resolve either by running `push` first, or by moving the local folders aside. A machine holding no backed folders has nothing to lose, so a restore onto a fresh checkout runs straight through.
|
|
58
|
+
|
|
59
|
+
## When it runs
|
|
60
|
+
|
|
61
|
+
`.husky/post-merge` runs `push` after the task archiving loop, on every merge rather than only on one that archived a task. A review report and a memory entry both land on runs that close nothing. The call sits inside an `if` and last in the file, so an unreachable remote neither aborts the hook nor delays the archiving above it, and a checkout that never ran the setup reports nothing. Anything the hook misses is covered by running the verb by hand.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Records validate
|
|
3
|
+
description: Validating the session records and the standards corpus, the per-kind checks, which root each kind defaults to, the exit codes, and the refusal reasons
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Records validate
|
|
7
|
+
|
|
8
|
+
`canon records validate <kind>` reports where a file and the standard governing it disagree. Five kinds are gitignored folders under the record root, resolved at `.canon/` first: `plans`, `groundwork`, `intake`, `memory`, and `teach`. The sixth is `standards`, the authoring corpus, which is tracked and installed rather than scratch.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
canon records validate plans
|
|
12
|
+
canon records validate memory
|
|
13
|
+
canon records validate standards
|
|
14
|
+
canon records validate intake --json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Option | Behavior |
|
|
18
|
+
| --------------- | ------------------------------------------------------------------- |
|
|
19
|
+
| `--json` | Add a machine-readable record on stdout |
|
|
20
|
+
| `--root <path>` | Project root, defaulting to the main worktree except on `standards` |
|
|
21
|
+
|
|
22
|
+
It reads and never writes, and the reason splits by kind. A session record is per-machine scratch with no history behind it, so a repair that guessed wrong could not be undone. A standard installs into every target and is cited by bare filename, so a rename the verb performed would reach further than the file it moved, which is why a finding naming one says so.
|
|
23
|
+
|
|
24
|
+
`standards` reads the authoring root at `standards/` where it exists and a copy at `.claude/standards/` otherwise. The authoring root wins because it is the only tree anyone authors in and the only one the resolver answers from, so a finding fixed anywhere else is fixed where nothing reads it. No repository generates the second candidate any more, which leaves it as a floor under a target holding a copy an older toolkit installed. The walk stays flat, matching the catalog.
|
|
25
|
+
|
|
26
|
+
Nothing fires it automatically. The five record folders are gitignored, so the standards-audit hook exits early on them and any check reading changed files from git never lists one. The corpus is tracked and still unreached, since the markdown audit reads content across the files git lists and rules on no filename. The verb runs at the moment a session claims the record is finished, which is the same placement `canon tasks validate` takes over the board.
|
|
27
|
+
|
|
28
|
+
The two record roots every kind resolves at are in `records.md`.
|
|
29
|
+
|
|
30
|
+
## What each kind checks
|
|
31
|
+
|
|
32
|
+
| Kind | What it reports |
|
|
33
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| `plans` | A filename that is not `feature-<slug>.md`, a missing `# Feature:` heading, a missing required section, a files-to-touch entry naming no file or saying nothing about one, a question carrying no suggestion or no answer slot, and a batch still staged with a `**Batch N**` sub-heading inside the one file rather than split into its own plan |
|
|
35
|
+
| `groundwork` | A track with no `README.md` or no `01-current-state.md`, a file missing `title` or `description`, a `README.md` with no `date` as `YYYY-MM-DD`, an unnumbered file, and a track holding a decision without its handoff or the reverse |
|
|
36
|
+
| `intake` | A dump with no `00-overview.md`, the same frontmatter and numbering checks, an item missing any of `Problem`, `Fix`, `Worth it`, or `You`, and an item carrying `Open` with no `Suggested` |
|
|
37
|
+
| `memory` | A filename whose prefix names none of the four types, an entry missing `title`, `description`, or `category`, a `category` disagreeing with that prefix, a title repeating the filename, and a rule-bearing body missing a part |
|
|
38
|
+
| `standards` | A standard missing `title` or `description`, an absent `## Scope` section, a scope section carrying no `Does not govern:` list, a statement anchoring nothing, and a filename naming no part of the path the statement governs |
|
|
39
|
+
| `teach` | A workspace folder carrying no two-digit ordinal, an absent `MISSION.md`, `RESOURCES.md`, or `GLOSSARY.md`, a file missing `title` or `description`, a mission with no `date` as `YYYY-MM-DD` or no `## Success looks like` section, an unnumbered learning record, and a reference page opening with an ordinal |
|
|
40
|
+
|
|
41
|
+
The half-closed track is the groundwork check a reader cannot run by eye. A folder holding `06` without `07` reads as closed to anyone scanning filenames while the file a returning session actually opens is absent.
|
|
42
|
+
|
|
43
|
+
The item check skips `00-overview.md` and `99-next-session.md`, since neither holds items and running it over the handoff would report every heading it carries. The memory walk skips `index.md` for the same reason, since the catalog is generated from its siblings rather than authored as an entry.
|
|
44
|
+
|
|
45
|
+
A memory `category` is compared against the sentence-case form of the filename prefix rather than checked field by field, so one finding covers a prefix outside the four types, a field disagreeing with the prefix, and a casing drift that would open a second group in the catalog. The body check runs on `feedback` and `project` entries alone, because a `user` or `reference` entry is a single sentence by design and has no rule to apply.
|
|
46
|
+
|
|
47
|
+
The standards filename check derives a word from the governed path rather than counting words in the name. Every member of the corpus is named for the artifact its scope statement governs, and one word is what that produces rather than the rule itself, so a check keyed on word count would pass a conforming single word naming the wrong artifact. Each path segment offers its own word, a dotted container segment offers none, a hyphenated segment offers its parts, and the singular and the plural both match.
|
|
48
|
+
|
|
49
|
+
The scope statement is read exactly as `scripts/standards/list.sh` reads it for the catalog's `appliesTo` field: backticked spans in the first sentence alone, with an attribute standard resolving where that sentence backticks nothing and the statement says it governs an attribute. One sentence read two ways would let a standard pass the check while publishing a different jurisdiction to every consumer of the catalog. An attribute standard is exempt from the filename derivation because it governs no path to derive from, and a statement that anchors nothing and claims no attribute is reported as `scope-unanchored` rather than passed, since silence there would let the backticks be removed to disable the check.
|
|
50
|
+
|
|
51
|
+
An absent `## Scope` section suppresses the filename finding. The name derives from the statement, so a missing section leaves nothing to derive against, and reporting both would name one defect twice and point the fix at the wrong file. The walk skips `index.md`, which is generated from its siblings rather than authored.
|
|
52
|
+
|
|
53
|
+
A plan section opens as a bold label or as an H2 and the check counts both, naming the standard's spelling when it reports one missing. The corpus splits roughly four to one between the two forms, so failing the variant would report nearly every plan on a rule that costs a reader nothing.
|
|
54
|
+
|
|
55
|
+
A section runs to the next marker-shaped line whatever it names, so a plan carrying a label of its own closes the section above it rather than collecting into it. Fenced blocks are dropped before any of this, since a plan showing the shape it writes puts real-looking bullets and headings inside a fence.
|
|
56
|
+
|
|
57
|
+
## Exit codes and refusals
|
|
58
|
+
|
|
59
|
+
Exit codes: `0` every check passed, `1` refused, `2` at least one record carries a finding. A `reason` field carries which gate fired: `no-folder` when none of the kind's directories exist, and `unknown-kind` when the argument names no published kind. A `no-folder` message names every candidate, so a record kind's refusal names both record roots and the `standards` refusal names the authoring root and the installed copy.
|
|
60
|
+
|
|
61
|
+
An exit code says nothing about a call made from a session, since a shell profile may wrap the binary in a function taking its status from a later command. Read the record's `findings` array and its `reason` rather than the exit when a skill consumes this.
|
|
62
|
+
|
|
63
|
+
The four record folders are shared scratch at the main worktree root, so `--root` defaults to the first entry of `git worktree list` rather than the working directory. A linked worktree validates the same records every other session reads.
|
|
64
|
+
|
|
65
|
+
`standards` takes the other default, the root of the checkout the caller stands in. The corpus is tracked, so a linked worktree holds its own edited copy, and resolving the main root there would report on a tree the session never touched while saying nothing about which one it read. A session that adds or renames a standard inside a worktree is the case, and it is the one the check exists for.
|
|
66
|
+
|
|
67
|
+
Skills branch on the findings rather than on the exit code:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
canon records validate plans --json | jq -r '.findings[] | "\(.kind): \(.subject)"'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For the shapes each check enforces, see `standards/plan.md`, `standards/groundwork.md`, `standards/intake.md`, `standards/memory.md`, and `standards/standard.md`.
|