@erclx/canon 5.41.0 → 5.43.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/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/references/adopted.md +34 -14
- package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
- package/claude/skills/auto-ship/SKILL.md +24 -4
- package/claude/skills/auto-ship/references/draft-mark.md +32 -6
- package/claude/skills/auto-ship/references/failure-recovery.md +15 -13
- package/claude/skills/auto-ship/references/tier-failures.md +31 -10
- package/claude/skills/auto-ship/references/ui-checklist.md +9 -4
- package/claude/skills/auto-ship/references/worktree-name.md +9 -3
- package/claude/skills/build-in-slices/references/adopted.md +35 -21
- package/claude/skills/canvas-board/references/canvas.md +17 -6
- package/claude/skills/canvas-board/references/design.md +26 -7
- package/claude/skills/ci-workflow/REQUIREMENT.md +2 -1
- package/claude/skills/ci-workflow/SKILL.md +12 -2
- package/claude/skills/ci-workflow/references/workflows.md +12 -9
- package/claude/skills/code-craft/references/adopted.md +58 -20
- package/claude/skills/codebase-layout/references/adopted.md +49 -16
- package/claude/skills/codebase-layout/references/frontend.md +11 -4
- package/claude/skills/codebase-layout/references/tests.md +4 -2
- package/claude/skills/context-fold/references/classify.md +10 -15
- package/claude/skills/context-fold/references/completion-report.md +11 -9
- package/claude/skills/context-fold/references/context-refresh.md +26 -9
- package/claude/skills/context-fold/references/unusable-baseline.md +5 -3
- package/claude/skills/create-rule/REQUIREMENT.md +5 -0
- package/claude/skills/create-rule/SKILL.md +10 -1
- package/claude/skills/create-rule/references/checklist.md +26 -0
- package/claude/skills/create-rule/references/rule.md +98 -20
- package/claude/skills/create-skill/SKILL.md +1 -0
- package/claude/skills/create-skill/references/skill-disclosure.md +10 -2
- package/claude/skills/create-skill/references/skill-frontmatter.md +43 -0
- package/claude/skills/create-skill/references/skill-paths.md +7 -4
- package/claude/skills/create-skill/references/skill-practice.md +6 -3
- package/claude/skills/create-skill/references/skill-requirement.md +4 -2
- package/claude/skills/create-skill/references/skill.md +67 -44
- package/claude/skills/decision-escalate/SKILL.md +1 -2
- package/claude/skills/deploy-app/references/vercel.md +2 -1
- package/claude/skills/deprecation-migration/references/adopted.md +44 -25
- package/claude/skills/design-taste/references/adopted.md +9 -1
- package/claude/skills/design-taste/references/craft.md +3 -1
- package/claude/skills/design-taste/references/kinds.md +8 -7
- package/claude/skills/design-taste/references/preflight.md +12 -0
- package/claude/skills/design-taste/references/systems.md +7 -6
- package/claude/skills/design-taste/references/tells.md +24 -25
- package/claude/skills/design-taste/references/vocabulary.md +7 -5
- package/claude/skills/document-health/references/axes.md +55 -30
- package/claude/skills/draft-and-pick/references/live-arms.md +2 -2
- package/claude/skills/draft-doc/references/context.md +4 -3
- package/claude/skills/draft-doc/references/docs.md +6 -3
- package/claude/skills/draft-doc/references/readme/agent-facing.md +6 -0
- package/claude/skills/draft-doc/references/readme/application.md +8 -2
- package/claude/skills/draft-doc/references/readme/cli.md +5 -1
- package/claude/skills/draft-doc/references/readme/library.md +4 -0
- package/claude/skills/draft-doc/references/readme/plugin.md +9 -1
- package/claude/skills/draft-doc/references/readme.md +30 -13
- package/claude/skills/draft-doc/references/requirements.md +17 -4
- package/claude/skills/draft-doc/references/wiki-kind.md +20 -10
- package/claude/skills/draft-doc/references/wiki.md +1 -1
- package/claude/skills/draft-figure/references/architecture-set.md +7 -3
- package/claude/skills/draft-figure/references/figures.md +8 -6
- package/claude/skills/draft-figure/references/hand-drawn.md +3 -1
- package/claude/skills/draft-figure/references/mermaid-path.md +19 -9
- package/claude/skills/draft-figure/references/mermaid.md +10 -5
- package/claude/skills/draft-ready/references/ready.md +22 -15
- package/claude/skills/git-branch/references/branch.md +11 -1
- package/claude/skills/git-branch/references/slug.md +17 -5
- package/claude/skills/git-commit/references/commit.md +9 -5
- package/claude/skills/git-commit/references/versioning.md +23 -17
- package/claude/skills/git-followup/REQUIREMENT.md +1 -1
- package/claude/skills/git-followup/SKILL.md +5 -6
- package/claude/skills/git-issue/references/issue.md +29 -16
- package/claude/skills/git-pr/REQUIREMENT.md +1 -5
- package/claude/skills/git-pr/SKILL.md +11 -22
- package/claude/skills/git-pr/references/final-command.md +21 -8
- package/claude/skills/git-pr/references/labels.md +45 -28
- package/claude/skills/git-pr/references/pr.md +29 -9
- package/claude/skills/git-pr/references/publish.md +42 -28
- package/claude/skills/git-pr/references/task-number.md +20 -8
- package/claude/skills/git-ship/SKILL.md +1 -1
- package/claude/skills/plan-feature/SKILL.md +1 -1
- package/claude/skills/plan-feature/references/plan-lifecycle.md +10 -3
- package/claude/skills/plan-feature/references/plan.md +58 -21
- package/claude/skills/plan-groundwork/references/groundwork-spikes.md +4 -1
- package/claude/skills/plan-groundwork/references/groundwork.md +14 -16
- package/claude/skills/plan-intake/references/intake.md +29 -24
- package/claude/skills/pr-evidence/REQUIREMENT.md +40 -0
- package/claude/skills/pr-evidence/SKILL.md +53 -0
- package/claude/skills/pr-evidence/references/evidence.md +150 -0
- package/claude/skills/review-address/references/post-review-findings.md +10 -3
- package/claude/skills/review-address/references/rebase-conflicts.md +15 -7
- package/claude/skills/review-address/references/rest-reads.md +3 -1
- package/claude/skills/review-craft/references/adopted.md +35 -18
- package/claude/skills/review-craft/references/security.md +42 -7
- package/claude/skills/review-craft/references/ui.md +23 -8
- package/claude/skills/review-pr/SKILL.md +6 -6
- package/claude/skills/review-pr/references/body-variants.md +29 -9
- package/claude/skills/review-pr/references/close-out.md +20 -5
- package/claude/skills/review-pr/references/key-changes.md +27 -3
- package/claude/skills/review-pr/references/post.md +83 -31
- package/claude/skills/review-pr/references/scope.md +54 -19
- package/claude/skills/review-ui/references/close-out.md +7 -5
- package/claude/skills/review-ui/references/driving.md +24 -4
- package/claude/skills/review-ui/references/post.md +43 -15
- package/claude/skills/role-orchestrator/SKILL.md +2 -0
- package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +67 -17
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +80 -40
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +52 -15
- package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +48 -26
- package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +12 -13
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +72 -36
- package/claude/skills/role-orchestrator/references/orchestrator-output.md +16 -4
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +64 -24
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +80 -26
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +41 -18
- package/claude/skills/role-orchestrator/references/orchestrator-resume.md +19 -7
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +22 -12
- package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +15 -6
- package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +25 -7
- package/claude/skills/role-orchestrator/references/orchestrator-watch.md +34 -8
- package/claude/skills/role-orchestrator/scripts/poll.ts +16 -24
- package/claude/skills/role-planner/REQUIREMENT.md +1 -1
- package/claude/skills/role-planner/SKILL.md +3 -3
- package/claude/skills/role-worker/REQUIREMENT.md +3 -0
- package/claude/skills/role-worker/SKILL.md +34 -2
- package/claude/skills/search-craft/REQUIREMENT.md +1 -1
- package/claude/skills/search-craft/SKILL.md +2 -2
- package/claude/skills/search-craft/references/adopted.md +18 -6
- package/claude/skills/session-map/references/session.md +26 -7
- package/claude/skills/session-worktree/references/main-root-writes.md +25 -8
- package/claude/skills/systematic-debugging/references/adopted.md +30 -16
- package/claude/skills/target-check/references/domains.md +39 -11
- package/claude/skills/target-setup/references/detect.md +80 -65
- package/claude/skills/target-setup/references/indexes.md +8 -6
- package/claude/skills/task-board/references/board.md +86 -30
- package/claude/skills/task-board/references/decline.md +29 -13
- package/claude/skills/task-board/references/tasks-decline.md +12 -5
- package/claude/skills/task-board/references/tasks.md +65 -33
- package/claude/skills/task-complete/references/archive.md +41 -17
- package/claude/skills/task-complete/references/receipt-sweep.md +23 -9
- package/claude/skills/task-complete/references/unusable-baseline.md +10 -3
- package/claude/skills/teach-workspace/references/glossary.md +6 -2
- package/claude/skills/teach-workspace/references/lesson-build.md +44 -16
- package/claude/skills/teach-workspace/references/lesson-craft.md +12 -7
- package/claude/skills/teach-workspace/references/pedagogy.md +6 -3
- package/claude/skills/teach-workspace/references/promotion-fold.md +17 -11
- package/claude/skills/teach-workspace/references/promotion.md +31 -8
- package/claude/skills/teach-workspace/references/revisit.md +8 -2
- package/claude/skills/teach-workspace/references/teach.md +17 -9
- package/claude/skills/test-craft/references/adopted.md +48 -14
- package/claude/skills/test-craft/references/component.md +7 -4
- package/claude/skills/test-craft/references/e2e.md +25 -7
- package/claude/skills/test-craft/references/unit.md +3 -1
- package/claude/skills/test-first/references/adopted.md +12 -4
- package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
- package/claude/skills/ui-checklist/SKILL.md +1 -1
- package/claude/skills/video-craft/references/adopted.md +62 -18
- package/claude/skills/write-human/references/adopted.md +21 -8
- package/docs/agents/audits.md +1 -0
- package/docs/agents/migrate.md +11 -8
- package/docs/target/migrations.md +11 -0
- package/docs/workflow/parallel-features.md +1 -1
- package/docs/workflow/skill-map.md +1 -0
- package/governance/detect.toml +4 -0
- package/governance/rules/canon/context.md +2 -1
- package/governance/rules/canon/plan.md +3 -5
- package/governance/rules/canon/scratch.md +3 -1
- package/governance/rules/canon/task-scope.md +2 -1
- package/governance/rules/canon/tasks.md +2 -1
- package/governance/rules/canon/worktrees.md +5 -2
- package/governance/rules/claude/output.md +7 -4
- package/governance/rules/claude/skill.md +6 -4
- package/governance/rules/tooling/ci-workflow.md +6 -39
- package/governance/rules/ui/design-reference.md +37 -0
- package/governance/rules/writing/markdown.md +2 -1
- package/package.json +6 -6
- package/src/audits/catalog.ts +42 -0
- package/src/claude/cases/workflow.ts +5 -0
- package/src/claude/settings.ts +8 -4
- package/src/commands/claude.ts +11 -1
- package/src/commands/gov.ts +198 -0
- package/src/commands/migrate.ts +121 -2
- package/src/commands/pr.ts +146 -105
- package/src/commands/tooling.ts +3 -3
- package/src/commands/worktrees.ts +49 -0
- package/src/gate/stages.ts +15 -0
- package/src/gov/audit.ts +440 -0
- package/src/listeners/belongs.ts +21 -0
- package/src/listeners/machine.ts +93 -0
- package/src/listeners/parse.ts +41 -0
- package/src/markdown/scope.ts +144 -0
- package/src/migrate/session-defaults.ts +138 -0
- package/src/pr/lift.ts +6 -3
- package/src/pr/local.ts +4 -69
- package/src/pr/review-state.ts +182 -0
- package/src/sync/check.ts +15 -0
- package/src/tooling/stamp.ts +56 -12
- package/src/worktrees/stop.ts +92 -0
- package/tooling/base/configs/.github/workflows/phase-label-gate.yml +6 -2
- package/tooling/base/configs/commitlint.config.js +0 -20
- package/tooling/base/reference.md +1 -1
- package/tooling/base/seeds/.github/workflows/verify.yml +4 -1
- package/tooling/claude/reference.md +13 -7
- package/tooling/claude/seeds/.claude/settings.json +4 -0
- package/tooling/claude/user/settings.template.json +0 -4
- package/tooling/cloudflare/configs/.github/workflows/deploy.yml +5 -5
- package/tooling/public/configs/.github/dependabot.yml +12 -0
- package/tooling/public/configs/.github/workflows/release-please.yml +30 -0
- package/tooling/public/manifest.toml +8 -0
- package/tooling/public/reference.md +53 -0
- package/tooling/public/seeds/.github/ISSUE_TEMPLATE/bug.yml +48 -0
- package/tooling/public/seeds/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/tooling/public/seeds/.github/ISSUE_TEMPLATE/feature.yml +16 -0
- package/tooling/public/seeds/.release-please-manifest.json +3 -0
- package/tooling/public/seeds/CONTRIBUTING.md +50 -0
- package/tooling/public/seeds/LICENSE +21 -0
- package/tooling/public/seeds/SECURITY.md +13 -0
- package/tooling/public/seeds/release-please-config.json +12 -0
- package/tooling/shell/configs/.github/workflows/shell.yml +4 -1
- package/tooling/web/configs/.github/workflows/readme-screenshot.yml +6 -3
- package/tooling/web/configs/.github/workflows/verify.yml +15 -12
- package/claude/skills/git-pr/references/evidence.md +0 -124
package/src/sync/check.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
resolveChain,
|
|
39
39
|
} from '@/tooling/manifest'
|
|
40
40
|
import { scan } from '@/tooling/scan'
|
|
41
|
+
import { isAddOn } from '@/tooling/stamp'
|
|
41
42
|
import { readSkew, type SkewReport } from '@/version/skew'
|
|
42
43
|
|
|
43
44
|
/**
|
|
@@ -302,6 +303,20 @@ export function buildToolingReport(
|
|
|
302
303
|
function resolveRecorded(
|
|
303
304
|
toolkitRoot: string,
|
|
304
305
|
recorded: readonly string[],
|
|
306
|
+
): Manifest[] {
|
|
307
|
+
const addOns = recorded
|
|
308
|
+
.map((name) => loadManifest(toolkitRoot, name))
|
|
309
|
+
.filter((manifest) => manifest !== undefined)
|
|
310
|
+
.filter(isAddOn)
|
|
311
|
+
const addOnNames = new Set(addOns.map((manifest) => manifest.name))
|
|
312
|
+
const core = recorded.filter((name) => !addOnNames.has(name))
|
|
313
|
+
|
|
314
|
+
return [...resolveCore(toolkitRoot, core), ...addOns]
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function resolveCore(
|
|
318
|
+
toolkitRoot: string,
|
|
319
|
+
recorded: readonly string[],
|
|
305
320
|
): Manifest[] {
|
|
306
321
|
const loaded = recorded
|
|
307
322
|
.map((name) => loadManifest(toolkitRoot, name))
|
package/src/tooling/stamp.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
-
import { writeChainStamp } from '@/sync/stamp'
|
|
4
|
-
import { isWholeStack, type Manifest } from '@/tooling/manifest'
|
|
3
|
+
import { readStamp, stampedChain, writeChainStamp } from '@/sync/stamp'
|
|
4
|
+
import { isWholeStack, loadManifest, type Manifest } from '@/tooling/manifest'
|
|
5
5
|
import { readPackage } from '@/tooling/package'
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -17,7 +17,9 @@ export function isWorkspaceRoot(target: string): boolean {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Records the chain an install resolved, and returns
|
|
20
|
+
* Records the chain an install resolved, and returns the names it wrote, or
|
|
21
|
+
* nothing when it wrote none. The names are the merged list, which can carry
|
|
22
|
+
* stacks an earlier sync recorded. A
|
|
21
23
|
* workspace root is the single refusal: one chain recorded there would be a
|
|
22
24
|
* guess at what its packages hold, and the report reading tooling as unmeasured
|
|
23
25
|
* is the true answer rather than a clean one.
|
|
@@ -27,16 +29,58 @@ export async function recordToolingChain(
|
|
|
27
29
|
target: string,
|
|
28
30
|
chain: readonly Manifest[],
|
|
29
31
|
now: Date,
|
|
30
|
-
): Promise<
|
|
31
|
-
if (isWorkspaceRoot(target)) return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
now,
|
|
32
|
+
): Promise<string[] | undefined> {
|
|
33
|
+
if (isWorkspaceRoot(target)) return undefined
|
|
34
|
+
|
|
35
|
+
const merged = mergeAddOns(
|
|
36
|
+
toolkitRoot,
|
|
37
|
+
stampedChain(readStamp(target), 'tooling'),
|
|
38
|
+
chain,
|
|
38
39
|
)
|
|
39
|
-
|
|
40
|
+
await writeChainStamp(target, { domain: 'tooling', toolkitRoot }, merged, now)
|
|
41
|
+
return merged
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* An add-on stands alone beside a core chain, so syncing one must not displace
|
|
46
|
+
* the chain it layers onto. Base extends nothing and still heads a core chain.
|
|
47
|
+
*/
|
|
48
|
+
export function isAddOn(manifest: Manifest | undefined): boolean {
|
|
49
|
+
return (
|
|
50
|
+
manifest !== undefined &&
|
|
51
|
+
manifest.parent === undefined &&
|
|
52
|
+
manifest.name !== 'base'
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Merges what this sync resolved into what an earlier one recorded. The core
|
|
58
|
+
* chain is the one just synced, or the recorded one when only add-ons synced.
|
|
59
|
+
* Add-ons accumulate after it, so a later stackless report measures them all.
|
|
60
|
+
*/
|
|
61
|
+
function mergeAddOns(
|
|
62
|
+
toolkitRoot: string,
|
|
63
|
+
recorded: readonly string[],
|
|
64
|
+
chain: readonly Manifest[],
|
|
65
|
+
): string[] {
|
|
66
|
+
const synced = chain.filter(isWholeStack)
|
|
67
|
+
const syncedCore = synced.filter((m) => !isAddOn(m)).map((m) => m.name)
|
|
68
|
+
const syncedAddOns = synced.filter(isAddOn).map((m) => m.name)
|
|
69
|
+
|
|
70
|
+
const recordedManifests = recorded.map((name) => ({
|
|
71
|
+
name,
|
|
72
|
+
manifest: loadManifest(toolkitRoot, name),
|
|
73
|
+
}))
|
|
74
|
+
const recordedCore = recordedManifests
|
|
75
|
+
.filter(({ manifest }) => !isAddOn(manifest))
|
|
76
|
+
.map(({ name }) => name)
|
|
77
|
+
const recordedAddOns = recordedManifests
|
|
78
|
+
.filter(({ manifest }) => isAddOn(manifest))
|
|
79
|
+
.map(({ name }) => name)
|
|
80
|
+
|
|
81
|
+
const core = syncedCore.length > 0 ? syncedCore : recordedCore
|
|
82
|
+
const addOns = [...new Set([...recordedAddOns, ...syncedAddOns])]
|
|
83
|
+
return [...core, ...addOns]
|
|
40
84
|
}
|
|
41
85
|
|
|
42
86
|
/**
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { realpath } from 'node:fs/promises'
|
|
2
|
+
import { belongsTo } from '@/listeners/belongs'
|
|
3
|
+
import type { ListenerReader } from '@/listeners/machine'
|
|
4
|
+
|
|
5
|
+
/** What a stop took, or would take on a dry run, from one worktree. */
|
|
6
|
+
export interface StoppedServer {
|
|
7
|
+
readonly path: string
|
|
8
|
+
readonly pids: readonly number[]
|
|
9
|
+
readonly ports: readonly number[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `unread` is a machine with no way to list sockets, which a reader must tell
|
|
14
|
+
* apart from `read` with nothing stopped, since the first says nothing about
|
|
15
|
+
* what is still listening.
|
|
16
|
+
*/
|
|
17
|
+
export type StopReport =
|
|
18
|
+
| { readonly kind: 'read'; readonly stopped: readonly StoppedServer[] }
|
|
19
|
+
| { readonly kind: 'unread' }
|
|
20
|
+
|
|
21
|
+
export interface StopOptions {
|
|
22
|
+
readonly reader: ListenerReader
|
|
23
|
+
/** Sends SIGTERM and says whether it landed, so a pid that already exited is not counted. */
|
|
24
|
+
readonly kill?: (pid: number) => boolean
|
|
25
|
+
readonly dryRun: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function sigterm(pid: number): boolean {
|
|
29
|
+
try {
|
|
30
|
+
process.kill(pid, 'SIGTERM')
|
|
31
|
+
return true
|
|
32
|
+
} catch {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A process cwd reads back resolved, so a path held through a symlink has to be too. */
|
|
38
|
+
async function resolved(path: string): Promise<string> {
|
|
39
|
+
return realpath(path).catch(() => path)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Stops every server listening from inside one of the worktrees about to be
|
|
44
|
+
* removed, so none outlives the directory it serves. Each path is tested by the
|
|
45
|
+
* nesting rule `canon pr local` uses, so a listener in the main checkout or in
|
|
46
|
+
* a sibling under `.claude/worktrees/` is never signaled. The cwd is read
|
|
47
|
+
* against the given paths and never against `process.cwd()`, which a run from
|
|
48
|
+
* inside another worktree would otherwise make the only match.
|
|
49
|
+
*/
|
|
50
|
+
export async function stopListeners(
|
|
51
|
+
paths: readonly string[],
|
|
52
|
+
opts: StopOptions,
|
|
53
|
+
): Promise<StopReport> {
|
|
54
|
+
const listeners = await opts.reader.listListeners()
|
|
55
|
+
if (listeners === undefined) return { kind: 'unread' }
|
|
56
|
+
|
|
57
|
+
const kill = opts.kill ?? sigterm
|
|
58
|
+
const roots = await Promise.all(paths.map(resolved))
|
|
59
|
+
const cwdByPid = new Map<number, string | undefined>()
|
|
60
|
+
const stopped: StoppedServer[] = []
|
|
61
|
+
|
|
62
|
+
for (const [index, root] of roots.entries()) {
|
|
63
|
+
const pids = new Set<number>()
|
|
64
|
+
const ports = new Set<number>()
|
|
65
|
+
for (const { pid, port } of listeners) {
|
|
66
|
+
if (!cwdByPid.has(pid)) cwdByPid.set(pid, await opts.reader.cwdOf(pid))
|
|
67
|
+
const cwd = cwdByPid.get(pid)
|
|
68
|
+
if (cwd === undefined || !belongsTo(cwd, root)) continue
|
|
69
|
+
pids.add(pid)
|
|
70
|
+
ports.add(port)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const landed = [...pids]
|
|
74
|
+
.sort((a, b) => a - b)
|
|
75
|
+
.filter((pid) => opts.dryRun || kill(pid))
|
|
76
|
+
if (landed.length === 0) continue
|
|
77
|
+
const landedSet = new Set(landed)
|
|
78
|
+
stopped.push({
|
|
79
|
+
path: paths[index] ?? root,
|
|
80
|
+
pids: landed,
|
|
81
|
+
ports: [
|
|
82
|
+
...new Set(
|
|
83
|
+
listeners
|
|
84
|
+
.filter(({ pid, port }) => landedSet.has(pid) && ports.has(port))
|
|
85
|
+
.map(({ port }) => port),
|
|
86
|
+
),
|
|
87
|
+
].sort((a, b) => a - b),
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { kind: 'read', stopped }
|
|
92
|
+
}
|
|
@@ -11,14 +11,18 @@ on:
|
|
|
11
11
|
types: [submitted, edited]
|
|
12
12
|
workflow_dispatch:
|
|
13
13
|
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
14
17
|
jobs:
|
|
15
18
|
phase-label-gate:
|
|
16
19
|
name: 🏷️ Phase Label Gate
|
|
17
20
|
runs-on: ubuntu-latest
|
|
18
|
-
|
|
21
|
+
# Dependabot bodies carry upstream release notes that read as phase labels
|
|
22
|
+
if: github.event.pull_request.user.login != 'dependabot[bot]' && (github.event_name == 'workflow_dispatch' || github.event.pull_request.base.ref == 'main')
|
|
19
23
|
steps:
|
|
20
24
|
- name: Checkout
|
|
21
|
-
uses: actions/checkout@
|
|
25
|
+
uses: actions/checkout@v7
|
|
22
26
|
|
|
23
27
|
- name: Setup Bun
|
|
24
28
|
uses: oven-sh/setup-bun@v2
|
|
@@ -22,38 +22,18 @@ const subjectFirstWordCase = (parsed) => {
|
|
|
22
22
|
]
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const CO_AUTHOR_TRAILER = /^co-authored-by:/i
|
|
26
|
-
const CLAUDE_OR_ANTHROPIC = /\b(claude|anthropic)\b/i
|
|
27
|
-
|
|
28
|
-
// Reads raw rather than footer, since the parser files a lone trailer under
|
|
29
|
-
// body and leaves footer null.
|
|
30
|
-
const noClaudeCoAuthor = (parsed) => {
|
|
31
|
-
const offending = (parsed.raw ?? '')
|
|
32
|
-
.split('\n')
|
|
33
|
-
.find(
|
|
34
|
-
(line) => CO_AUTHOR_TRAILER.test(line) && CLAUDE_OR_ANTHROPIC.test(line),
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
return [
|
|
38
|
-
offending === undefined,
|
|
39
|
-
`remove the co-author trailer crediting Claude or Anthropic: ${offending}`,
|
|
40
|
-
]
|
|
41
|
-
}
|
|
42
|
-
|
|
43
25
|
const config = {
|
|
44
26
|
extends: ['@commitlint/config-conventional'],
|
|
45
27
|
plugins: [
|
|
46
28
|
{
|
|
47
29
|
rules: {
|
|
48
30
|
'subject-first-word-case': subjectFirstWordCase,
|
|
49
|
-
'no-claude-co-author': noClaudeCoAuthor,
|
|
50
31
|
},
|
|
51
32
|
},
|
|
52
33
|
],
|
|
53
34
|
rules: {
|
|
54
35
|
'subject-case': [0],
|
|
55
36
|
'subject-first-word-case': [2, 'always'],
|
|
56
|
-
'no-claude-co-author': [2, 'always'],
|
|
57
37
|
'header-max-length': [2, 'always', 72],
|
|
58
38
|
'scope-case': [2, 'always', 'lower-case'],
|
|
59
39
|
'subject-full-stop': [2, 'never', '.'],
|
|
@@ -17,7 +17,7 @@ Two configs fix how output looks and how commits read.
|
|
|
17
17
|
- `.oxfmtrc.json` sets `semi: false`, `singleQuote: true`, `printWidth: 80`, and `sortPackageJson: false`, keeping the output Prettier wrote.
|
|
18
18
|
- The `astro` stack repeats those options in its own copy, so copy a change here into it.
|
|
19
19
|
- `commitlint.config.js` is an ESM default export extending `@commitlint/config-conventional`. It sets `header-max-length: 72`, `scope-case: lower-case`, and `subject-full-stop: never`.
|
|
20
|
-
- Its `subject-case` rule is off
|
|
20
|
+
- Its `subject-case` rule is off.
|
|
21
21
|
- `.husky/` holds `pre-commit`, `commit-msg`, `pre-push`, `post-merge`, and `post-rewrite`.
|
|
22
22
|
|
|
23
23
|
### Verify runner
|
|
@@ -6,13 +6,16 @@ on:
|
|
|
6
6
|
- main
|
|
7
7
|
workflow_dispatch:
|
|
8
8
|
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
9
12
|
jobs:
|
|
10
13
|
static-checks:
|
|
11
14
|
name: 🛡️ Static Checks
|
|
12
15
|
runs-on: ubuntu-latest
|
|
13
16
|
steps:
|
|
14
17
|
- name: Checkout
|
|
15
|
-
uses: actions/checkout@
|
|
18
|
+
uses: actions/checkout@v7
|
|
16
19
|
|
|
17
20
|
- name: Setup Bun
|
|
18
21
|
uses: oven-sh/setup-bun@v2
|
|
@@ -57,11 +57,11 @@ The `# Claude` group holds three entries, being `.canon/`, `.claude/worktrees/`,
|
|
|
57
57
|
|
|
58
58
|
## CLI
|
|
59
59
|
|
|
60
|
-
| Command | What it does
|
|
61
|
-
| -------------------- |
|
|
62
|
-
| `canon claude init` | Seeds `.claude/` workflow docs, updates `.gitignore`
|
|
63
|
-
| `canon claude sync` | Reconciles `.gitignore` against the manifest. Reports seeded file status.
|
|
64
|
-
| `canon claude setup` | Installs user-level config to `~/.claude/` once per machine: statusline
|
|
60
|
+
| Command | What it does |
|
|
61
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `canon claude init` | Seeds `.claude/` workflow docs, updates `.gitignore` |
|
|
63
|
+
| `canon claude sync` | Reconciles `.gitignore` against the manifest. Reports seeded file status. |
|
|
64
|
+
| `canon claude setup` | Installs user-level config to `~/.claude/` once per machine: statusline and the permissions allow and deny lists, and removes the empty attribution pair an earlier setup wrote. |
|
|
65
65
|
|
|
66
66
|
## CLAUDE.md
|
|
67
67
|
|
|
@@ -85,11 +85,17 @@ The seed at `tooling/claude/seeds/.claude/settings.json` ships hooks that pair w
|
|
|
85
85
|
- A PostToolUse hook pairs with `standards-audit.sh`.
|
|
86
86
|
- Project settings layer over user settings, so a project file carries only what is project-specific.
|
|
87
87
|
|
|
88
|
+
### Session defaults
|
|
89
|
+
|
|
90
|
+
The seed also sets two defaults every session in the target runs under.
|
|
91
|
+
|
|
92
|
+
- `permissions.deny` holds `Agent` and `Task`, so no session spawns a subagent. Parallel work runs as separate background sessions instead.
|
|
93
|
+
- `autoMemoryEnabled` is `false`. A person turns it back on for themselves in `settings.local.json`.
|
|
94
|
+
- `canon migrate session-defaults` adds both to a target seeded before them, since sync never rewrites an existing settings file.
|
|
95
|
+
|
|
88
96
|
### User template
|
|
89
97
|
|
|
90
98
|
The template at `tooling/claude/user/settings.template.json` is installed once per machine by `canon claude setup`.
|
|
91
99
|
|
|
92
|
-
- It sets `attribution.commit` and `attribution.pr` to empty strings to suppress co-author tags.
|
|
93
100
|
- It allows `Bash(bun run *)` under `permissions.allow`.
|
|
94
101
|
- Its `permissions.deny` holds `Read(**/.env)` and `Read(**/.env.*)`, blocking accidental secret exposure across any project.
|
|
95
|
-
- The deprecated `includeCoAuthoredBy` key is not used.
|
|
@@ -36,7 +36,7 @@ jobs:
|
|
|
36
36
|
runs-on: ubuntu-latest
|
|
37
37
|
steps:
|
|
38
38
|
- name: Checkout
|
|
39
|
-
uses: actions/checkout@
|
|
39
|
+
uses: actions/checkout@v7
|
|
40
40
|
|
|
41
41
|
- name: Setup Bun
|
|
42
42
|
uses: oven-sh/setup-bun@v2
|
|
@@ -50,7 +50,7 @@ jobs:
|
|
|
50
50
|
run: bun run build
|
|
51
51
|
|
|
52
52
|
- name: Upload artifact
|
|
53
|
-
uses: actions/upload-artifact@
|
|
53
|
+
uses: actions/upload-artifact@v7
|
|
54
54
|
with:
|
|
55
55
|
name: dist
|
|
56
56
|
path: dist
|
|
@@ -84,7 +84,7 @@ jobs:
|
|
|
84
84
|
# as dead weight breaks the deploy on every push.
|
|
85
85
|
- name: Checkout
|
|
86
86
|
if: steps.secrets-check.outputs.skip == 'false'
|
|
87
|
-
uses: actions/checkout@
|
|
87
|
+
uses: actions/checkout@v7
|
|
88
88
|
|
|
89
89
|
- name: Setup Bun
|
|
90
90
|
if: steps.secrets-check.outputs.skip == 'false'
|
|
@@ -94,7 +94,7 @@ jobs:
|
|
|
94
94
|
|
|
95
95
|
- name: Download artifact
|
|
96
96
|
if: steps.secrets-check.outputs.skip == 'false'
|
|
97
|
-
uses: actions/download-artifact@
|
|
97
|
+
uses: actions/download-artifact@v8
|
|
98
98
|
with:
|
|
99
99
|
name: dist
|
|
100
100
|
path: dist
|
|
@@ -104,7 +104,7 @@ jobs:
|
|
|
104
104
|
- name: Publish
|
|
105
105
|
id: publish
|
|
106
106
|
if: steps.secrets-check.outputs.skip == 'false'
|
|
107
|
-
uses: cloudflare/wrangler-action@
|
|
107
|
+
uses: cloudflare/wrangler-action@v4
|
|
108
108
|
with:
|
|
109
109
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
110
110
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# canon-no-seed: an add-on stack's config with no root counterpart by design.
|
|
2
|
+
version: 2
|
|
3
|
+
updates:
|
|
4
|
+
- package-ecosystem: github-actions
|
|
5
|
+
directory: /
|
|
6
|
+
schedule:
|
|
7
|
+
interval: weekly
|
|
8
|
+
commit-message:
|
|
9
|
+
prefix: ci
|
|
10
|
+
groups:
|
|
11
|
+
actions:
|
|
12
|
+
update-types: [minor, patch]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
# canon-no-seed: an add-on stack's workflow with no root counterpart by design, since the toolkit cuts its own releases through its own workflow.
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release-please:
|
|
14
|
+
name: 🏷️ Release Please
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
permissions:
|
|
17
|
+
contents: write
|
|
18
|
+
pull-requests: write
|
|
19
|
+
# Queues rather than cancels, since a cancel can land after the release exists and before its outputs do
|
|
20
|
+
concurrency:
|
|
21
|
+
group: release-please
|
|
22
|
+
cancel-in-progress: false
|
|
23
|
+
steps:
|
|
24
|
+
- name: Release Please
|
|
25
|
+
uses: googleapis/release-please-action@v5
|
|
26
|
+
with:
|
|
27
|
+
# A personal token lets CI run on the release pull request, which the default token's bot author does not
|
|
28
|
+
token: ${{ secrets.RELEASE_PLEASE_TOKEN || github.token }}
|
|
29
|
+
config-file: release-please-config.json
|
|
30
|
+
manifest-file: .release-please-manifest.json
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Tooling public reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The public stack is an add-on that ships the open-source files a public repository carries. It extends nothing and syncs beside any other stack at the same root with `canon tooling sync public . --write`.
|
|
6
|
+
|
|
7
|
+
## Seeds
|
|
8
|
+
|
|
9
|
+
A seed is copied only when its path is absent, so a project's filled `LICENSE`, `SECURITY.md`, or `CONTRIBUTING.md` is never overwritten. Fill each `<placeholder>` after the first sync.
|
|
10
|
+
|
|
11
|
+
### Policy files
|
|
12
|
+
|
|
13
|
+
These three name the project's owner and terms.
|
|
14
|
+
|
|
15
|
+
- Use MIT for `LICENSE`, which needs a year and a holder. Swap the text by hand for a different license.
|
|
16
|
+
- Point `SECURITY.md` and the issue chooser at GitHub private vulnerability reporting only, with no email.
|
|
17
|
+
- Switch private reporting on under the repository's Settings, Code security.
|
|
18
|
+
- Ship no `CODE_OF_CONDUCT.md`. Add one when outside contributors arrive.
|
|
19
|
+
|
|
20
|
+
### Issue forms
|
|
21
|
+
|
|
22
|
+
`bug.yml`, `feature.yml`, and `config.yml` turn blank issues off.
|
|
23
|
+
|
|
24
|
+
- Add fields specific to the project, such as its CLI's version command, by hand.
|
|
25
|
+
|
|
26
|
+
### Release files
|
|
27
|
+
|
|
28
|
+
These two start release-please at version zero.
|
|
29
|
+
|
|
30
|
+
- Start `release-please-config.json` on the `simple` release type, with `initial-version` `0.0.1` and `bump-minor-pre-major` on.
|
|
31
|
+
- Switch the type and add `extra-files` for a package that carries its version in a manifest.
|
|
32
|
+
- Start `.release-please-manifest.json` at `0.0.0`.
|
|
33
|
+
|
|
34
|
+
## Release workflow
|
|
35
|
+
|
|
36
|
+
`release-please.yml` is a config, so each sync overwrites it and a fix reaches every public repository.
|
|
37
|
+
|
|
38
|
+
- Run it on every push to `main`. It keeps one release pull request open and tags the release when that pull request merges.
|
|
39
|
+
- Publish nothing. A project that publishes a package adds its own workflow keyed on the release, and never edits this one.
|
|
40
|
+
- Open the release pull request with `RELEASE_PLEASE_TOKEN` when the secret exists, and fall back to the default token otherwise.
|
|
41
|
+
- Set the secret to a personal token so CI runs on the release pull request, since the default token's author does not start it.
|
|
42
|
+
- Keep the token read-only at the top, with `contents: write` and `pull-requests: write` on the one job.
|
|
43
|
+
|
|
44
|
+
## Dependabot
|
|
45
|
+
|
|
46
|
+
`dependabot.yml` is how an existing repository keeps its seeded workflows' action majors current, since a seed never updates after install.
|
|
47
|
+
|
|
48
|
+
- Update `github-actions` weekly with an `ci` commit prefix.
|
|
49
|
+
- Group minor and patch updates into one pull request per run, and leave majors ungrouped so each arrives alone.
|
|
50
|
+
|
|
51
|
+
## Beside another stack
|
|
52
|
+
|
|
53
|
+
Run `canon tooling sync public . --write` on a repository that already carries a stack. The install stamp keeps the core chain and lists the add-on after it.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something does not work as expected
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what-happened
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened
|
|
9
|
+
description: What you did, what you expected, and what happened instead.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: steps
|
|
14
|
+
attributes:
|
|
15
|
+
label: Steps to reproduce
|
|
16
|
+
placeholder: |
|
|
17
|
+
1. Run `<command>`
|
|
18
|
+
2. ...
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: reproduction
|
|
23
|
+
attributes:
|
|
24
|
+
label: Minimal reproduction
|
|
25
|
+
description: The smallest input that shows the problem. Paste it, or link a repository or gist.
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
- type: input
|
|
29
|
+
id: version
|
|
30
|
+
attributes:
|
|
31
|
+
label: Version
|
|
32
|
+
description: The release or commit you ran.
|
|
33
|
+
validations:
|
|
34
|
+
required: true
|
|
35
|
+
- type: input
|
|
36
|
+
id: runtime
|
|
37
|
+
attributes:
|
|
38
|
+
label: Runtime and version
|
|
39
|
+
placeholder: Node 22.12.0, or Bun 1.3.0
|
|
40
|
+
validations:
|
|
41
|
+
required: true
|
|
42
|
+
- type: input
|
|
43
|
+
id: os
|
|
44
|
+
attributes:
|
|
45
|
+
label: Operating system
|
|
46
|
+
placeholder: macOS 26.1, Windows 11, Ubuntu 24.04
|
|
47
|
+
validations:
|
|
48
|
+
required: true
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest something this project should do
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: job
|
|
7
|
+
attributes:
|
|
8
|
+
label: The job
|
|
9
|
+
description: What you were trying to get done when the project fell short, and how you work around it today.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: proposal
|
|
14
|
+
attributes:
|
|
15
|
+
label: What you would like
|
|
16
|
+
description: The behavior you have in mind, if you have one.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Contributing to <project>
|
|
2
|
+
|
|
3
|
+
This page covers getting a working checkout, how to test a change, and the commit and pull request rules the hooks enforce.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
<Prerequisites, such as the runtime and its minimum version.>
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/<owner>/<repo>.git
|
|
11
|
+
cd <repo>
|
|
12
|
+
<install command>
|
|
13
|
+
<check command>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
<What the install step activates, such as git hooks, and what the check command runs.>
|
|
17
|
+
|
|
18
|
+
## Layout
|
|
19
|
+
|
|
20
|
+
| Path | What it holds |
|
|
21
|
+
| -------- | ------------- |
|
|
22
|
+
| `<path>` | <role> |
|
|
23
|
+
|
|
24
|
+
## Tests
|
|
25
|
+
|
|
26
|
+
<The commands that run the tests and what each one covers.>
|
|
27
|
+
|
|
28
|
+
## Commit messages
|
|
29
|
+
|
|
30
|
+
Commits follow [Conventional Commits](https://www.conventionalcommits.org), and release-please reads them to cut each release.
|
|
31
|
+
|
|
32
|
+
- The header reads `<type>(<scope>): <subject>`, such as `fix(server): close the socket on shutdown`
|
|
33
|
+
- The header is at most 72 characters
|
|
34
|
+
- The first word of the subject is lowercase, and the subject ends with no period
|
|
35
|
+
|
|
36
|
+
The types are `feat`, `fix`, `refactor`, `docs`, `chore`, `perf`, `test`, `style`, `build`, `ci`, and `revert`.
|
|
37
|
+
|
|
38
|
+
## Pull requests
|
|
39
|
+
|
|
40
|
+
Branch from `main` and open the pull request against it. Say what the change does and why, which files it touches, and how you tested it.
|
|
41
|
+
|
|
42
|
+
CI runs on every pull request, and a pull request merges once it passes. Keep each one to a single concern. A refactor that a feature needs reads better as its own pull request ahead of the feature.
|
|
43
|
+
|
|
44
|
+
## Releases
|
|
45
|
+
|
|
46
|
+
release-please cuts every release from the conventional commits on `main`. Each push to `main` keeps one release pull request open, holding the next version and its `CHANGELOG.md` entry. Merging that pull request tags the release.
|
|
47
|
+
|
|
48
|
+
## AI-assisted contributions
|
|
49
|
+
|
|
50
|
+
Contributions written with a coding agent are welcome. You are the author of what you submit, so read every line before you open the pull request and be ready to explain it.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) <year> <holder>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|