@erclx/canon 4.118.0 → 4.120.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/canon-rollout/SKILL.md +2 -2
- package/claude/skills/create-skill/SKILL.md +2 -1
- package/claude/skills/design-extract/SKILL.md +1 -1
- package/claude/skills/docs-fold/SKILL.md +2 -2
- package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
- package/claude/skills/draft-ready/SKILL.md +1 -1
- package/claude/skills/draft-screencast/SKILL.md +1 -1
- package/claude/skills/git-pr/REQUIREMENT.md +1 -0
- package/claude/skills/git-pr/SKILL.md +39 -7
- package/claude/skills/memory-capture/SKILL.md +3 -3
- package/claude/skills/memory-review/SKILL.md +3 -3
- package/claude/skills/plan-feature/SKILL.md +1 -1
- package/claude/skills/review-branch/SKILL.md +1 -1
- package/claude/skills/review-pr/SKILL.md +13 -242
- 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 +1 -1
- package/claude/skills/role-planner/SKILL.md +1 -1
- package/claude/skills/role-worker/REQUIREMENT.md +1 -1
- package/claude/skills/role-worker/SKILL.md +10 -41
- package/claude/skills/seed-sync/SKILL.md +1 -1
- package/claude/skills/session-compact/SKILL.md +1 -1
- package/claude/skills/session-map/SKILL.md +1 -1
- package/claude/skills/session-relay/REQUIREMENT.md +7 -1
- package/claude/skills/session-relay/SKILL.md +4 -7
- package/claude/skills/session-worktree/REQUIREMENT.md +5 -1
- package/claude/skills/session-worktree/SKILL.md +7 -1
- package/claude/skills/session-worktree/references/main-root-writes.md +24 -0
- package/claude/skills/task-board/SKILL.md +4 -3
- package/claude/skills/teach-workspace/SKILL.md +4 -4
- package/claude/skills/ui-checklist/SKILL.md +1 -1
- package/claude/skills/ux-audit/SKILL.md +1 -1
- package/claude/skills/ux-measure/SKILL.md +1 -1
- package/claude/skills/ux-walkthrough/references/relay.md +1 -1
- package/docs/agents/commands.md +4 -3
- package/docs/agents/counts.md +1 -1
- package/docs/agents/index.md +8 -3
- package/docs/agents/install-and-sync.md +11 -6
- package/docs/agents/pr-evidence.md +19 -3
- package/docs/agents/pr-local.md +102 -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/tasks.md +4 -4
- 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 +10 -10
- package/docs/workflow/visual-design-workflow.md +2 -2
- package/governance/renames.toml +13 -0
- package/governance/rules/claude/570-skill.md +2 -1
- package/governance/rules/core/085-worktrees.md +2 -8
- package/package.json +1 -1
- package/scripts/core/check-ignore-parity.sh +1 -1
- package/scripts/lib/sandbox-git.sh +1 -1
- package/src/claude/cases/workflow.ts +10 -0
- package/src/commands/pr.ts +324 -2
- package/src/commands/sync.ts +11 -3
- package/src/commands/tasks.ts +9 -8
- package/src/context/classify/extract.ts +2 -2
- package/src/counts/scan.ts +1 -1
- package/src/design/base.css +19 -19
- package/src/design/board.ts +18 -7
- package/src/design/neutral.ts +127 -0
- package/src/design/regen.ts +6 -4
- package/src/design/tokens.ts +6 -3
- package/src/gate/stages.ts +6 -6
- package/src/gov/adapter.ts +51 -1
- package/src/gov/install.ts +13 -3
- package/src/gov/renames.ts +76 -0
- package/src/gov/stacks.ts +5 -1
- package/src/pr/evidence.ts +42 -2
- package/src/pr/local.ts +125 -0
- package/src/sync/check.ts +19 -4
- package/src/sync/engine.ts +67 -3
- package/src/tasks/archive.ts +28 -7
- package/src/tasks/record.ts +16 -9
- package/src/tasks/validate.ts +3 -1
- package/standards/index.md +1 -0
- package/standards/skill-requirement.md +72 -0
- package/standards/skill.md +34 -152
- package/standards/tasks.md +5 -3
- package/tooling/astro/reference.md +1 -1
- package/tooling/base/configs/.github/workflows/pr-local-preview.yml +32 -0
- package/governance/rules/core/091-channel.md +0 -13
|
@@ -32,10 +32,10 @@ canon teach list --json
|
|
|
32
32
|
|
|
33
33
|
It reports every workspace with its path, its counts, and the ordinal a new one would take. A `no-teach` refusal means the project has opened none yet, which is the new-workspace path in Step 1 rather than a reason to stop. The open verb creates the folder.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Every path under the main root is a main-root write, routed the way `session-worktree` states. The route splits by what the write does to the file:
|
|
36
36
|
|
|
37
|
-
- Creating a whole file that does not exist yet, which is a lesson, a reference page, and a learning record, goes out as
|
|
38
|
-
- Changing a line inside a file that already exists goes through the verb that owns it
|
|
37
|
+
- Creating a whole file that does not exist yet, which is a lesson, a reference page, and a learning record, goes out as a heredoc
|
|
38
|
+
- Changing a line inside a file that already exists goes through the verb that owns it
|
|
39
39
|
|
|
40
40
|
## Step 1: open or resume
|
|
41
41
|
|
|
@@ -239,7 +239,7 @@ Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. Fall back to
|
|
|
239
239
|
|
|
240
240
|
The handoff is its own file rather than a shared one. The routed-facts file another skill writes is deleted by whichever pass folds it, so a second producer's unread work goes with it, and a sibling path costs the folding skill one more read and removes the interaction.
|
|
241
241
|
|
|
242
|
-
An append is a whole-file operation, so send it as a
|
|
242
|
+
An append is a whole-file operation, so send it as a heredoc, per Step 0. Then tell the operator that `/docs-fold` folds the file in from a branch. The proposal costs nothing tracked and runs anywhere, while the page it describes is a tracked file, so the fold is a worktree operation and the workspace it came from is not.
|
|
243
243
|
|
|
244
244
|
## Output
|
|
245
245
|
|
|
@@ -72,7 +72,7 @@ When a checklist is produced, write it directly to `.canon/tmp/handoff/ui-checkl
|
|
|
72
72
|
|
|
73
73
|
The path names the artifact rather than this skill, so it does not move when the skill is renamed. A checklist written by an older binary still lands where the consumer looks.
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
The checklist is a main-root write, so it goes out as a heredoc, routed the way `session-worktree` states.
|
|
76
76
|
|
|
77
77
|
Skip the file write when there is nothing to verify visually and nothing shipping untested.
|
|
78
78
|
|
|
@@ -64,7 +64,7 @@ Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. Fall back to
|
|
|
64
64
|
|
|
65
65
|
Write the full report directly to `.canon/review/ux-audit-<slug>.md` at the main worktree root, not the current worktree. Resolve that root the way `session-worktree` does. Create the directory if it does not exist. Always overwrite.
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
The report is a main-root write, so it goes out as a heredoc, routed the way `session-worktree` states.
|
|
68
68
|
|
|
69
69
|
If there are no observations, write `✅ No observations.` to the file with a timestamp.
|
|
70
70
|
|
|
@@ -108,7 +108,7 @@ Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. Fall back to
|
|
|
108
108
|
|
|
109
109
|
Write the full reading directly to `.canon/review/ux-measure-<slug>.md` at the main worktree root, not the current worktree. Resolve that root the way `session-worktree` does. Create the directory if it does not exist. Always overwrite.
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
The reading is a main-root write, so it goes out as a heredoc, routed the way `session-worktree` states.
|
|
112
112
|
|
|
113
113
|
The `.canon/review/` directory is gitignored. Do not stage or commit the file.
|
|
114
114
|
|
|
@@ -4,7 +4,7 @@ Read when the operator calls a batch, and at the start of the walkthrough when a
|
|
|
4
4
|
|
|
5
5
|
## Where the batch goes
|
|
6
6
|
|
|
7
|
-
- Send it to the controller where the session was dispatched by one,
|
|
7
|
+
- Send it to the controller where the session was dispatched by one, through `canon:session-relay`, which resolves the address and carries the send.
|
|
8
8
|
- Give it to the operator as one list in the conversation where no controller exists. The walkthrough file is the handoff either way.
|
|
9
9
|
|
|
10
10
|
## When
|
package/docs/agents/commands.md
CHANGED
|
@@ -23,10 +23,10 @@ Full help: `canon <command> --help`. Bare `canon --help` lists every top-level c
|
|
|
23
23
|
| `canon indexes list [path]` | Flatten every folder index under a path into one `{path, title, description}` catalog (`--json`) |
|
|
24
24
|
| `canon docs [topic]` | Emit toolkit reference docs (`list`, or a topic by name) |
|
|
25
25
|
| `canon design render` | Render `canon/DESIGN.md` tokens to HTML and CSS, declaring each vendored face the typography table names with `--embed-fonts` |
|
|
26
|
-
| `canon design regen` | Rewrite this repository's `canon/DESIGN.md` and `src/design/base.css` from `src/design/
|
|
26
|
+
| `canon design regen` | Rewrite this repository's `canon/DESIGN.md` from `src/design/tokens.ts` and `src/design/base.css` from `src/design/neutral.ts` |
|
|
27
27
|
| `canon design board` | Generate a static page set indexing tokens, surfaces, wireframes, past candidates, components, and references against a caller-resolved `--root` (`--out`), defaulting to the main worktree |
|
|
28
28
|
| `canon design css` | Emit the design tokens and components as CSS on stdout (`--no-components` for properties alone) |
|
|
29
|
-
| `canon design install` | Install the base stylesheet into a project at `.claude/design/base.css`
|
|
29
|
+
| `canon design install` | Install the neutral base stylesheet into a project at `.claude/design/base.css` |
|
|
30
30
|
| `canon design sync` | Update a base stylesheet already installed under `.claude/design/` |
|
|
31
31
|
| `canon slides render` | Render a `.claude/SLIDES.md` source into a PowerPoint deck, reporting any unrecognized layout name on stderr |
|
|
32
32
|
| `canon slides list` | List the available slide layouts (`--json` for the catalog) |
|
|
@@ -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
|
|
@@ -19,10 +19,13 @@ removes a stale `.claude/GOV.md` from the retired build. Use
|
|
|
19
19
|
|
|
20
20
|
Everything under `.claude/rules/canon/` belongs to the toolkit, so a rule
|
|
21
21
|
there that the toolkit no longer ships is deleted on sync, edited or not. That
|
|
22
|
-
is how a retired
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
is how a retired rule stops loading in a target. A renamed or renumbered rule
|
|
23
|
+
the toolkit declares in `governance/renames.toml` moves instead: the sync
|
|
24
|
+
installs the rule under its new name and deletes the old file, whether or not
|
|
25
|
+
the target's stamp records a stack. Local edits to the old file are not
|
|
26
|
+
carried, and the sync says so. The sync lists each change by path before it
|
|
27
|
+
applies, and the target's git diff is the record afterwards. `canon sync
|
|
28
|
+
--check` lists the same file as `retired` or `renamed` before any sync runs. A project's own rules belong under `.claude/rules/project/`, where
|
|
26
29
|
`canon standards rule` reserves `900-999` for them, and a rule placed under
|
|
27
30
|
`canon/` instead is lost on the next sync.
|
|
28
31
|
|
|
@@ -164,9 +167,11 @@ matches what the toolkit installed, `customized` when the project edited it,
|
|
|
164
167
|
`stranded` when it sits at a path the toolkit no longer installs to, `orphaned`
|
|
165
168
|
when the project authored it, or `drifted` when no stamp covers it. Governance
|
|
166
169
|
also reports `missing`, for a rule the target's recorded stack lists that its
|
|
167
|
-
tree does not hold at all,
|
|
170
|
+
tree does not hold at all, `retired`, for a rule under
|
|
168
171
|
`.claude/rules/canon/` the toolkit no longer ships, which the next sync
|
|
169
|
-
deletes
|
|
172
|
+
deletes, and `renamed`, for one the toolkit ships under a new name, which the
|
|
173
|
+
next sync moves. A `retired` or `renamed` file counts toward `--exit-code`,
|
|
174
|
+
since one sync clears it.
|
|
170
175
|
|
|
171
176
|
Use `--json` for the machine-readable report and `--exit-code` to fail a CI job. Orphaned
|
|
172
177
|
and missing files are both excluded from that exit code: a project-authored
|
|
@@ -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,102 @@
|
|
|
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 through `lsof` where it is installed. A Linux machine without
|
|
33
|
+
it falls back to `/proc/net/tcp` and `/proc/net/tcp6`, matched against each
|
|
34
|
+
process's `fd` table. A machine with neither refuses as `no-listener-reader`.
|
|
35
|
+
|
|
36
|
+
The port is never derived from the worktree. A port helper needs a per-stack
|
|
37
|
+
base port the toolkit cannot know, and a server started by hand or by an
|
|
38
|
+
earlier session would not match it anyway.
|
|
39
|
+
|
|
40
|
+
## Why another worktree's server is never reported
|
|
41
|
+
|
|
42
|
+
A server running in a different worktree shows a different branch, and a link
|
|
43
|
+
to it on this pull request would show a reviewer the wrong code.
|
|
44
|
+
|
|
45
|
+
- A sibling linked worktree sits outside this one's toplevel, so its process
|
|
46
|
+
never matches.
|
|
47
|
+
- The main checkout's server, read from a linked worktree, sits outside the
|
|
48
|
+
linked worktree's toplevel for the same reason.
|
|
49
|
+
- A linked worktree's server, read from the main checkout, sits inside the main
|
|
50
|
+
toplevel. The verb excludes everything under `.claude/worktrees/` for that
|
|
51
|
+
case alone.
|
|
52
|
+
|
|
53
|
+
## What the record carries
|
|
54
|
+
|
|
55
|
+
`reason` on the record is what a caller branches on, not the exit code:
|
|
56
|
+
|
|
57
|
+
| Reason | What it means |
|
|
58
|
+
| -------------------- | --------------------------------------------------------------------- |
|
|
59
|
+
| `ok` | A server answered. `url` carries the address and `port` the port. |
|
|
60
|
+
| `no-server` | Nothing inside this worktree served an HTML page. |
|
|
61
|
+
| `no-listener-reader` | Neither `lsof` nor `/proc` is available, so no socket could be read. |
|
|
62
|
+
| `removed` | `--remove` replaced the local line on the evidence comment. |
|
|
63
|
+
| `no-comment` | `--remove` found no comment carrying the evidence marker. |
|
|
64
|
+
| `no-line` | `--remove` found the comment but no local line on it. |
|
|
65
|
+
| `gh-missing` | `--remove` needs `gh` and it is not on the path. |
|
|
66
|
+
| `gh-failed` | `--remove` could not read the comments or could not edit the comment. |
|
|
67
|
+
|
|
68
|
+
The exit is 0 on `ok` and on every `--remove` outcome that finished, including
|
|
69
|
+
the two no-ops, and 1 on every refusal. A caller on an older binary gets an
|
|
70
|
+
unknown-subcommand error instead of a record, which `git-pr` reads as a skip.
|
|
71
|
+
|
|
72
|
+
## Removing the link at close
|
|
73
|
+
|
|
74
|
+
The link goes dead once the branch's server stops, and nothing on the pull
|
|
75
|
+
request says so. `--remove` runs on the pull request's `closed` event, merged or
|
|
76
|
+
abandoned, from a workflow seeded into every target:
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
on:
|
|
80
|
+
pull_request:
|
|
81
|
+
types: [closed]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
It reads the comment carrying the `pr-evidence` marker, replaces the
|
|
85
|
+
`**Local preview:**` line with the `--note` text, and edits the comment itself
|
|
86
|
+
through `gh api`. The verb writes here rather than handing a body back, because
|
|
87
|
+
its caller is a workflow with no session to post one. Every other byte of the
|
|
88
|
+
comment stays as it was, which keeps the marker, the checklist delimiters, and
|
|
89
|
+
any ticked box intact. A second run finds no line and reports `no-line`.
|
|
90
|
+
|
|
91
|
+
A fork pull request gets a read-only token, so the workflow skips the edit with
|
|
92
|
+
a notice rather than failing the close.
|
|
93
|
+
|
|
94
|
+
## What this does not cover
|
|
95
|
+
|
|
96
|
+
The link can still go dead while the pull request is open, such as when the
|
|
97
|
+
session holding the server retires. Only the close is covered, and a re-render
|
|
98
|
+
after a push carries the line forward whether the server still answers or not.
|
|
99
|
+
|
|
100
|
+
The hosted `**Preview:**` line from `canon pr preview` stays on the comment
|
|
101
|
+
after close, even though its deployments are deleted then. `canon docs
|
|
102
|
+
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`.
|