@erclx/canon 5.26.0 → 5.27.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/api-design/SKILL.md +2 -0
- package/claude/skills/auto-ship/SKILL.md +3 -11
- package/claude/skills/auto-ship/references/review-findings.md +20 -0
- package/claude/skills/backlog-triage/SKILL.md +2 -0
- package/claude/skills/bash-cli-script/SKILL.md +2 -0
- package/claude/skills/build-in-slices/SKILL.md +2 -0
- package/claude/skills/canon-cli/SKILL.md +2 -0
- package/claude/skills/canon-feedback/SKILL.md +2 -0
- package/claude/skills/canon-feedback-triage/SKILL.md +2 -0
- package/claude/skills/canon-operator/SKILL.md +2 -0
- package/claude/skills/canon-rollout/SKILL.md +3 -11
- package/claude/skills/canon-rollout/references/failure-recovery.md +20 -0
- package/claude/skills/canvas/SKILL.md +13 -0
- package/claude/skills/ci-workflow/SKILL.md +2 -0
- package/claude/skills/code-craft/SKILL.md +2 -0
- package/claude/skills/codebase-layout/SKILL.md +2 -0
- package/claude/skills/context-fold/SKILL.md +3 -17
- package/claude/skills/context-fold/references/completion-report.md +26 -0
- package/claude/skills/create-rule/SKILL.md +2 -0
- package/claude/skills/create-skill/SKILL.md +2 -0
- package/claude/skills/create-standard/SKILL.md +2 -0
- package/claude/skills/decision-escalate/SKILL.md +2 -0
- package/claude/skills/deploy-app/SKILL.md +2 -0
- package/claude/skills/deprecation-migration/SKILL.md +2 -0
- package/claude/skills/design-extract/SKILL.md +2 -0
- package/claude/skills/design-taste/SKILL.md +2 -0
- package/claude/skills/docs-sync/SKILL.md +2 -0
- package/claude/skills/document-health/SKILL.md +2 -0
- package/claude/skills/draft-and-pick/SKILL.md +2 -0
- package/claude/skills/draft-doc/SKILL.md +2 -0
- package/claude/skills/draft-doc/references/wiki.md +1 -1
- package/claude/skills/draft-figure/SKILL.md +2 -0
- package/claude/skills/draft-identity/SKILL.md +2 -0
- package/claude/skills/draft-ready/SKILL.md +2 -0
- package/claude/skills/draft-screencast/SKILL.md +2 -0
- package/claude/skills/draft-slides/SKILL.md +2 -0
- package/claude/skills/git-branch/SKILL.md +2 -0
- package/claude/skills/git-commit/SKILL.md +2 -0
- package/claude/skills/git-followup/SKILL.md +2 -0
- package/claude/skills/git-issue/SKILL.md +2 -0
- package/claude/skills/git-pr/SKILL.md +2 -0
- package/claude/skills/git-pr/references/evidence.md +6 -6
- package/claude/skills/git-ship/SKILL.md +2 -0
- package/claude/skills/git-split/SKILL.md +2 -0
- package/claude/skills/git-stage/SKILL.md +2 -0
- package/claude/skills/git-worktree/SKILL.md +2 -0
- package/claude/skills/index-lookup/SKILL.md +2 -0
- package/claude/skills/markdown-propose/SKILL.md +2 -0
- package/claude/skills/memory-capture/SKILL.md +2 -0
- package/claude/skills/memory-review/SKILL.md +2 -0
- package/claude/skills/plan-feature/SKILL.md +2 -0
- package/claude/skills/plan-groundwork/SKILL.md +2 -0
- package/claude/skills/plan-intake/SKILL.md +2 -0
- package/claude/skills/plan-intake-answer/SKILL.md +2 -0
- package/claude/skills/project-commands/SKILL.md +2 -0
- package/claude/skills/read-frames/SKILL.md +2 -0
- package/claude/skills/record-screencast/SKILL.md +2 -0
- package/claude/skills/repo-metadata/SKILL.md +2 -0
- package/claude/skills/restate-plainly/SKILL.md +2 -0
- package/claude/skills/review-address/SKILL.md +3 -7
- package/claude/skills/review-address/references/post-review-findings.md +16 -0
- package/claude/skills/review-branch/SKILL.md +2 -0
- package/claude/skills/review-craft/SKILL.md +2 -0
- package/claude/skills/review-craft/references/ui.md +2 -2
- package/claude/skills/review-pr/SKILL.md +4 -8
- package/claude/skills/review-pr/references/body-variants.md +52 -0
- package/claude/skills/review-pr/references/key-changes.md +16 -0
- package/claude/skills/review-pr/references/post.md +15 -43
- package/claude/skills/review-ui/SKILL.md +3 -1
- package/claude/skills/role-orchestrator/SKILL.md +5 -14
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +23 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +5 -33
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +1 -21
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +38 -0
- package/claude/skills/role-orchestrator/references/orchestrator-watch.md +26 -0
- package/claude/skills/role-planner/SKILL.md +2 -0
- package/claude/skills/role-reviewer/REQUIREMENT.md +1 -1
- package/claude/skills/role-reviewer/SKILL.md +2 -0
- package/claude/skills/role-worker/SKILL.md +2 -0
- package/claude/skills/search-craft/SKILL.md +2 -0
- package/claude/skills/seed-sync/SKILL.md +2 -0
- package/claude/skills/session-compact/SKILL.md +2 -0
- package/claude/skills/session-map/SKILL.md +2 -0
- package/claude/skills/session-relay/SKILL.md +2 -0
- package/claude/skills/session-resume/SKILL.md +2 -0
- package/claude/skills/session-worktree/SKILL.md +3 -11
- package/claude/skills/session-worktree/references/install-command.md +20 -0
- package/claude/skills/sketch-design/SKILL.md +2 -0
- package/claude/skills/systematic-debugging/SKILL.md +2 -0
- package/claude/skills/target-check/SKILL.md +2 -0
- package/claude/skills/target-setup/SKILL.md +2 -0
- package/claude/skills/task-board/SKILL.md +2 -0
- package/claude/skills/teach-workspace/SKILL.md +5 -15
- package/claude/skills/teach-workspace/references/lesson-build.md +75 -0
- package/claude/skills/teach-workspace/references/lesson-craft.md +13 -69
- package/claude/skills/teach-workspace/references/revisit.md +22 -0
- package/claude/skills/test-craft/SKILL.md +2 -0
- package/claude/skills/test-first/SKILL.md +2 -0
- package/claude/skills/ui-checklist/SKILL.md +2 -0
- package/claude/skills/ux-audit/SKILL.md +2 -0
- package/claude/skills/ux-measure/SKILL.md +2 -0
- package/claude/skills/ux-walkthrough/SKILL.md +2 -0
- package/claude/skills/video-craft/SKILL.md +2 -0
- package/claude/skills/write-human/SKILL.md +2 -0
- package/claude/skills/youtube-transcripts/SKILL.md +2 -0
- package/docs/agents/canvas.md +23 -7
- package/docs/agents/capture.md +5 -5
- package/docs/agents/commands.md +2 -2
- package/docs/agents/index.md +3 -3
- package/docs/agents/install-and-sync.md +7 -0
- package/docs/agents/key-changes.md +1 -1
- package/docs/agents/pr-evidence.md +38 -15
- package/docs/agents/pr-local.md +19 -13
- package/docs/agents/pr-preview.md +44 -21
- package/docs/agents/sandbox.md +28 -4
- package/docs/agents/scripting.md +8 -1
- package/docs/agents/skills-audit.md +2 -1
- package/docs/workflow/operating-model.md +5 -6
- package/docs/workflow/parallel-features.md +22 -1
- package/docs/workflow/skill-map.md +3 -1
- package/docs/workflow/zshrc-aliases.md +1 -1
- package/package.json +1 -1
- package/scripts/core/regen-hero.ts +1 -1
- package/scripts/lib/sandbox-path.sh +6 -162
- package/src/audits/run.ts +1 -1
- package/src/canvas/address.ts +12 -3
- package/src/canvas/client/inspector/align-grid.tsx +162 -0
- package/src/canvas/client/inspector/color-field.tsx +201 -171
- package/src/canvas/client/inspector/color-inputs.tsx +139 -0
- package/src/canvas/client/inspector/color-picker.tsx +668 -0
- package/src/canvas/client/inspector/color-space.ts +240 -0
- package/src/canvas/client/inspector/field.tsx +5 -1
- package/src/canvas/client/inspector/fields.css +395 -6
- package/src/canvas/client/inspector/size-field.tsx +69 -0
- package/src/canvas/client/inspector/values.ts +43 -1
- package/src/canvas/client/inspector.tsx +190 -4
- package/src/canvas/client/selection.tsx +197 -0
- package/src/canvas/client/shell.css +104 -12
- package/src/canvas/client/state.ts +69 -9
- package/src/canvas/client/surface.tsx +147 -22
- package/src/canvas/content.ts +50 -1
- package/src/canvas/edit.ts +37 -4
- package/src/canvas/editing.ts +203 -0
- package/src/canvas/global.d.ts +15 -0
- package/src/canvas/server.ts +149 -12
- package/src/canvas/tokens.ts +59 -17
- package/src/census/count.ts +1 -1
- package/src/claude/seeds.ts +1 -1
- package/src/claude/skills-audit.ts +28 -0
- package/src/claude/skills-drift.ts +1 -1
- package/src/claude/skills-families.ts +36 -0
- package/src/claude/skills-list.ts +76 -15
- package/src/commands/audits.ts +2 -2
- package/src/commands/canvas.ts +179 -2
- package/src/commands/claude.ts +67 -9
- package/src/commands/design.ts +142 -2
- package/src/commands/gov.ts +1 -1
- package/src/commands/hooks.ts +1 -1
- package/src/commands/indexes.ts +1 -1
- package/src/commands/init.ts +1 -1
- package/src/commands/intake.ts +1 -1
- package/src/commands/migrate.ts +1 -1
- package/src/commands/pr.ts +105 -58
- package/src/commands/records.ts +1 -1
- package/src/commands/repo.ts +1 -1
- package/src/commands/sandbox.ts +54 -2
- package/src/commands/sessions.ts +1 -1
- package/src/commands/sync.ts +1 -1
- package/src/commands/tasks.ts +2 -2
- package/src/commands/teach.ts +1 -1
- package/src/commands/transcripts.ts +1 -1
- package/src/commands/worktrees.ts +1 -1
- package/src/comments/scan.ts +1 -1
- package/src/comments/trend.ts +1 -1
- package/src/context/citations.ts +1 -1
- package/src/context/classify/extract.ts +2 -2
- package/src/design/project-fonts.ts +392 -0
- package/src/feedback/github.ts +1 -1
- package/src/gate/measures.ts +47 -9
- package/src/gate/sequencer.ts +1 -1
- package/src/gate/stages.ts +9 -0
- package/src/{git-files.ts → git/files.ts} +2 -2
- package/src/{git-ignore.ts → git/ignore.ts} +1 -1
- package/src/{worktree.ts → git/worktree.ts} +1 -1
- package/src/gov/citations.ts +1 -1
- package/src/gov/install.ts +1 -1
- package/src/gov/superseded.ts +1 -1
- package/src/gov/test-order.ts +2 -2
- package/src/help.ts +1 -1
- package/src/hooks/post-merge.ts +1 -1
- package/src/indexes/walk.ts +1 -1
- package/src/labels/audit.ts +1 -1
- package/src/markdown/files.ts +1 -1
- package/src/migrate/apply.ts +1 -1
- package/src/pr/bijection.ts +1 -1
- package/src/pr/evidence.ts +96 -43
- package/src/pr/local.ts +16 -10
- package/src/pr/preview.ts +78 -2
- package/src/records/backup.ts +1 -1
- package/src/records/stale-canonical.ts +1 -1
- package/src/sandbox/headless/dispatch.ts +264 -0
- package/src/sandbox/headless/run.ts +653 -0
- package/src/sandbox/headless/watch.ts +145 -0
- package/src/sandbox/provision.ts +12 -1
- package/src/sandbox/tree.ts +4 -8
- package/src/secrets/scan.ts +1 -1
- package/src/sessions/claim.ts +1 -1
- package/src/sessions/resolve.ts +1 -1
- package/src/sessions/transfer/bundle.ts +1 -1
- package/src/slides/package/fonts.ts +6 -2
- package/src/standards/audit.ts +2 -2
- package/src/sync/check.ts +2 -2
- package/src/sync/engine.ts +2 -2
- package/src/sync/git.ts +1 -1
- package/src/sync/history.ts +1 -1
- package/src/sync/reverse.ts +1 -1
- package/src/sync/target.ts +1 -1
- package/src/sync/workflow.ts +1 -1
- package/src/targets/pulls.ts +2 -2
- package/src/targets/sweep.ts +2 -2
- package/src/tasks/reach.ts +1 -1
- package/src/tasks/trunk.ts +1 -1
- package/src/tooling/inject.ts +1 -1
- package/src/tooling/subfolder.ts +1 -1
- package/src/web/readme-citations.ts +1 -1
- package/src/worktrees/bare-flag.ts +1 -1
- package/src/worktrees/reclaim.ts +2 -2
- package/src/worktrees/remove.ts +1 -1
- package/standards/canvas.md +2 -1
- package/standards/skill.md +1 -0
- package/standards/wiki.md +12 -3
- package/tooling/base/configs/.github/workflows/pr-local-preview.yml +2 -2
- package/scripts/lib/sandbox-dispatch.sh +0 -190
- /package/src/{client-commands.ts → gate/client-commands.ts} +0 -0
- /package/src/{git-base.ts → git/base.ts} +0 -0
- /package/src/{git-env.ts → git/env.ts} +0 -0
- /package/src/{copy.ts → sync/copy.ts} +0 -0
- /package/src/{target.ts → targets/validate.ts} +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
4
|
+
import { join, relative } from 'node:path'
|
|
5
|
+
|
|
6
|
+
/** Path to content hash, one entry per regular file. */
|
|
7
|
+
export type Manifest = ReadonlyMap<string, string>
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hashes every regular file under `base`, keyed by its path relative to it, for
|
|
11
|
+
* the top-level entries `keep` admits. A symlink is neither followed nor
|
|
12
|
+
* hashed, matching `find -type f`.
|
|
13
|
+
*/
|
|
14
|
+
function hashFiles(base: string, keep: (name: string) => boolean): Manifest {
|
|
15
|
+
const manifest = new Map<string, string>()
|
|
16
|
+
const pending = readdirSync(base, { withFileTypes: true })
|
|
17
|
+
.filter((entry) => keep(entry.name))
|
|
18
|
+
.map((entry) => ({ entry, dir: base }))
|
|
19
|
+
|
|
20
|
+
for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
|
|
21
|
+
const path = join(next.dir, next.entry.name)
|
|
22
|
+
if (next.entry.isDirectory()) {
|
|
23
|
+
for (const entry of readdirSync(path, { withFileTypes: true }))
|
|
24
|
+
pending.push({ entry, dir: path })
|
|
25
|
+
} else if (next.entry.isFile()) {
|
|
26
|
+
const hash = createHash('sha1').update(readFileSync(path)).digest('hex')
|
|
27
|
+
manifest.set(relative(base, path), hash)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return new Map([...manifest].sort(([a], [b]) => (a < b ? -1 : 1)))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Records a hash per file so the post-run comparison can name what the session
|
|
36
|
+
* wrote. A diff against `refs/sandbox/baseline` cannot stand in for this: an arm
|
|
37
|
+
* that sets SANDBOX_SKIP_AUTO_COMMIT leaves its last stage uncommitted, so the
|
|
38
|
+
* fixtures the harness staged would read as session writes.
|
|
39
|
+
*/
|
|
40
|
+
export function snapshotTree(dir: string): Manifest {
|
|
41
|
+
return hashFiles(dir, (name) => name !== '.git')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Reports both sides of a change, so a deletion is a write. Reporting only the
|
|
46
|
+
* after side would let a file removed outside the declared scope produce no
|
|
47
|
+
* assertion at all, which is the one way this check stayed weaker than the
|
|
48
|
+
* permission scoping it replaced. A modified file differs on both sides and
|
|
49
|
+
* reports once.
|
|
50
|
+
*/
|
|
51
|
+
export function writesBetween(before: Manifest, after: Manifest): string[] {
|
|
52
|
+
const paths = new Set([...before.keys(), ...after.keys()])
|
|
53
|
+
|
|
54
|
+
return [...paths]
|
|
55
|
+
.filter((path) => before.get(path) !== after.get(path))
|
|
56
|
+
.sort()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The roots a run must not write shared session scratch to. `snapshotTree`
|
|
61
|
+
* reads the sandbox alone, so before this a session that resolved that scratch
|
|
62
|
+
* against a toolkit root wrote where no manifest looked: the run reported
|
|
63
|
+
* success, the write list came back empty, and `write_scope` had nothing to
|
|
64
|
+
* assert against.
|
|
65
|
+
*
|
|
66
|
+
* It makes an escape visible rather than impossible. A session can write to a
|
|
67
|
+
* home directory, to a sibling worktree, or to any path outside these roots, and
|
|
68
|
+
* none of that is watched. The standing limits in
|
|
69
|
+
* `canon/context/sandbox/overview.md` carry what stays invisible.
|
|
70
|
+
*
|
|
71
|
+
* A linked worktree is the normal place to develop this repository, and the
|
|
72
|
+
* rule an escaping session follows sends scratch to the main root rather than to
|
|
73
|
+
* the worktree it was launched from, so the main root joins the list whenever it
|
|
74
|
+
* differs.
|
|
75
|
+
*/
|
|
76
|
+
export function escapeRoots(projectRoot: string): string[] {
|
|
77
|
+
const listing = spawnSync('git', ['worktree', 'list', '--porcelain'], {
|
|
78
|
+
cwd: projectRoot,
|
|
79
|
+
encoding: 'utf8',
|
|
80
|
+
})
|
|
81
|
+
const line = (listing.stdout ?? '')
|
|
82
|
+
.split('\n')
|
|
83
|
+
.find((l) => l.startsWith('worktree '))
|
|
84
|
+
const mainRoot = line?.slice('worktree '.length)
|
|
85
|
+
|
|
86
|
+
return mainRoot === undefined || mainRoot === '' || mainRoot === projectRoot
|
|
87
|
+
? [projectRoot]
|
|
88
|
+
: [projectRoot, mainRoot]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The four directories the seed's shared-scratch rule names, and the whole of
|
|
93
|
+
* where an escape lands. A session that resolves that rule against a toolkit
|
|
94
|
+
* root writes here and nowhere else, so watching these rather than the whole
|
|
95
|
+
* root costs no detection.
|
|
96
|
+
*
|
|
97
|
+
* It buys the difference between a signal and noise. Nothing distinguishes the
|
|
98
|
+
* spawned session's writes from the operator's own, so a watch over the whole
|
|
99
|
+
* root reports ordinary editing during a run as an escape. Measured 2026-08-02:
|
|
100
|
+
* three runs against the relocated sandbox all wrote their report to the right
|
|
101
|
+
* place, and two of the three still reported an escape under the wide watch,
|
|
102
|
+
* naming a context entry being edited in the session that launched them.
|
|
103
|
+
*
|
|
104
|
+
* The scope stated for an arm author: `escapeRoots` times two, these times four.
|
|
105
|
+
* This watch reaches nothing past those eight destinations, so a nested run's
|
|
106
|
+
* own escape is invisible here whether or not an arm declares `escape_scope`. A
|
|
107
|
+
* nested background dispatch reports through `sessions` instead, from
|
|
108
|
+
* `dispatch.ts`, so the key defined against this file-write watch keeps one
|
|
109
|
+
* meaning. An arm whose skill is meant to reach past the sandbox tree declares
|
|
110
|
+
* `escape_scope` in its `expect.toml`, which bounds a legitimate write rather
|
|
111
|
+
* than the run. Read `canon/context/sandbox/isolation.md` and
|
|
112
|
+
* `canon/context/sandbox/coverage/workflow-arms.md` before writing a claim past
|
|
113
|
+
* that.
|
|
114
|
+
*/
|
|
115
|
+
export const ESCAPE_SCRATCH_DIRS = [
|
|
116
|
+
'.canon/plans',
|
|
117
|
+
'.canon/review',
|
|
118
|
+
'.canon/memory',
|
|
119
|
+
'.canon/tasks',
|
|
120
|
+
] as const
|
|
121
|
+
|
|
122
|
+
export interface RootSnapshot {
|
|
123
|
+
readonly manifest: Manifest
|
|
124
|
+
/**
|
|
125
|
+
* The root held at least one of the four directories. A root with none of
|
|
126
|
+
* them contributes no manifest and no diff, which reads identically to a watch
|
|
127
|
+
* that ran clean, so `checkEscapeScope` needs this to tell "watched and clean"
|
|
128
|
+
* from "nothing to watch" apart.
|
|
129
|
+
*/
|
|
130
|
+
readonly isWatched: boolean
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function snapshotRoot(dir: string): RootSnapshot {
|
|
134
|
+
const targets = ESCAPE_SCRATCH_DIRS.filter((sub) =>
|
|
135
|
+
existsSync(join(dir, sub)),
|
|
136
|
+
)
|
|
137
|
+
if (targets.length === 0) return { manifest: new Map(), isWatched: false }
|
|
138
|
+
|
|
139
|
+
const manifest = new Map<string, string>()
|
|
140
|
+
for (const sub of targets)
|
|
141
|
+
for (const [path, hash] of hashFiles(join(dir, sub), () => true))
|
|
142
|
+
manifest.set(`${sub}/${path}`, hash)
|
|
143
|
+
|
|
144
|
+
return { manifest, isWatched: true }
|
|
145
|
+
}
|
package/src/sandbox/provision.ts
CHANGED
|
@@ -273,12 +273,22 @@ function setupAssets(
|
|
|
273
273
|
* tree, so a scenario exercises the branch's skill rather than the installed
|
|
274
274
|
* plugin's. The merge base against `origin/main` comes first, so a local `main`
|
|
275
275
|
* trailing the remote does not pull in skills other merged branches changed.
|
|
276
|
+
* A headless run skips it, since a copied body alone shadows the plugin's whole
|
|
277
|
+
* skill folder and its relative citations then resolve to nothing.
|
|
276
278
|
*/
|
|
277
279
|
export function injectChangedSkills(
|
|
278
280
|
root: string,
|
|
279
281
|
sandbox: string,
|
|
280
282
|
isSkipAutoCommit: boolean,
|
|
283
|
+
isSkipInject = false,
|
|
281
284
|
): string[] {
|
|
285
|
+
if (isSkipInject) {
|
|
286
|
+
logInfo(
|
|
287
|
+
'Skipped dev skill injection: the run resolves skills through --plugin-dir',
|
|
288
|
+
)
|
|
289
|
+
return []
|
|
290
|
+
}
|
|
291
|
+
|
|
282
292
|
const base = (
|
|
283
293
|
readGit(root, ['merge-base', 'HEAD', 'origin/main']) ??
|
|
284
294
|
readGit(root, ['merge-base', 'HEAD', 'main']) ??
|
|
@@ -438,7 +448,8 @@ function provisionScenario(
|
|
|
438
448
|
Object.assign(env, staged.exports)
|
|
439
449
|
|
|
440
450
|
const isSkipAutoCommit = (env.SANDBOX_SKIP_AUTO_COMMIT ?? '') !== ''
|
|
441
|
-
|
|
451
|
+
const isSkipInject = (env.SANDBOX_SKIP_SKILL_INJECT ?? '') !== ''
|
|
452
|
+
injectChangedSkills(root, sandbox, isSkipAutoCommit, isSkipInject)
|
|
442
453
|
must(commitScenarioChanges(sandbox, isSkipAutoCommit, env))
|
|
443
454
|
tagBaseline(sandbox, env)
|
|
444
455
|
|
package/src/sandbox/tree.ts
CHANGED
|
@@ -6,8 +6,8 @@ import { join } from 'node:path'
|
|
|
6
6
|
/**
|
|
7
7
|
* Mints a short per-run identifier the first time it is asked for and holds it
|
|
8
8
|
* in `CANON_SANDBOX_RUN_ID` for the rest of this process, so a script that
|
|
9
|
-
* spawns a child inheriting `process.env` — `
|
|
10
|
-
* provision and then `canon sandbox check` — resolves the same tree in every one
|
|
9
|
+
* spawns a child inheriting `process.env` — `canon sandbox run` calling
|
|
10
|
+
* `canon sandbox` to provision and then `canon sandbox check` — resolves the same tree in every one
|
|
11
11
|
* of them, as do the hook and installer children provisioning spawns. A process
|
|
12
12
|
* that already carries the variable, inherited from such a parent, reuses it
|
|
13
13
|
* rather than minting a new one.
|
|
@@ -49,9 +49,8 @@ export function sandboxTree(): string {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* runs before the tree exists, so nothing here follows a symlink.
|
|
52
|
+
* Lexical rather than `realpath`, since the guard runs before the tree exists,
|
|
53
|
+
* so nothing here follows a symlink.
|
|
55
54
|
*/
|
|
56
55
|
export function normalizeSandboxPath(path: string): string {
|
|
57
56
|
if (path === '') return ''
|
|
@@ -110,9 +109,6 @@ export function sandboxRoots(projectRoot: string): SandboxRoots {
|
|
|
110
109
|
* Provisioning removes the tree before staging, so the test is an allowlist: a
|
|
111
110
|
* strict descendant of the home folder or the temp root, outside the main
|
|
112
111
|
* worktree in both directions.
|
|
113
|
-
*
|
|
114
|
-
* Twin of `assert_sandbox_dir_safe` in `scripts/lib/sandbox-path.sh`, message
|
|
115
|
-
* for message, since the headless runner still guards through that copy.
|
|
116
112
|
*/
|
|
117
113
|
export function assertSandboxDirSafe(
|
|
118
114
|
raw: string,
|
package/src/secrets/scan.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { isBinary } from '@/sweep/binary'
|
|
4
|
-
import { listRepositoryFiles } from '@/git
|
|
4
|
+
import { listRepositoryFiles } from '@/git/files'
|
|
5
5
|
import { isExempt } from '@/secrets/marker'
|
|
6
6
|
import { matchLine } from '@/secrets/patterns'
|
|
7
7
|
import { readShipEntries, selectShipped } from '@/secrets/shipped'
|
package/src/sessions/claim.ts
CHANGED
package/src/sessions/resolve.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from 'node:fs'
|
|
11
11
|
import { basename, dirname, isAbsolute, join, relative } from 'node:path'
|
|
12
12
|
import { $ } from 'bun'
|
|
13
|
-
import { gitEnv } from '@/git
|
|
13
|
+
import { gitEnv } from '@/git/env'
|
|
14
14
|
import { recordDir, recordRoot, SCRATCH } from '@/record-root'
|
|
15
15
|
import { remoteIdentity } from '@/records/backup'
|
|
16
16
|
import {
|
|
@@ -152,9 +152,13 @@ export function eotOf(bytes: Uint8Array): EotResult {
|
|
|
152
152
|
}
|
|
153
153
|
const strings = names(font, name)
|
|
154
154
|
const padding = Buffer.alloc(2)
|
|
155
|
-
// RootStringCheckSum, EUDCCodePage, Padding6, SignatureSize,
|
|
156
|
-
// and EUDCFontSize,
|
|
155
|
+
// RootStringCheckSum, then EUDCCodePage, Padding6, SignatureSize,
|
|
156
|
+
// EUDCFlags, and EUDCFontSize, zero for a face with no EUDC data. The
|
|
157
|
+
// checksum is the root string's byte sum XOR 0x50475342, so an empty root
|
|
158
|
+
// string still writes the constant, and PowerPoint refuses to install a
|
|
159
|
+
// face whose checksum is zero as tampered with.
|
|
157
160
|
const tail = Buffer.alloc(20)
|
|
161
|
+
tail.writeUInt32LE(0x50475342, 0)
|
|
158
162
|
const eot = Buffer.concat([
|
|
159
163
|
header(font, os2.offset, head.offset),
|
|
160
164
|
padding,
|
package/src/standards/audit.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
2
2
|
import { basename, join } from 'node:path'
|
|
3
3
|
import { $ } from 'bun'
|
|
4
|
-
import { gitEnv } from '@/git
|
|
5
|
-
import { resolveBaseRef } from '@/git
|
|
4
|
+
import { gitEnv } from '@/git/env'
|
|
5
|
+
import { resolveBaseRef } from '@/git/files'
|
|
6
6
|
import { INDEX_FILE, standardsSourceDir } from '@/standards/read'
|
|
7
7
|
|
|
8
8
|
/** Returned when a standard new to this branch states no success criterion under either heading `CRITERION_HEADING` accepts, the gating check. */
|
package/src/sync/check.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { basename, join, sep } from 'node:path'
|
|
3
3
|
import { execa } from 'execa'
|
|
4
|
-
import { gitEnv } from '@/git
|
|
4
|
+
import { gitEnv } from '@/git/env'
|
|
5
5
|
import { createDesignAdapter, DESIGN_INSTALL_DIR } from '@/design/adapter'
|
|
6
6
|
import { createGovAdapter, rulesSourceDir } from '@/gov/adapter'
|
|
7
7
|
import { successorResolver } from '@/gov/renames'
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
stampedCommit,
|
|
30
30
|
type StampDomain,
|
|
31
31
|
} from '@/sync/stamp'
|
|
32
|
-
import { isDirectory } from '@/
|
|
32
|
+
import { isDirectory } from '@/targets/validate'
|
|
33
33
|
import {
|
|
34
34
|
isWholeStack,
|
|
35
35
|
loadManifest,
|
package/src/sync/engine.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs'
|
|
2
2
|
import { rm } from 'node:fs/promises'
|
|
3
3
|
import { isAbsolute, relative, resolve } from 'node:path'
|
|
4
|
-
import { copyPreservingMode } from '@/copy'
|
|
4
|
+
import { copyPreservingMode } from '@/sync/copy'
|
|
5
5
|
import { checkoutMismatchWarning } from '@/project-root'
|
|
6
6
|
import { findInstalledOrigin, readHistoryIndex } from '@/sync/history'
|
|
7
7
|
import {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
toStampKey,
|
|
17
17
|
writeStamp,
|
|
18
18
|
} from '@/sync/stamp'
|
|
19
|
-
import { isDirectory } from '@/
|
|
19
|
+
import { isDirectory } from '@/targets/validate'
|
|
20
20
|
import {
|
|
21
21
|
intro,
|
|
22
22
|
isNonInteractive,
|
package/src/sync/git.ts
CHANGED
package/src/sync/history.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto'
|
|
|
2
2
|
import { readFileSync } from 'node:fs'
|
|
3
3
|
import { sep } from 'node:path'
|
|
4
4
|
import { execaSync } from 'execa'
|
|
5
|
-
import { gitEnv } from '@/git
|
|
5
|
+
import { gitEnv } from '@/git/env'
|
|
6
6
|
|
|
7
7
|
/** Blob SHA to the newest commit whose post-image for that path held it. */
|
|
8
8
|
export type PathHistory = ReadonlyMap<string, string>
|
package/src/sync/reverse.ts
CHANGED
package/src/sync/target.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { DESIGN_INSTALL_DIR } from '@/design/adapter'
|
|
4
|
-
import { isDirectory } from '@/
|
|
4
|
+
import { isDirectory } from '@/targets/validate'
|
|
5
5
|
|
|
6
6
|
export const SYNC_DOMAINS = ['governance', 'design', 'claude'] as const
|
|
7
7
|
|
package/src/sync/workflow.ts
CHANGED
package/src/targets/pulls.ts
CHANGED
package/src/targets/sweep.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readdirSync } from 'node:fs'
|
|
2
2
|
import { join, resolve } from 'node:path'
|
|
3
3
|
import { $ } from 'bun'
|
|
4
|
-
import { gitEnv } from '@/git
|
|
4
|
+
import { gitEnv } from '@/git/env'
|
|
5
5
|
import {
|
|
6
6
|
claudeCanonStampPath,
|
|
7
7
|
isLegacyStamped,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
retiredNameStampPath,
|
|
10
10
|
stampPath,
|
|
11
11
|
} from '@/sync/stamp'
|
|
12
|
-
import { isDirectory } from '@/
|
|
12
|
+
import { isDirectory } from '@/targets/validate'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Folders a walk never descends into.
|
package/src/tasks/reach.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { readdir, readFile } from 'node:fs/promises'
|
|
3
3
|
import { basename, join } from 'node:path'
|
|
4
|
-
import { listChangedFiles, resolveBaseRef } from '@/git
|
|
4
|
+
import { listChangedFiles, resolveBaseRef } from '@/git/files'
|
|
5
5
|
import { recordDir } from '@/record-root'
|
|
6
6
|
import { splitPlanSections } from '@/records/validate'
|
|
7
7
|
import { type AnswersRefused, resolvePlanReference } from '@/tasks/answers'
|
package/src/tasks/trunk.ts
CHANGED
package/src/tooling/inject.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
|
|
|
2
2
|
import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
3
3
|
import { dirname, join, relative } from 'node:path'
|
|
4
4
|
import { $ } from 'bun'
|
|
5
|
-
import { copyPreservingMode } from '@/copy'
|
|
5
|
+
import { copyPreservingMode } from '@/sync/copy'
|
|
6
6
|
import { rewritesOnInstall, stripSeedMarker } from '@/tooling/seed-marker'
|
|
7
7
|
import { resolveSurfacePath } from '@/surface-root'
|
|
8
8
|
import { mergeSections, pruneSections } from '@/tooling/gitignore'
|
package/src/tooling/subfolder.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, realpathSync } from 'node:fs'
|
|
2
2
|
import { basename, join, relative } from 'node:path'
|
|
3
|
-
import { gitEnv } from '@/git
|
|
3
|
+
import { gitEnv } from '@/git/env'
|
|
4
4
|
import { isWholeStack, type Manifest, seedFiles } from '@/tooling/manifest'
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -34,7 +34,7 @@ const QUOTED_PHRASE = /"([^"]+)"/g
|
|
|
34
34
|
* retired `README.md:<n>` form, reported rather than accepted so the fragile
|
|
35
35
|
* convention this replaces cannot come back on a later edit.
|
|
36
36
|
*
|
|
37
|
-
* Modeled on `clientCommandCitationsIn` in `src/client-commands.ts`, including
|
|
37
|
+
* Modeled on `clientCommandCitationsIn` in `src/gate/client-commands.ts`, including
|
|
38
38
|
* its use of `isMarked` for the exemption.
|
|
39
39
|
*/
|
|
40
40
|
export function readmeCitationsIn(
|
package/src/worktrees/reclaim.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $ } from 'bun'
|
|
2
2
|
import { execa } from 'execa'
|
|
3
3
|
import { existsSync } from 'node:fs'
|
|
4
|
-
import { gitEnv } from '@/git
|
|
4
|
+
import { gitEnv } from '@/git/env'
|
|
5
5
|
import {
|
|
6
6
|
repositoryOf,
|
|
7
7
|
type ResolvedSession,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
currentWorktreeRoot,
|
|
13
13
|
listWorktrees,
|
|
14
14
|
type WorktreeEntry,
|
|
15
|
-
} from '@/worktree'
|
|
15
|
+
} from '@/git/worktree'
|
|
16
16
|
|
|
17
17
|
const GH_TIMEOUT_MS = 30_000
|
|
18
18
|
|
package/src/worktrees/remove.ts
CHANGED
package/standards/canvas.md
CHANGED
|
@@ -41,7 +41,8 @@ A canvas failing these is non-conforming even when it satisfies every shape rule
|
|
|
41
41
|
## Frames
|
|
42
42
|
|
|
43
43
|
- Write one frame as one `.html` file inside its page folder. The filename without its extension is the frame's name, and the name follows the page naming rule.
|
|
44
|
-
- Write
|
|
44
|
+
- Write a frame as a whole document or as a fragment. The server writes in the `html`, `head`, and `body` a fragment leaves out, marked so a pick still matches the file.
|
|
45
|
+
- State a `body` whenever the frame states a `head`. The browser builds one the file leaves out, and an address the operator picks inside the frame can no longer be matched to the file.
|
|
45
46
|
- Write every table with its `tbody` stated, for the same reason. A browser adds one the file leaves out.
|
|
46
47
|
- Keep a frame self-contained. Inline its own styles and scripts, or link a file inside the same page folder, since a frame is served from its page and nothing outside the canvas resolves.
|
|
47
48
|
- Use the project's custom properties, `var(--<name>)`, for every value the token stylesheet defines. A literal copied from it shows the right color today and the wrong one after the next token change.
|
package/standards/skill.md
CHANGED
|
@@ -100,6 +100,7 @@ allowed-tools: <tools required>
|
|
|
100
100
|
- `disable-model-invocation: true`: user-invoked only, Claude will not auto-trigger. Set it on a skill that starts a process the operator owns, never on one reachable by a matched trigger.
|
|
101
101
|
- `allowed-tools`: restrict tool access when the skill is active
|
|
102
102
|
- `metadata`: optional key-value pairs (`author`, `version`, `mcp-server`)
|
|
103
|
+
- `metadata.family` (required on a skill under `claude/skills/`): the skill map group the skill's row sits under, as one key from `families` in `canon claude skills list --json`. A skill under `.claude/skills/` takes none.
|
|
103
104
|
|
|
104
105
|
## Description
|
|
105
106
|
|
package/standards/wiki.md
CHANGED
|
@@ -47,9 +47,18 @@ A page failing either is non-conforming even when it satisfies every shape rule
|
|
|
47
47
|
## Sourcing
|
|
48
48
|
|
|
49
49
|
- Close the intro paragraph with a `Source:` sentence naming the owner. Link the canonical page where one exists, and name the owner alone where the subject has no single URL.
|
|
50
|
+
- Link a docs page in its fetchable form, `https://code.claude.com/docs/en/<page>.md`, so a session reads the live text rather than a copy.
|
|
50
51
|
- Fetch current information through the `claude-code-guide` agent when the subject is Claude Code. Do not work from training knowledge.
|
|
51
52
|
- Propose an addition or correction and wait for confirmation. Do not write to a wiki file unasked.
|
|
52
53
|
|
|
54
|
+
## Shape
|
|
55
|
+
|
|
56
|
+
- Keep a page thin: the `Source:` sentence, an orientation paragraph, and lesson sections. The source owns the reference detail and a paraphrase of it falls behind within days.
|
|
57
|
+
- Apply the lesson test per section: keep a fact about the subject only when the source page does not state it. Such a fact is a gotcha this project hit, a constraint it relies on, or how it uses the feature.
|
|
58
|
+
- Read the current source before keeping or cutting a section. A fact kept from memory is the drift this shape exists to prevent.
|
|
59
|
+
- Send a session that wants reference detail to the source and fetch it live. The page answers orientation and lessons only.
|
|
60
|
+
- Leave a page with no lessons as the source link and the orientation paragraph. A catalog routes a reader by page, so the stub stays.
|
|
61
|
+
|
|
53
62
|
## Template
|
|
54
63
|
|
|
55
64
|
```markdown
|
|
@@ -60,9 +69,9 @@ description: <one line naming what this page covers>
|
|
|
60
69
|
|
|
61
70
|
# <Subject>
|
|
62
71
|
|
|
63
|
-
<What the subject is and why it matters.> Source: <owner, with a link to the canonical page where one exists>.
|
|
72
|
+
<What the subject is and why it matters, in a short orientation.> Source: <owner, with a link to the canonical page where one exists>.
|
|
64
73
|
|
|
65
|
-
## <
|
|
74
|
+
## <Lesson>
|
|
66
75
|
|
|
67
|
-
<
|
|
76
|
+
<A fact the source page does not state.>
|
|
68
77
|
```
|
|
@@ -16,7 +16,7 @@ jobs:
|
|
|
16
16
|
# Say so and pass rather than failing a close nobody can fix.
|
|
17
17
|
- name: Skip a fork pull request
|
|
18
18
|
if: github.event.pull_request.head.repo.full_name != github.repository
|
|
19
|
-
run: echo "::notice::Fork pull request, so the token cannot edit the evidence comment. The
|
|
19
|
+
run: echo "::notice::Fork pull request, so the token cannot edit the evidence comment. The preview line stays."
|
|
20
20
|
|
|
21
21
|
- name: Setup Bun
|
|
22
22
|
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
@@ -24,7 +24,7 @@ jobs:
|
|
|
24
24
|
with:
|
|
25
25
|
bun-version: latest
|
|
26
26
|
|
|
27
|
-
- name:
|
|
27
|
+
- name: Drop the preview line from the evidence comment
|
|
28
28
|
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
29
29
|
env:
|
|
30
30
|
GH_TOKEN: ${{ github.token }}
|