@erclx/canon 5.38.0 → 5.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/SKILL.md +12 -9
- package/claude/skills/auto-ship/SKILL.md +43 -23
- package/claude/skills/backlog-triage/SKILL.md +62 -53
- package/claude/skills/backlog-triage/references/apply.md +69 -0
- package/claude/skills/build-in-slices/SKILL.md +8 -4
- package/claude/skills/canon-cli/SKILL.md +5 -1
- package/claude/skills/canon-feedback/SKILL.md +18 -8
- package/claude/skills/canon-feedback-triage/SKILL.md +10 -3
- package/claude/skills/canvas-board/SKILL.md +37 -18
- package/claude/skills/ci-workflow/SKILL.md +5 -4
- package/claude/skills/code-craft/SKILL.md +10 -6
- package/claude/skills/codebase-layout/SKILL.md +3 -5
- package/claude/skills/context-fold/REQUIREMENT.md +4 -4
- package/claude/skills/context-fold/SKILL.md +42 -23
- package/claude/skills/context-fold/references/context-refresh.md +4 -3
- package/claude/skills/context-fold/references/context.md +79 -64
- package/claude/skills/create-rule/SKILL.md +7 -3
- package/claude/skills/create-skill/SKILL.md +27 -7
- package/claude/skills/create-skill/references/skill-reference.md +41 -17
- package/claude/skills/decision-escalate/SKILL.md +8 -5
- package/claude/skills/deploy-app/SKILL.md +1 -1
- package/claude/skills/deprecation-migration/SKILL.md +12 -4
- package/claude/skills/design-taste/SKILL.md +17 -11
- package/claude/skills/docs-sync/REQUIREMENT.md +1 -0
- package/claude/skills/docs-sync/SKILL.md +17 -6
- package/claude/skills/docs-sync/references/docs.md +54 -21
- package/claude/skills/docs-sync/references/readme.md +32 -8
- package/claude/skills/document-health/SKILL.md +3 -4
- package/claude/skills/draft-and-pick/SKILL.md +73 -47
- package/claude/skills/draft-doc/SKILL.md +9 -4
- package/claude/skills/draft-doc/references/context.md +4 -4
- package/claude/skills/draft-doc/references/docs.md +1 -0
- package/claude/skills/draft-doc/references/wiki.md +3 -2
- package/claude/skills/draft-figure/SKILL.md +9 -7
- package/claude/skills/draft-identity/SKILL.md +24 -52
- package/claude/skills/draft-identity/references/card-route.md +46 -0
- package/claude/skills/draft-ready/SKILL.md +19 -7
- package/claude/skills/draft-screencast/SKILL.md +6 -112
- package/claude/skills/draft-screencast/references/beats-template.md +119 -0
- package/claude/skills/draft-slides/SKILL.md +9 -7
- package/claude/skills/draft-slides/references/deck.md +183 -0
- package/claude/skills/git-branch/SKILL.md +3 -3
- package/claude/skills/git-commit/SKILL.md +5 -3
- package/claude/skills/git-followup/SKILL.md +16 -9
- package/claude/skills/git-issue/SKILL.md +6 -5
- package/claude/skills/git-pr/SKILL.md +56 -44
- package/claude/skills/git-pr/references/evidence.md +2 -2
- package/claude/skills/git-pr/references/final-command.md +1 -1
- package/claude/skills/git-pr/references/labels.md +1 -1
- package/claude/skills/git-pr/references/publish.md +1 -1
- package/claude/skills/git-ship/SKILL.md +43 -34
- package/claude/skills/git-ship/references/ci-watch.md +31 -0
- package/claude/skills/git-split/SKILL.md +28 -100
- package/claude/skills/git-split/references/independent.md +40 -0
- package/claude/skills/git-split/references/stacked.md +73 -0
- package/claude/skills/git-stage/SKILL.md +9 -7
- package/claude/skills/git-worktree/SKILL.md +27 -49
- package/claude/skills/git-worktree/references/cleanup.md +55 -0
- package/claude/skills/markdown-craft/SKILL.md +2 -2
- package/claude/skills/markdown-craft/references/markdown.md +82 -53
- package/claude/skills/plan-feature/SKILL.md +52 -46
- package/claude/skills/plan-feature/references/batches.md +46 -0
- package/claude/skills/plan-groundwork/SKILL.md +60 -74
- package/claude/skills/plan-groundwork/references/output.md +48 -0
- package/claude/skills/plan-intake/SKILL.md +28 -21
- package/claude/skills/plan-intake-answer/SKILL.md +14 -13
- package/claude/skills/read-frames/SKILL.md +2 -0
- package/claude/skills/record-screencast/SKILL.md +31 -11
- package/claude/skills/review-address/SKILL.md +76 -158
- package/claude/skills/review-address/references/rebase-only.md +34 -0
- package/claude/skills/review-craft/SKILL.md +27 -11
- package/claude/skills/review-pr/SKILL.md +91 -40
- package/claude/skills/review-ui/SKILL.md +63 -37
- package/claude/skills/review-ui/references/hosted-preview.md +30 -0
- package/claude/skills/role-orchestrator/SKILL.md +105 -74
- package/claude/skills/role-orchestrator/references/orchestrator-filing.md +26 -0
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
- package/claude/skills/role-planner/SKILL.md +66 -64
- package/claude/skills/role-reviewer/SKILL.md +57 -46
- package/claude/skills/role-worker/SKILL.md +68 -63
- package/claude/skills/search-craft/SKILL.md +7 -5
- package/claude/skills/session-compact/SKILL.md +16 -11
- package/claude/skills/session-map/SKILL.md +31 -10
- package/claude/skills/session-relay/SKILL.md +32 -65
- package/claude/skills/session-resume/SKILL.md +18 -13
- package/claude/skills/session-worktree/SKILL.md +70 -42
- package/claude/skills/systematic-debugging/SKILL.md +6 -1
- package/claude/skills/target-check/SKILL.md +13 -6
- package/claude/skills/target-setup/SKILL.md +39 -27
- package/claude/skills/task-board/SKILL.md +68 -60
- package/claude/skills/task-board/references/board.md +1 -1
- package/claude/skills/task-board/references/create-report.md +36 -0
- package/claude/skills/task-board/references/tasks.md +1 -1
- package/claude/skills/task-complete/SKILL.md +20 -18
- package/claude/skills/teach-workspace/SKILL.md +61 -33
- package/claude/skills/test-craft/SKILL.md +2 -1
- package/claude/skills/test-first/SKILL.md +15 -9
- package/claude/skills/ui-checklist/SKILL.md +18 -16
- package/claude/skills/ux-audit/SKILL.md +9 -10
- package/claude/skills/ux-measure/SKILL.md +23 -19
- package/claude/skills/video-craft/SKILL.md +15 -7
- package/claude/skills/write-human/SKILL.md +9 -1
- package/claude/skills/youtube-transcripts/SKILL.md +2 -1
- package/docs/agents/audits.md +61 -60
- package/docs/agents/context-audit.md +40 -58
- package/docs/agents/index.md +11 -61
- package/docs/agents/indexes.md +25 -29
- package/docs/agents/install-and-sync.md +125 -235
- package/docs/agents/markdown-audit.md +54 -96
- package/docs/agents/migrate.md +41 -16
- package/docs/agents/output-shape.md +60 -45
- package/docs/agents/overview.md +36 -12
- package/docs/agents/records.md +37 -79
- package/docs/agents/sessions-transfer.md +26 -70
- package/docs/agents/targets.md +25 -77
- package/docs/target/index.md +0 -1
- package/docs/target/migrations.md +57 -66
- package/docs/target/projects.md +36 -78
- package/docs/target/scenarios.md +9 -19
- package/docs/target/sync.md +27 -68
- package/docs/workflow/ai-workflow.md +22 -68
- package/docs/workflow/operating-model.md +32 -186
- package/docs/workflow/parallel-features.md +29 -66
- package/docs/workflow/skill-map-on-request.md +22 -24
- package/docs/workflow/skill-map.md +72 -76
- package/docs/workflow/visual-design-workflow.md +25 -118
- package/docs/workflow/zshrc-aliases.md +18 -37
- package/package.json +1 -1
- package/src/canvas/client/inspector/fields.css +4 -0
- package/src/canvas/index.html +6 -0
- package/src/claude/skills-reach.ts +2 -2
- package/src/cli/ui.ts +3 -1
- package/src/commands/canvas.ts +23 -0
- package/src/commands/capture.ts +14 -0
- package/src/commands/claude.ts +14 -1
- package/src/commands/context.ts +106 -7
- package/src/commands/demo.ts +22 -0
- package/src/commands/deps.ts +6 -0
- package/src/commands/driver.ts +28 -2
- package/src/commands/gov.ts +52 -18
- package/src/commands/hooks.ts +63 -0
- package/src/commands/intake.ts +17 -0
- package/src/commands/labels.ts +38 -5
- package/src/commands/markdown.ts +169 -32
- package/src/commands/pr.ts +160 -43
- package/src/commands/records.ts +8 -0
- package/src/commands/secrets.ts +6 -0
- package/src/commands/sessions.ts +8 -0
- package/src/commands/tasks.ts +52 -0
- package/src/commands/teach.ts +13 -0
- package/src/commands/upstream.ts +25 -1
- package/src/commands/worktrees.ts +5 -0
- package/src/context/audit.ts +274 -5
- package/src/context/classify/prompts.ts +1 -1
- package/src/context/folders.ts +1 -1
- package/src/context/gate.ts +23 -7
- package/src/context/shape-gated.ts +30 -0
- package/src/counts/scan.ts +1 -1
- package/src/design/components.ts +1 -1
- package/src/design/document.ts +1 -1
- package/src/design/tokens.ts +1 -1
- package/src/gate/boundaries.ts +4 -1
- package/src/gate/measures.ts +16 -191
- package/src/gate/stages.ts +14 -30
- package/src/git/env.ts +6 -0
- package/src/hooks/reply-links.ts +110 -0
- package/src/markdown/density.ts +258 -0
- package/src/markdown/gate.ts +57 -18
- package/src/markdown/reference-shape.ts +103 -0
- package/src/markdown/scope.ts +162 -0
- package/src/markdown/structure.ts +9 -59
- package/src/pr/lift.ts +153 -19
- package/src/pr/painted.ts +87 -0
- package/src/roots/record.ts +18 -1
- package/src/sandbox/census.ts +1 -1
- package/src/sandbox/headless/run.ts +3 -2
- package/src/sandbox/headless/watch.ts +3 -4
- package/src/shipped/references.ts +1 -1
- package/src/slides/convert/master.ts +3 -0
- package/src/sync/format.ts +46 -0
- package/src/sync/stamp.ts +6 -0
- package/tooling/astro/reference.md +2 -2
- package/tooling/base/reference.md +1 -1
- package/tooling/claude/seeds/.claude/hooks/reply-links.sh +72 -0
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +22 -1
- package/tooling/claude/seeds/.claude/settings.json +4 -0
- package/tooling/go/reference.md +1 -1
- package/tooling/nextjs/reference.md +1 -1
- package/tooling/php/reference.md +1 -1
- package/tooling/python/reference.md +1 -1
- package/tooling/vite-react/reference.md +1 -1
- package/tooling/web/reference.md +1 -1
- package/docs/agents/canvas.md +0 -86
- package/docs/agents/capture.md +0 -65
- package/docs/agents/census.md +0 -23
- package/docs/agents/commands.md +0 -154
- package/docs/agents/comments.md +0 -34
- package/docs/agents/context-audit-checks.md +0 -90
- package/docs/agents/context-classify.md +0 -101
- package/docs/agents/counts.md +0 -74
- package/docs/agents/demo.md +0 -140
- package/docs/agents/docs.md +0 -27
- package/docs/agents/drift-surfaces.md +0 -201
- package/docs/agents/driver.md +0 -103
- package/docs/agents/gate.md +0 -90
- package/docs/agents/gov-detect.md +0 -47
- package/docs/agents/hooks.md +0 -58
- package/docs/agents/intake.md +0 -81
- package/docs/agents/key-changes.md +0 -116
- package/docs/agents/label-coverage.md +0 -86
- package/docs/agents/markdown-audit-weight.md +0 -81
- package/docs/agents/pr-evidence-layout.md +0 -70
- package/docs/agents/pr-evidence.md +0 -273
- package/docs/agents/pr-frames.md +0 -131
- package/docs/agents/pr-lift.md +0 -132
- package/docs/agents/pr-local.md +0 -111
- package/docs/agents/pr-preview.md +0 -155
- package/docs/agents/pr-reads.md +0 -111
- package/docs/agents/pr-tick.md +0 -72
- package/docs/agents/records-push.md +0 -61
- package/docs/agents/records-stale.md +0 -40
- package/docs/agents/records-validate.md +0 -57
- package/docs/agents/restated.md +0 -83
- package/docs/agents/routing.md +0 -61
- package/docs/agents/rule-citations.md +0 -90
- package/docs/agents/sandbox.md +0 -126
- package/docs/agents/scripting.md +0 -142
- package/docs/agents/sessions.md +0 -128
- package/docs/agents/skills-audit.md +0 -120
- package/docs/agents/skills-reach.md +0 -64
- package/docs/agents/slides.md +0 -176
- package/docs/agents/state-scoped-risk.md +0 -105
- package/docs/agents/superseded.md +0 -85
- package/docs/agents/tasks-archive.md +0 -83
- package/docs/agents/tasks-plans.md +0 -152
- package/docs/agents/tasks-validate.md +0 -133
- package/docs/agents/tasks.md +0 -123
- package/docs/agents/teach-authoring.md +0 -118
- package/docs/agents/teach.md +0 -157
- package/docs/agents/test-order.md +0 -58
- package/docs/agents/upstream.md +0 -94
- package/docs/agents/worktrees.md +0 -118
- package/docs/target/standards-moved.md +0 -34
- package/src/markdown/growth.ts +0 -43
package/docs/agents/routing.md
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Routing report
|
|
3
|
-
description: 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
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Routing report
|
|
7
|
-
|
|
8
|
-
`canon claude routing` reports, per `CLAUDE.md` section, how many top-level bullets name a path and how many of those a path-scoped rule already covers. It answers the firing axis of the tier test in `canon/context/context-model/overview.md`, whether a fact applies every session or fires on one path, and which until this verb existed was a judgment nothing counted. The tier test names two further axes, conditional presence and updatability, that this verb does not check.
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
canon claude routing
|
|
12
|
-
canon claude routing --json
|
|
13
|
-
canon claude routing ../my-app
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
| Option | Behavior |
|
|
17
|
-
| -------- | ------------------------------------------------------------ |
|
|
18
|
-
| `[path]` | Repository root to read, defaulting to the current directory |
|
|
19
|
-
| `--json` | Add a machine-readable record on stdout, keeping the frame |
|
|
20
|
-
|
|
21
|
-
Under `--json` the record holds stdout alone and the frame still renders on stderr, refusals included, which is the split `output-shape.md` fixes for every mode.
|
|
22
|
-
|
|
23
|
-
The root is the argument rather than the toolkit, matching the reach and drift verbs, so a linked worktree reads its own branch and a target reads its own file.
|
|
24
|
-
|
|
25
|
-
## What it reads
|
|
26
|
-
|
|
27
|
-
Every H2 and H3 owning at least one top-level bullet. An H3 is reported under the H2 containing it, as `Behavior / Scope discipline`, so a section and its subsections are counted apart rather than summed.
|
|
28
|
-
|
|
29
|
-
Three things are read past. A nested bullet belongs to the one above it rather than to the section. A bullet inside a fenced block is example text rather than instruction. A heading carrying no bullet answers nothing the report asks, so it is dropped rather than listed at zero.
|
|
30
|
-
|
|
31
|
-
## What counts as naming a path
|
|
32
|
-
|
|
33
|
-
A bullet is path-scoped here when it names a path. That is evidence for the tier judgment rather than the judgment itself, and the gap runs both ways: a bullet naming a folder can still apply every session, and one firing on a path it never spells is invisible to the count. Read a section's number as a place to look rather than as a verdict on it.
|
|
34
|
-
|
|
35
|
-
A backticked token counts when it carries a separator or an alphabetic extension, which admits `src/cli.ts` and `tsconfig.json` while leaving a flag, a bare word, and a version string out.
|
|
36
|
-
|
|
37
|
-
A shape counts as the folder above its placeholder. `canon/context/<domain>.md` names `canon/context/` and nothing narrower, so dropping the token whole would report the section carrying it as naming no path at all. A placeholder opening the first segment has no openable prefix and is dropped.
|
|
38
|
-
|
|
39
|
-
## When a rule counts as covering it
|
|
40
|
-
|
|
41
|
-
A rule covers a named path only when its glob anchors to a location. A glob opening `**` reaches every folder in the tree, so it answers that a file type is governed and never that a named path is.
|
|
42
|
-
|
|
43
|
-
The corpus-wide markdown rules are what force that. Counting one reports every markdown path as covered, which collapses the column to a constant and tells a reader nothing about which folder somebody actually scoped a rule to.
|
|
44
|
-
|
|
45
|
-
A folder is probed with a handful of extensions rather than matched literally, because a glob narrowed by file type reaches under a folder without ever matching the folder's own name. Globs are read from each rule's frontmatter block alone, so a rule quoting a path in its body does not register a scope it never declared.
|
|
46
|
-
|
|
47
|
-
The paths a section names that no rule reaches are listed beside it, which is where a reader looks first when deciding what a new rule would cover.
|
|
48
|
-
|
|
49
|
-
## Refusals
|
|
50
|
-
|
|
51
|
-
Two, each naming what a reader does about it. `no-claude-md` is a tree with no always-loaded file. `no-rules` is a tree carrying no path-scoped rule under `.claude/rules/`, where every path would report as uncovered and the column would say nothing.
|
|
52
|
-
|
|
53
|
-
An always-on rule declaring no `paths` is skipped rather than refused. It applies at the same priority as the always-loaded file, so it covers no path in particular.
|
|
54
|
-
|
|
55
|
-
## Exit codes
|
|
56
|
-
|
|
57
|
-
Exit codes are `0` when the file was read and `1` for a refusal. No finding moves the exit code.
|
|
58
|
-
|
|
59
|
-
Nothing wires this into `bun run check` or into a hook. Whether a bullet belongs in a rule is a judgment, and gating a measure that counts a judgment forces an escape hatch for every deliberate case.
|
|
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 JSON record's `sections` array rather than the exit when a skill consumes this.
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Rule citations
|
|
3
|
-
description: Resolving every path a governance rule cites and every frontmatter glob the internal corpus declares, the one form 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
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Rule citations
|
|
7
|
-
|
|
8
|
-
`canon gov citations` resolves every path a rule cites and every frontmatter glob the internal corpus declares, naming the ones reaching nothing. It answers a failure no other stage sees: a rule points a reader at a file, the file moves, and nothing reports it until a session opens the path and finds an absence.
|
|
9
|
-
|
|
10
|
-
A glob fails the same way and more quietly. A rule scoped at a directory that moved stops matching, so it never loads again, and a rule that never fires looks exactly like a rule nobody violated.
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
canon gov citations
|
|
14
|
-
canon gov citations --json
|
|
15
|
-
canon gov citations --root ../my-app
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
| Option | Behavior |
|
|
19
|
-
| --------------- | ---------------------------------------------------------- |
|
|
20
|
-
| `--root <path>` | Tree to read, defaulting to the current directory |
|
|
21
|
-
| `--json` | Add a machine-readable record on stdout, keeping the frame |
|
|
22
|
-
|
|
23
|
-
Under `--json` the record holds stdout alone and the frame still renders on stderr, refusals included, which is the split `output-shape.md` fixes for every mode.
|
|
24
|
-
|
|
25
|
-
## What the drift check cannot see
|
|
26
|
-
|
|
27
|
-
`bun run check` asserts that an authored rule and its consumed copy agree, and a dead citation passes that assertion because both copies carry it. The teach rule told its reader to open a `references/glossary.md` inside the teach skill that had never existed at that path. The rule shipped, survived a release, and fired during a real teach run before anyone noticed.
|
|
28
|
-
|
|
29
|
-
Two files agreeing is what a drift check measures. Whether either one is right about the tree is a different question, and this is the stage that asks it.
|
|
30
|
-
|
|
31
|
-
## The one form
|
|
32
|
-
|
|
33
|
-
A citation reaches a reader as a backticked path, reported under the form `path`, and it resolves against the root. A bare filename is never a citation, since with no folder around it nothing tells a rule from a convention such as `index.md`. A rule pointing at another rule writes its repository path.
|
|
34
|
-
|
|
35
|
-
A call to the retired standards verb is no form here. A rule holding one fails the shipped-references stage of `canon gate run`, which reads every shipped file for it.
|
|
36
|
-
|
|
37
|
-
A `path` is anchored on the whole backticked span rather than on a trailing pattern inside it. Cutting `tooling-reference.md` out of `internal/standards/tooling-reference.md` and resolving that against the root alone reports a file that exists as missing, which is a mistake made by hand while measuring this corpus before the stage was written.
|
|
38
|
-
|
|
39
|
-
## What is not a citation
|
|
40
|
-
|
|
41
|
-
A rule body is full of backticked spans carrying a slash, and almost none of them names a file in this tree. The stage declines four shapes outright.
|
|
42
|
-
|
|
43
|
-
- A placeholder or glob segment describes a shape rather than naming a file: `canon/context/<domain>.md`, `app/**/route.ts`, `${CLAUDE_SKILL_DIR}/../references/<name>.md`.
|
|
44
|
-
- A bare filename names a convention: `route.ts`, `manifest.toml`, `components.json`, `playwright.config.ts`.
|
|
45
|
-
- A span carrying no file extension is a folder or a module specifier: `src/pages/`, `next/font`, `try/except`, `react-hooks/set-state-in-effect`, `oven-sh/setup-bun@v2`, `@/lib/utils`.
|
|
46
|
-
- A fenced block displays a path rather than pointing at one.
|
|
47
|
-
|
|
48
|
-
The extension test is what carries most of the separation, and it costs the real paths that name a folder or a symlink, which this stage declines to check because nothing in the span says it is a file. Declining is the right direction for a gate, where a guess that goes wrong fails a push over prose.
|
|
49
|
-
|
|
50
|
-
## Where an absent path is correct
|
|
51
|
-
|
|
52
|
-
Two classes resolve to nothing and are right to. Both are reported by name rather than dropped, so a reader can see what the verdict declined to judge.
|
|
53
|
-
|
|
54
|
-
**Governed.** A rule spelling a path in its own frontmatter `paths:` is naming an artifact a target holds rather than a file here, so the file is correctly absent from this tree and correctly named in the rule. No shipped rule declares one today, and the stage's test fixtures carry the instance that keeps the class exercised.
|
|
55
|
-
|
|
56
|
-
Only an exact declaration exempts, never a glob match against one. A glob declares a shape, so a body path sitting inside it is still a citation and a stale one is still a defect. A rule scoped at `docs/**` citing a `docs/agents/renamed.md` that moved is exactly the class this stage exists to catch, and matching the glob would excuse it.
|
|
57
|
-
|
|
58
|
-
**Ignored.** A path git ignores is session scratch no clone is expected to hold. `governance/rules/canon/tasks.md` cites `.canon/tasks/index.md`, which is real in a live project and absent from a fresh clone and from every linked worktree. Resolving against the filesystem alone would make the verdict depend on which tree the stage ran in, so the unresolved paths go to one batched `git check-ignore` and an ignored one is excused. A read git cannot answer refuses rather than reporting those paths dead.
|
|
59
|
-
|
|
60
|
-
## Why the glob half reads one corpus
|
|
61
|
-
|
|
62
|
-
Bodies are read across both corpora and frontmatter globs across `internal/rules/` alone.
|
|
63
|
-
|
|
64
|
-
A rule under `governance/rules/` installs into a target, and its `paths:` entries name the shape that project holds rather than anything here. Measured over that corpus, 32 of its 72 globs match nothing in this tree and every one of them is correct. `src/pages/**` in the Astro rule cannot be told by pattern from a path this repository might hold, so a check cannot separate the two, and gating them would ship an exemption list the length of the corpus. Nothing reads the shipped globs, and the measurement is the reason rather than a gap left for someone to close.
|
|
65
|
-
|
|
66
|
-
`internal/rules/` ships nowhere. The tree it governs is the tree present, which makes the question answerable, and all 14 of its globs across 7 rules match at the commit this shipped on.
|
|
67
|
-
|
|
68
|
-
What that leaves unreached is a glob that matches real files and still reaches none of the work it was scoped at. `governance/rules/lib/e2e-reliability.md` scoped itself at `e2e/*.ts` and `e2e/**/*.ts` and now reads `**/e2e/**/*.ts`. No probe in this repository is written under `e2e/`, so the rule asking a session to watch a new guard fail never fired for the session writing guards. Resolution is mechanical and reach is a judgment about where the work happens, so only the first is here.
|
|
69
|
-
|
|
70
|
-
## The exemption marker
|
|
71
|
-
|
|
72
|
-
Everything the classifier can separate mechanically is separated there. For the residue, a line carries `canon-allow-citation: <reason>` on itself or the one directly above, which moves it into the report's `Exempt` section. Only a marker naming a reason counts, since a bare token is a line that meant to say something and did not. This is the `canon-allow-superseded` shape, and both read the same placement rule through one helper.
|
|
73
|
-
|
|
74
|
-
## The blind spots
|
|
75
|
-
|
|
76
|
-
A citation that resolves and points at the wrong file passes. Resolution is mechanical and correctness is a reading, and only the first is a gate's business.
|
|
77
|
-
|
|
78
|
-
A path written into running prose without backticks is not read at all. Matching one would report every sentence that happens to name a file, and the backticks are what separate a reference a reader follows from a name in a sentence.
|
|
79
|
-
|
|
80
|
-
A folder or a path carrying no extension is declined rather than guessed at, which is stated above as the cost of the test that carries the separation.
|
|
81
|
-
|
|
82
|
-
A glob under `governance/rules/` is not read, and a glob that resolves while reaching none of the work it was scoped at is a reading rather than a resolution. Both are stated above.
|
|
83
|
-
|
|
84
|
-
## Exit codes
|
|
85
|
-
|
|
86
|
-
Exit codes are `0` when every citation resolves or is excused and every glob read matches, `1` for a refusal, and `2` for at least one cited path reaching nothing or one glob matching nothing. It refuses a tree holding neither rule corpus, since a tree with no rules passes each of its zero rules, and a `git check-ignore` read that fails.
|
|
87
|
-
|
|
88
|
-
This gates, and `bun run check` runs it as the `Rule citations` stage. The sibling sweeps report rather than gate because a value appears for reasons unrelated to the convention, so their output is a reading. A path resolving to nothing carries no judgment: either the file is there or the citation is stale, and the two classes where absence is correct are separated before the verdict rather than left for a reader to settle. The corpus is clean at the commit this shipped on, so the gate starts green and stays that way until something breaks.
|
|
89
|
-
|
|
90
|
-
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 JSON record's `citations` array and filter on `status` rather than the exit when a skill consumes this.
|
package/docs/agents/sandbox.md
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Sandbox
|
|
3
|
-
description: Scenario routing, the expectation scoring surface, and the coverage census over scenarios and skills
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Sandbox
|
|
7
|
-
|
|
8
|
-
`canon sandbox` provisions isolated project states, scores a provisioned one against a declared expectation, and reports which scenarios declare an expectation at all.
|
|
9
|
-
|
|
10
|
-
## Scenarios
|
|
11
|
-
|
|
12
|
-
Scenarios live under `sandbox/`, one folder per category and one file per command, each a TypeScript module. `sandbox/fixtures/` is the exception, holding file content that scenarios stage rather than scenarios of its own, so the picker and the coverage report both filter it out. That tree sits outside `files` in `package.json`, so an installed `canon` carries the command, reports it as toolkit-only on one line, and exits 1 rather than failing on the missing directory. Route non-interactively with `SANDBOX_SCENARIO`:
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
SANDBOX_SCENARIO=sync canon sandbox infra:tooling
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Scenario categories: `infra:*` (domain flows), `git:*`, `scaffold:*`. `create` scenarios require interactive input and loop on empty input, so skip them in automated runs.
|
|
19
|
-
|
|
20
|
-
## Scenario expectations
|
|
21
|
-
|
|
22
|
-
`canon sandbox check <category>:<command> [arm]` scores a provisioned sandbox against the arm's `expect.toml`, printing a verdict on stderr and, with `--json`, the same verdict as a record on stdout.
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
canon sandbox check claude:context-fold drift --json
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
| Flag | Effect |
|
|
29
|
-
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
|
30
|
-
| `--envelope <file>` | Read `is_error`, `num_turns`, denials, and the reply text |
|
|
31
|
-
| `--writes <file>` | Newline-delimited paths the session wrote, for write scope |
|
|
32
|
-
| `--escapes <file>` | Newline-delimited paths written to a watched toolkit root, for escape scope |
|
|
33
|
-
| `--escapes-watched` | At least one watched root held a target this run |
|
|
34
|
-
| `--concurrent-sessions <file>` | Newline-delimited sessions live in the registry both before and after this run, a witness for an unbounded escape |
|
|
35
|
-
| `--json` | Emit the verdict record on stdout |
|
|
36
|
-
| `--strict` | Exit 1 on `unchecked` instead of 0 |
|
|
37
|
-
|
|
38
|
-
The verdict `state` is `pass`, `fail`, or `unchecked`. An arm with no `expect.toml` is `unchecked` and exits 0, so the harness stays usable while expectations roll out. A declaration that exists but asserts nothing is a failure, since an expectation file that asserts nothing passes every run.
|
|
39
|
-
|
|
40
|
-
Omitting `--writes`, `--escapes`, or `--envelope` does not silently drop the assertion kinds that need them. Write scope, escape scope, the turn ceiling, and the reply assertion report as unchecked and appear in the count, so the standalone command cannot claim more coverage than it had. Supplying `--escapes` without `--escapes-watched` reports the same way: a zero-escape result with no root confirmed watched is unmeasured rather than a pass. A verdict never reports `pass` with zero assertions.
|
|
41
|
-
|
|
42
|
-
An arm declaring `escape_scope` asserts a bound on the headless runner's own escape watch rather than on the sandbox tree. `write_scope` skips when the run wrote nothing, since a required output missing is itself a finding, but `escape_scope` passes on zero escapes outright, since a clean run producing none is the expected outcome for a destination nothing requires a skill to touch, provided `--escapes-watched` confirms a root held something to watch. Declaring `escape_scope = []` asserts that a correct run reaches none of the watched destinations at all. `canon/context/sandbox/isolation.md` names what the watch reaches and what it cannot.
|
|
43
|
-
|
|
44
|
-
An envelope that parses but carries no `result` field skips the reply assertion the same way an absent file does. An envelope carrying an empty `result` fails it, since a run that returned no text is a finding rather than a gap in the input.
|
|
45
|
-
|
|
46
|
-
Exit 0 means `pass` or `unchecked`. Exit 1 means `fail`, or a caller error: a malformed target, or a sandbox that was never provisioned. A missing sandbox reports as an error rather than a failed verdict, because failing every path assertion would read as a skill that did nothing. `--strict` moves `unchecked` to exit 1 for a caller that has finished arming its scenarios.
|
|
47
|
-
|
|
48
|
-
## Headless runs
|
|
49
|
-
|
|
50
|
-
`canon sandbox run` provisions a scenario, drives one `claude -p` session over it, and scores the result with `canon sandbox check`.
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
canon sandbox run <category>:<command> "<prompt>" [arm]
|
|
54
|
-
canon sandbox run claude:plan-feature "/canon:plan-feature add a widget" small
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Every run bills a real session through the `claude` binary on `PATH`. The session runs with `--plugin-dir` pointed at this checkout's `claude/`, so it loads the branch's skills rather than an installed copy, and provisioning injects no skill into the tree for the same reason. Four variables override the flags it hands to `claude -p`:
|
|
58
|
-
|
|
59
|
-
| Variable | Default |
|
|
60
|
-
| ---------------------------------- | -------------------------------- |
|
|
61
|
-
| `CANON_SKILL_TEST_MODEL` | `sonnet` |
|
|
62
|
-
| `CANON_SKILL_TEST_TOOLS` | `Bash,Read,Glob,Grep,Edit,Write` |
|
|
63
|
-
| `CANON_SKILL_TEST_MAX_TURNS` | `30` |
|
|
64
|
-
| `CANON_SKILL_TEST_PERMISSION_MODE` | `bypassPermissions` |
|
|
65
|
-
|
|
66
|
-
The max-turns value is the only budget a run has. An arm's `max_turns` is asserted after the run and cannot raise it.
|
|
67
|
-
|
|
68
|
-
Stdout carries one JSON record: the session's envelope with `verdict`, `escapes`, and `sessions` merged in, described under the skills census below. Stderr carries the framed timeline, the provisioning output, the session's own stderr, the verdict report, and a warning for each escape, new session record, or reaped process. An envelope that does not parse still prints its verdict, under `is_error: true`.
|
|
69
|
-
|
|
70
|
-
Exit codes follow whichever step stopped the run. Provisioning's code ends a run before the session starts, and a session that exits non-zero ends it with that code after writing a dead-run record, `{is_error, exit_code, raw_output}`, to the same runs folder the merged record uses. Otherwise the verdict's code stands, except that an envelope that does not parse forces 1. An interrupt reaps the session's process group before the runner exits.
|
|
71
|
-
|
|
72
|
-
## Provisioning equivalence
|
|
73
|
-
|
|
74
|
-
`canon sandbox equivalence` proves a change moved nothing in what scenarios provision. It provisions every arm of the named targets on a base ref and on the working checkout, builds a manifest of each tree, and names what differs. It runs offline and never calls `canon sandbox run` or a `claude` binary, so no arm it runs bills a model.
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
canon sandbox equivalence claude --base origin/main --json
|
|
78
|
-
canon sandbox equivalence claude:plan-feature/full --base HEAD
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
A target is `<category>`, `<category>:<command>`, or `<category>:<command>/<arm>`, defaulting to every category.
|
|
82
|
-
|
|
83
|
-
| Flag | Effect |
|
|
84
|
-
| ------------------ | -------------------------------------------------------------------------------------------------- |
|
|
85
|
-
| `--base <ref>` | Ref the baseline is provisioned from, `main` by default |
|
|
86
|
-
| `--include-anchor` | Also run the arms that push to the shared GitHub anchor, one at a time, for an attended run |
|
|
87
|
-
| `--stub-remote` | Run the anchor arms against a local bare repository and a stub `gh`, writing nothing to the anchor |
|
|
88
|
-
| `--out <dir>` | Keep the provisioned trees, logs, and per-side manifests in this folder |
|
|
89
|
-
| `--no-masks` | Compare every path, which lists what each mask would hide |
|
|
90
|
-
| `--json` | Emit the record on stdout |
|
|
91
|
-
|
|
92
|
-
The baseline is a detached worktree with full history, removed on exit including after `SIGINT`. Each arm provisions on the base side, is read into a manifest, is deleted, then provisions on the head side at the same path with the run id pinned. A manifest holds every file's mode and hash, every symlink's target, the exit status, the narration, and for the outer repository and each nested one the commit subjects, the staged index, and the branch.
|
|
93
|
-
|
|
94
|
-
The record lists per arm `identical`, `differs` with the manifest keys that differ, `red-on-base` when both sides fail the same way, or `skipped-anchor`. It also carries `canonVersion`, `baseCommit`, `masksApplied`, `errors`, and `counts`. A declared arm adds a `check` entry from `canon sandbox check` on the head tree, whose `asserted` count shows its `expect.toml` resolved from `sandbox/fixtures/`.
|
|
95
|
-
|
|
96
|
-
Exit 0 means every compared arm is identical or red the same way on both sides. Exit 1 means any `differs`, a base and head exit that disagree, an armed arm that `canon sandbox coverage` lists and enumeration lacks, or a `.ts` scenario that fails to load.
|
|
97
|
-
|
|
98
|
-
## Scenario coverage
|
|
99
|
-
|
|
100
|
-
`canon sandbox coverage` reports which scenarios declare expectations and which only provision a state. It reads the fixture tree, so it needs no provisioned sandbox and runs nothing. Where that tree does not ship it exits 1 and prints no percentage, since a denominator nobody looked at is not a coverage result. A tree that is present and holds no scenarios is a real zero and still reports one.
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
canon sandbox coverage --json
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
| Flag | Effect |
|
|
107
|
-
| ---------- | ----------------------------------------------------------- |
|
|
108
|
-
| `--json` | Emit the coverage record on stdout |
|
|
109
|
-
| `--strict` | Exit 1 while any scenario declares no expectation |
|
|
110
|
-
| `--skills` | Add a per-skill asserted, should-be-asserted, exempt census |
|
|
111
|
-
|
|
112
|
-
The record carries every scenario with the arms that declare, plus `totalScenarios`, `armedScenarios`, and `armedArms`. Scenarios and arms count separately, since several arms can share one scenario and dividing one by the other overstates the rollout.
|
|
113
|
-
|
|
114
|
-
### The skills census
|
|
115
|
-
|
|
116
|
-
`--skills` answers what the scenario count cannot, which is whether anything can fail a given skill. It adds `skills`, `totalSkills`, `asserted`, `shouldBeAsserted`, `exempt`, `staleExemptions`, and `supersededExemptions` to the record, and keeps the scenario view rather than replacing it. The two denominators disagree on purpose: an armed scenario under `infra/` or `tooling/` exercises a CLI domain and pairs with no skill at all.
|
|
117
|
-
|
|
118
|
-
A skill pairs to a scenario by filename, `<category>-<command>` first and bare `<command>` second, so `claude/target-setup.ts` reaches the `target-setup` skill. `should-be-asserted` is the default rather than a queue to drain, and which of those skills earns an arm is a project decision the census does not make.
|
|
119
|
-
|
|
120
|
-
`exempt` means no arm should be written and holds only with a reason, declared in `sandbox/exempt.toml` and limited to a harness limit the checker cannot reach past or a skill that writes no artifact. An armed arm outranks an exemption. An exemption naming no shipped skill, or naming one an arm now asserts, exits 1 without `--strict`. Each armed arm reports as `<category>:<command>/<arm>`, so two same-named arms under different scenarios stay distinct.
|
|
121
|
-
|
|
122
|
-
`canon sandbox run` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.canon/tmp/runs/sandbox/<target>-<arm>-<timestamp>.json` with a `writes` array appended, and logs the path on stderr. Both fields are what a later re-score needs, since `--envelope` and `--writes` read files the run deletes on exit.
|
|
123
|
-
|
|
124
|
-
Two more fields ride alongside the verdict rather than inside it. `escapes` lists what the run wrote under a watched toolkit root, which the verdict cannot assert over because those files sit outside the sandbox tree. `sessions` reports the nested-dispatch bound, carrying `watched` for whether the client's session registry was there to read, `new` for the records that appeared while the run was in flight, `concurrent` for the records present both before and after, and `reap` for what the run found in the session's process group afterwards. Neither field fails a run on its own.
|
|
125
|
-
|
|
126
|
-
`canon sandbox run` passes `concurrent` through `--concurrent-sessions` to `sandbox check`, and `checkEscapeScope` appends a witness count to an `unbounded escape:` message rather than lets it soften the verdict.
|
package/docs/agents/scripting.md
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Scripting
|
|
3
|
-
description: The runtime catalogs that replace hardcoded names, what each carries, and a headless invocation per domain
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Scripting
|
|
7
|
-
|
|
8
|
-
What a skill or script reads to discover names at runtime, and how each domain is invoked with no TTY.
|
|
9
|
-
|
|
10
|
-
## Runtime catalogs
|
|
11
|
-
|
|
12
|
-
Use these to discover what's available instead of hardcoding names.
|
|
13
|
-
|
|
14
|
-
| Command | Returns |
|
|
15
|
-
| --------------------------------- | ---------------------------------------------------------------- |
|
|
16
|
-
| `canon tooling list --json` | Stacks, extends chain, dep and script counts |
|
|
17
|
-
| `canon gov list --json` | Governance stacks, rule sets, evidence, and unreferenced rules |
|
|
18
|
-
| `canon gov detect --json` | Opt-in rules a target's dependencies and paths propose |
|
|
19
|
-
| `canon claude seeds list --json` | Seed doc sources with content |
|
|
20
|
-
| `canon claude skills list --json` | Plugin skills, descriptions, requirement flag, family vocabulary |
|
|
21
|
-
| `canon docs list --json` | Consumer docs plus per-domain context |
|
|
22
|
-
| `canon audits list --json` | Audits the set runs, the corpus each reads, and whether it gates |
|
|
23
|
-
|
|
24
|
-
### Catalog fields
|
|
25
|
-
|
|
26
|
-
Every catalog serializes through `JSON.stringify`, so a name carrying a quote
|
|
27
|
-
emits valid JSON. `canon tooling list` previously built
|
|
28
|
-
its output with `printf` and no escaping.
|
|
29
|
-
|
|
30
|
-
`canon claude seeds list` reads the same plan `canon claude init` applies, so the
|
|
31
|
-
listing and the install cannot disagree. It now reports
|
|
32
|
-
`canon/context/index.md`, which `init` has always installed and the listing
|
|
33
|
-
never named, and it emits the project-level `CLAUDE.md` last rather than first.
|
|
34
|
-
|
|
35
|
-
`canon gov list` reports each stack's rules already expanded. A stack entry names
|
|
36
|
-
either a rule or a whole rule folder under `governance/rules/`, and the folder
|
|
37
|
-
form reaches this listing as the rules it stands for rather than as the folder
|
|
38
|
-
name. A consumer deduping `--add` extras against a stack therefore reads the same
|
|
39
|
-
set the install writes, whichever form the stack file uses.
|
|
40
|
-
|
|
41
|
-
It also carries `unreferenced`, the rules no stack reaches, on every invocation
|
|
42
|
-
rather than behind a flag. These install only through `--add`, so the key answers
|
|
43
|
-
what a stack leaves out without a caller resolving every stack itself. The key is
|
|
44
|
-
present alongside `stacks` and `rules` and survives `--stacks` or `--rules`
|
|
45
|
-
narrowing the other two.
|
|
46
|
-
|
|
47
|
-
### The skills catalog
|
|
48
|
-
|
|
49
|
-
`canon claude skills list` reads `claude/skills/*/SKILL.md` and reports the folder
|
|
50
|
-
name with the frontmatter description, sorted by name. Internal skills under
|
|
51
|
-
`.claude/skills/` are excluded, since they never install into a target and a
|
|
52
|
-
count spanning both overstates what ships. A skill whose frontmatter is missing
|
|
53
|
-
or unparseable returns an empty description rather than failing the listing, so
|
|
54
|
-
one malformed file cannot hide the rest of the catalog. `--names` emits skill
|
|
55
|
-
names one per line.
|
|
56
|
-
|
|
57
|
-
Each skill in `--json` carries `family`, the `metadata.family` its frontmatter
|
|
58
|
-
declares, or `null` where it declares none. A top-level `families` array holds
|
|
59
|
-
the vocabulary as `{ key, group }` entries in skill map order, where `group` is
|
|
60
|
-
the map heading the family's rows sit under. Read a new skill's family from it
|
|
61
|
-
rather than from the map. `--by-family` prints the human listing as one step per
|
|
62
|
-
family, with a trailing step for any skill whose family is missing or unknown.
|
|
63
|
-
|
|
64
|
-
Each entry also carries `requirement`, whether the folder holds a sibling
|
|
65
|
-
`REQUIREMENT.md`. Every skill is meant to carry one, so a `false` is a gap to
|
|
66
|
-
close rather than a recorded exemption, and the flag answers which skills are
|
|
67
|
-
missing theirs without a caller listing the directory itself. `canon claude skills
|
|
68
|
-
audit` gates that rule across both corpora, so read this flag for the state of
|
|
69
|
-
the shipped catalog and the audit for whether a working tree conforms. Its
|
|
70
|
-
surface is in `skills-audit.md`.
|
|
71
|
-
|
|
72
|
-
## Non-interactive examples
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
# Report what a stack would change, writing nothing
|
|
76
|
-
CANON_NON_INTERACTIVE=1 canon tooling diff astro /path/to/project
|
|
77
|
-
|
|
78
|
-
# Sync a stack into a target project, overwriting its golden configs
|
|
79
|
-
CANON_NON_INTERACTIVE=1 canon tooling sync astro /path/to/project --write
|
|
80
|
-
|
|
81
|
-
# Install a governance stack (the stack argument is required headlessly)
|
|
82
|
-
CANON_NON_INTERACTIVE=1 canon gov install node --add astro,shadcn /path/to/project
|
|
83
|
-
|
|
84
|
-
# Update installed governance rules, dropping a retired .claude/GOV.md
|
|
85
|
-
CANON_NON_INTERACTIVE=1 canon gov sync /path/to/project
|
|
86
|
-
|
|
87
|
-
# Concatenate installed rules into a paste payload
|
|
88
|
-
CANON_NON_INTERACTIVE=1 canon gov build /path/to/project
|
|
89
|
-
|
|
90
|
-
# Sync a monorepo subtree, skipping the base layer the repo root already owns
|
|
91
|
-
CANON_NON_INTERACTIVE=1 canon tooling sync vite-react /path/to/repo/frontend --skip base --write
|
|
92
|
-
|
|
93
|
-
# Verify a stack end-to-end in a throwaway scaffold
|
|
94
|
-
canon tooling verify vite-react
|
|
95
|
-
|
|
96
|
-
# Apply one stack without scanning or prompting, for scripted provisioning
|
|
97
|
-
canon tooling inject base /path/to/project
|
|
98
|
-
canon tooling inject base /path/to/project --configs --seeds
|
|
99
|
-
|
|
100
|
-
# Drop managed gitignore entries a manifest no longer declares
|
|
101
|
-
# Prints the number removed on stdout, diagnostics on stderr
|
|
102
|
-
canon tooling prune-gitignore base /path/to/project
|
|
103
|
-
|
|
104
|
-
# Bootstrap a project. Any flag suppresses the confirmation prompt
|
|
105
|
-
CANON_NON_INTERACTIVE=1 canon init --stack node --skip wiki /path/to/project
|
|
106
|
-
|
|
107
|
-
# Run every domain sync. The git workflow is refused headlessly, so nothing is pushed
|
|
108
|
-
CANON_NON_INTERACTIVE=1 canon sync /path/to/project
|
|
109
|
-
|
|
110
|
-
# Scaffold .claude/wiki/ with a stub index. The target must already exist
|
|
111
|
-
CANON_NON_INTERACTIVE=1 canon wiki init /path/to/project
|
|
112
|
-
|
|
113
|
-
# Run a sandbox scenario non-interactively
|
|
114
|
-
SANDBOX_SCENARIO=sync canon sandbox infra:tooling
|
|
115
|
-
|
|
116
|
-
# Read every audit as one record. Exit 2 is a fact, 3 an audit that did not report
|
|
117
|
-
canon audits run --json
|
|
118
|
-
|
|
119
|
-
# Read committed state rather than an arriving change. Exit 2 carries findings
|
|
120
|
-
canon secrets scan --json
|
|
121
|
-
canon deps audit --json
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
The two state-scoped verbs both exit 2 on findings and mean different things by
|
|
125
|
-
it. A credential in the shipped tree is a fact and fails `canon audits run`, while
|
|
126
|
-
a published advisory is a judgment that reports and moves no verdict. Neither
|
|
127
|
-
reads a diff, so a consumer already running a review surface gets no overlap.
|
|
128
|
-
|
|
129
|
-
Both refuse rather than report clean when they have no corpus, and the refusal
|
|
130
|
-
reason is the field to branch on. A project publishing nothing refuses the secret
|
|
131
|
-
scan with `no-manifest`, and one whose dependencies are not installed refuses the
|
|
132
|
-
advisory check with `no-lockfile`. The aggregate reads those as an absent corpus
|
|
133
|
-
rather than a broken run, so neither pins its verdict at `incomplete`.
|
|
134
|
-
|
|
135
|
-
`canon tooling sync` is the one verb above whose flag is mandatory headlessly. It
|
|
136
|
-
overwrites every golden config a stack ships, which reaches the CI workflow, the
|
|
137
|
-
git hooks, the end-to-end harness, and the shell scripts under `scripts/`, so a
|
|
138
|
-
headless run carrying neither `--check` nor `--write` reports what it would
|
|
139
|
-
replace and exits 1 rather than applying it. Run `--check` first to read the
|
|
140
|
-
list, then `--write` to apply it. `canon tooling sync --help` names both, and
|
|
141
|
-
`canon tooling diff <stack> <target>` reports the per-stack path list resolved
|
|
142
|
-
against a real target.
|
package/docs/agents/sessions.md
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Sessions
|
|
3
|
-
description: 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
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Sessions
|
|
7
|
-
|
|
8
|
-
## List
|
|
9
|
-
|
|
10
|
-
`canon sessions list` reports every live Claude Code session on the machine with the working directory and branch it holds.
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
canon sessions list
|
|
14
|
-
canon sessions list --json
|
|
15
|
-
canon sessions list --branch feat/parser --json
|
|
16
|
-
canon sessions list --branch chore/agents --repository ../caret --json
|
|
17
|
-
canon sessions list --self --json
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
| Option | Behavior |
|
|
21
|
-
| --------------------- | ----------------------------------------------------- |
|
|
22
|
-
| `--json` | Add a machine-readable record on stdout |
|
|
23
|
-
| `--branch <name>` | Report the sessions holding this branch |
|
|
24
|
-
| `--repository <path>` | Answer about this project rather than the working one |
|
|
25
|
-
| `--self` | Report the caller's own row |
|
|
26
|
-
|
|
27
|
-
It reads and never writes. The question it answers is which session to address when work has to reach the one holding a given branch, which a session listing cannot answer on its own.
|
|
28
|
-
|
|
29
|
-
Exit codes: `0` the roster was read, `1` refused. The refusal carries a `reason` of `no-registry`, `no-repository`, `no-self-identity`, or `no-self-row`.
|
|
30
|
-
|
|
31
|
-
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 `reason` rather than the exit when a skill consumes this.
|
|
32
|
-
|
|
33
|
-
## Scope and count
|
|
34
|
-
|
|
35
|
-
`--branch` scopes the match to one repository, and refuses when none resolves. A branch name identifies a branch inside a repository and nothing across a machine, so an unscoped match reaches a session working in a different project, and `main` collides on every machine running two of them.
|
|
36
|
-
|
|
37
|
-
Which repository that is defaults to the one the command runs in, and `--repository <path>` names another. Every reading moves with it, the session match and the worktree and ref reads alike, because the answer is about that project rather than about where the caller stands. The roster underneath is machine-wide already, so the flag removes a filter rather than widening a search.
|
|
38
|
-
|
|
39
|
-
That is what a dispatcher reaching outside its own project needs. Without it a check run from the toolkit against a branch held by a live session in a consuming project answered unclaimed, and two sessions were sent onto branches other sessions were holding. One refused on the worktree lock and one cut a second worktree on the same branch, which would have put two sessions pushing to one ref.
|
|
40
|
-
|
|
41
|
-
A bare run reports every repository and carries a `repository` field on each row, holding the shared git directory that a main checkout and all its linked worktrees agree on. That is what a caller filters on when it wants a scope of its own.
|
|
42
|
-
|
|
43
|
-
The match can return more than one session. Read the count rather than the first row, since nothing stops two sessions holding one branch, and a caller that treats the result as singular picks among candidates without knowing it.
|
|
44
|
-
|
|
45
|
-
Resolution reads a session's registered `cwd`, taken at whatever `locate()` finds there rather than wherever it has since worked. That registration updates whenever the harness enters a worktree: a worker dispatched onto `main` re-registers at its own branch once `auto-ship` Step 0 runs. What stays fixed is a session moved by shell command, `cd` or `git -C` against another root, with no worktree entry behind it, so it answers only for the branch its last registered `cwd` sits in.
|
|
46
|
-
|
|
47
|
-
A session whose `cwd` reflects a harness worktree entry, at launch or mid-session, resolves fully, covering every dispatched worker once Step 0 runs. A zero for a different branch it is genuinely working on is not evidence the branch is unclaimed, only that a shell-moved registration cannot see it.
|
|
48
|
-
|
|
49
|
-
## Whether a branch is already claimed
|
|
50
|
-
|
|
51
|
-
With `--branch`, the JSON record also carries `worktree` (the path of any worktree already checked out to it, or `null`), `refs` (the refs that already name the branch, the local head and the `origin` remote-tracking ref alike), and `claimed` (`true` when any of the three holds it).
|
|
52
|
-
|
|
53
|
-
Read `claimed` rather than composing the three fields by hand. A worktree can outlive the session that made it, and a session can hold a branch before any worktree exists for it. A branch sitting behind a merged pull request has neither and is taken all the same, which is the reading that was missing when a dispatcher cleared a merged branch and told a worker to build on it.
|
|
54
|
-
|
|
55
|
-
The ref read covers the local head and the remote-tracking ref, which means it sees the remote at whatever the last fetch left behind. A branch pushed from another machine since then reads absent. Closing that gap needs `git ls-remote`, and it is left open deliberately: the remote read costs 0.438s against 0.001s for the local one, on a check that runs before every dispatch.
|
|
56
|
-
|
|
57
|
-
`worktree`, `refs`, and `claimed` are `null` on a bare run with no `--branch`, since none of the three questions has a branch to answer about. A refusal (`no-registry` or `no-repository`) carries none of the keys at all, which a caller should read as unverified rather than as clear.
|
|
58
|
-
|
|
59
|
-
Two flags say which reading came up short. `sessionsReadable` is `false` when the session roster could not be read, and `refsReadable` is `false` when the ref read failed. Either one leaves `claimed` covering the readings around it alone, so a `false` there is a report that ran short of evidence rather than a report that the branch is clear. They stay separate fields because a caller told the roster failed goes and looks at the roster, and folding both into one flag would send it to the wrong place.
|
|
60
|
-
|
|
61
|
-
## Which row is the caller
|
|
62
|
-
|
|
63
|
-
`--self` narrows the report to the row belonging to the session making the call. A dispatcher reads it to learn the `sessionId` it carries into a launch, because the roster returns every field on every row and marks none of them as the caller.
|
|
64
|
-
|
|
65
|
-
Three identifier namespaces reach a session and two of them join to a row. Four variables spell them:
|
|
66
|
-
|
|
67
|
-
- `CLAUDE_CODE_SESSION_ID` holds the roster's own `sessionId`, and is read first because it survives a rename.
|
|
68
|
-
- `CLAUDE_PID` holds the caller's process id, which every row already carries as `pid`.
|
|
69
|
-
- `CLAUDE_CODE_MESSAGING_SOCKET` spells that same pid in its basename. It is read last, since the spelling is a client convention rather than a published interface and a client that moves the socket drops this rung alone.
|
|
70
|
-
- `CLAUDE_CODE_HOST_SESSION_ID` is never read. It carries a `local_`-prefixed value from the harness namespace that matches no row, and it is the variable a reader searching the environment for a session id finds first.
|
|
71
|
-
|
|
72
|
-
The read refuses rather than returning an empty roster, on the same ground as `no-registry`. A `no-self-identity` refusal means the environment stated none of the three, which is a client identifying its sessions some other way. A `no-self-row` refusal means it stated one and no live row carries it, which is what a session running outside a local process gets.
|
|
73
|
-
|
|
74
|
-
## What each surface can see
|
|
75
|
-
|
|
76
|
-
The roster this verb reads and the channel a session sends messages on enumerate different populations, and neither contains the other. Read at one moment, `canon sessions list` returned 6 rows against the 13 peers plus caller the agent listing reported. The roster held a background session at confirmed confidence on two reads a minute apart that the listing never carried, so a live session sat here with no way to reach it. Nine Remote Control sessions ran the other way, reachable there with no row here at all, since they leave no local process for the registry to record.
|
|
77
|
-
|
|
78
|
-
That bounds what a carried identifier buys. Resolving a `sessionId` to a name here can produce a name the send channel rejects, and `--self` answers nothing for a controller driving from Remote Control, which is the operator working from their phone rather than an edge case. A caller that cannot resolve an addressee reports the failed resolution instead of falling back to a guess.
|
|
79
|
-
|
|
80
|
-
The `name` field is what both surfaces agree on, and that agreement is what makes the resolution work. `SendMessage` takes a name and carries no identifier parameter, so the carry runs id to name to send rather than sending an id anywhere.
|
|
81
|
-
|
|
82
|
-
Agreement is not uniqueness, and the last hop is where that bites. Two live sessions can hold one name, the roster carries no field that separates them, and a send to the bare name reaches whichever the channel resolves first. The agent listing prints a ref beside each row and the roster does not, so a name resolving to more than one live row is completed from that listing rather than from anything this verb returns.
|
|
83
|
-
|
|
84
|
-
## Why the verb exists
|
|
85
|
-
|
|
86
|
-
A session listing reports a name, a kind, a status, and how long each session has been running. None of those names a branch. Resolving a branch to a session therefore meant ordering the roster by start time and matching it against the order the worktrees were created, which is an inference that fails whenever two sessions start inside the same minute.
|
|
87
|
-
|
|
88
|
-
Each session writes its own record on disk carrying its working directory beside its own name. This verb reads those records, so a name joins to a branch by an exact match on one file rather than by a guess across two orderings.
|
|
89
|
-
|
|
90
|
-
## What a row carries
|
|
91
|
-
|
|
92
|
-
Every row names the session, the process holding it, its working directory, and the branch checked out there. The status field repeats what the session listing reports, so a caller picking a target reads one output instead of two.
|
|
93
|
-
|
|
94
|
-
A field the record did not carry is reported as null rather than as a value, so an absent start time never reads as a session launched in 1970 and an absent identifier never reads as an empty one.
|
|
95
|
-
|
|
96
|
-
A row whose branch cannot be read is kept and marked rather than dropped. A caller has to be able to tell a session holding no branch from one the resolver never saw, and the second is the failure the verb replaces. The `unresolved` field carries the reason:
|
|
97
|
-
|
|
98
|
-
- `detached-head`: the session holds a worktree with no branch name
|
|
99
|
-
- `not-a-repository`: the session is working outside any git repository
|
|
100
|
-
- `git-unavailable`: git is not on the path, so nothing could be read
|
|
101
|
-
|
|
102
|
-
## The confidence field
|
|
103
|
-
|
|
104
|
-
Every report states how liveness was decided, on a pass as well as a failure.
|
|
105
|
-
|
|
106
|
-
`confirmed` means each row's process was matched against the start time its own record stamped at launch, so the process holding the pid is the session that wrote the file. A caller can address a row of that kind directly.
|
|
107
|
-
|
|
108
|
-
`unverified` means only that the pid answers a probe. That cannot separate the original session from an unrelated process that inherited its pid after the session ended, so the roster is a candidate list rather than an identity. Treat the mapping as inferred and open the message by naming the branch the reader is believed to hold, asking to be corrected.
|
|
109
|
-
|
|
110
|
-
The registry holds one record per session and is never pruned, so it accumulates thousands of entries. On the `unverified` path a stale record whose pid has been reused reads as live, which is why the field is reported rather than assumed.
|
|
111
|
-
|
|
112
|
-
## The status dwell
|
|
113
|
-
|
|
114
|
-
Every row carries `statusUpdatedAt`, the stamp a client writes beside `status` at the moment it last changed, and `statusDwellMs`, the elapsed milliseconds since that stamp. Measured over the live registry, 23 of 341 usable records carry `statusUpdatedAt`, so `null` is the ordinary answer rather than an edge case, and the absence tracks a client version rather than a record's age alone: the one record ever measured carrying `status: "waiting"` is among the 318 without it.
|
|
115
|
-
|
|
116
|
-
`statusDwellMs` falls back to the coarser `updatedAt` stamp when `statusUpdatedAt` is absent, so it is `null` only where a record carries neither. `statusUpdatedAt` itself is never backfilled from the fallback and stays `null` in that case, since it names the exact stamp rather than an estimate. A stamp ahead of the reading clock clamps the dwell to zero rather than reporting a negative one.
|
|
117
|
-
|
|
118
|
-
The dwell is what separates a status that resolves on its own from one that does not. `busy` and `idle` transition without help, so a long dwell there is ordinary. `waiting` does not: a session in that state is blocked on something outside itself, and a dwell that keeps growing past the ordinary span of a prompt is a session stalled rather than paused. `canon sessions list` renders the dwell beside the status at the coarsest unit that keeps it a whole number, and the JSON record carries both fields on every row.
|
|
119
|
-
|
|
120
|
-
## Export and import
|
|
121
|
-
|
|
122
|
-
`canon sessions export <id>` packs one session into a bundle and `canon sessions import <bundle>` places it on another machine. Both are in `sessions-transfer.md`.
|
|
123
|
-
|
|
124
|
-
## What the read depends on
|
|
125
|
-
|
|
126
|
-
The records live under the Claude Code configuration directory, which the verb resolves from `CLAUDE_CONFIG_DIR` and falls back to `~/.claude`. Their location, their filenames, and the fields inside them are a client implementation detail rather than a published interface, so a client change can move them. The verb reports an absent registry as a refusal rather than as a machine running no sessions, which is what surfaces the move instead of burying it in an empty roster.
|
|
127
|
-
|
|
128
|
-
The start-time comparison reads the process filesystem and exists only on some platforms. Where it does not, the verb still answers and marks the confidence, so a target without it keeps a working roster rather than losing the command.
|