@erclx/aitk 0.22.1 → 0.24.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.
Files changed (37) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/claude-roadmap/references/roadmap.md +12 -2
  3. package/claude/skills/claude-standards-audit/SKILL.md +3 -3
  4. package/claude/skills/create-standard/references/snippets.md +9 -0
  5. package/claude/skills/git-branch/references/branch.md +12 -1
  6. package/claude/skills/git-commit/references/commit.md +11 -1
  7. package/claude/skills/git-issue/references/issue.md +10 -0
  8. package/claude/skills/git-pr/references/branch.md +12 -1
  9. package/claude/skills/git-pr/references/pr.md +12 -1
  10. package/claude/skills/git-split/references/branch.md +12 -1
  11. package/claude/skills/git-split/references/pr.md +12 -1
  12. package/claude/skills/git-stage/references/commit.md +11 -1
  13. package/docs/agents.md +10 -5
  14. package/package.json +1 -1
  15. package/src/commands/sandbox.ts +99 -5
  16. package/src/sandbox/census.ts +228 -0
  17. package/standards/architecture.md +10 -2
  18. package/standards/bundled/branch.md +12 -1
  19. package/standards/bundled/commit.md +11 -1
  20. package/standards/bundled/issue.md +10 -0
  21. package/standards/bundled/pr.md +12 -1
  22. package/standards/bundled/roadmap.md +12 -2
  23. package/standards/bundled/snippets.md +9 -0
  24. package/standards/context.md +11 -0
  25. package/standards/design.md +9 -1
  26. package/standards/diagrams.md +13 -2
  27. package/standards/prose.md +11 -0
  28. package/standards/readme.md +9 -0
  29. package/standards/requirements.md +10 -1
  30. package/standards/rule.md +10 -0
  31. package/standards/skill.md +11 -0
  32. package/standards/standard.md +24 -4
  33. package/standards/tasks.md +11 -0
  34. package/standards/versioning.md +11 -0
  35. package/standards/wireframes.md +11 -1
  36. package/claude/skills/claude-standards-audit/references/branch.md +0 -49
  37. package/claude/skills/claude-standards-audit/references/pr.md +0 -124
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aitk",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "0.22.1",
4
+ "version": "0.24.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -10,6 +10,17 @@ Applies to `.claude/ROADMAP.md`. Sequences the scope from `.claude/REQUIREMENTS.
10
10
 
11
11
  The roadmap is one scannable table. That is what keeps it distinct from `.claude/tasks/`: the roadmap is an overview read at a glance, while tasks are worked one file at a time.
12
12
 
13
+ ## Scope
14
+
15
+ Governs the sequencing document at `.claude/ROADMAP.md`: the version table, its columns, and its lifecycle.
16
+
17
+ Does not govern:
18
+
19
+ - What the scope is, which the roadmap sequences rather than defines: `requirements.md`
20
+ - Task files, outcomes, and board state: `tasks.md`
21
+ - Phase-label format and semver discipline: `versioning.md`
22
+ - Rationale for a technical choice: `architecture.md`
23
+
13
24
  ## What goes in
14
25
 
15
26
  - One row per version, ordered top to bottom by sequence
@@ -20,9 +31,8 @@ The roadmap is one scannable table. That is what keeps it distinct from `.claude
20
31
 
21
32
  ## What does not go in
22
33
 
23
- - Task breakdown, checkboxes, or per-feature file lists. Those live in `.claude/tasks/` and `.claude/plans/`.
34
+ - Task breakdown, checkboxes, or per-feature file lists
24
35
  - Implementation detail, API names, or component references
25
- - Rationale for tech choices. That belongs in `.claude/ARCHITECTURE.md`.
26
36
  - Sentence-long cells. Keep each cell terse so the table stays scannable.
27
37
 
28
38
  ## Format
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: claude-standards-audit
3
- description: Audits changed markdown files against applicable authoring standards (prose, skill, readme, branch, pr) and reports violations without fixing. Maps each file to its standards, greps for banned tokens, and groups findings by file. Use when asked to "audit prose", "audit standards", "check standards", "standards audit", or after editing markdown where standards compliance matters. Do NOT fix violations. Reporting only.
3
+ description: Audits changed markdown files against applicable authoring standards (prose, skill, readme) and reports violations without fixing. Maps each file to its standards, greps for banned tokens, and groups findings by file. Use when asked to "audit prose", "audit standards", "check standards", "standards audit", or after editing markdown where standards compliance matters. Do NOT fix violations. Reporting only.
4
4
  ---
5
5
 
6
6
  # Claude standards audit
@@ -44,11 +44,11 @@ For each changed markdown file, pick the applicable standards:
44
44
  - Any markdown with prose: `.claude/standards/prose.md`
45
45
  - `SKILL.md` under `.claude/skills/` or `claude/skills/`: also `.claude/standards/skill.md`
46
46
  - `README.md` at any level: also `.claude/standards/readme.md`
47
- - Branch names proposed in the session: `${CLAUDE_SKILL_DIR}/references/branch.md`
48
- - PR titles or bodies drafted in the session: `${CLAUDE_SKILL_DIR}/references/pr.md`
49
47
 
50
48
  Read a standard from `${CLAUDE_SKILL_DIR}/../../standards/` instead when the project does not have it.
51
49
 
50
+ Every mapping names a changed markdown file, which is the only thing Step 1 produces. Text that never lands in the tree, such as a branch name or a pull request body, is checked by the skill that publishes it rather than here.
51
+
52
52
  ## Step 3: read standards and audit
53
53
 
54
54
  Read each applicable standard once. For each changed file, audit against every rule:
@@ -6,6 +6,15 @@ consumers: create-standard
6
6
 
7
7
  # Snippet reference
8
8
 
9
+ ## Scope
10
+
11
+ Governs a snippet file: what one is for, how it is invoked, and the structure of its body.
12
+
13
+ Does not govern:
14
+
15
+ - Skill folders, which carry frontmatter, references, and scripts a snippet has none of: `skill.md`
16
+ - Voice, punctuation, and formatting in snippet prose: `prose.md`
17
+
9
18
  ## What a snippet is
10
19
 
11
20
  A snippet is a short, focused prompt stored as a plain markdown file. Invoke one to insert a prepared instruction into any AI chat without retyping it. Each snippet covers one purpose. If a prompt needs headers or multiple goals, use a system prompt instead.
@@ -1,11 +1,22 @@
1
1
  ---
2
2
  title: Branch reference
3
3
  description: Branch naming format and type conventions
4
- consumers: git-branch, git-split, git-pr, claude-standards-audit
4
+ consumers: git-branch, git-split, git-pr
5
5
  ---
6
6
 
7
7
  # Branch reference
8
8
 
9
+ ## Scope
10
+
11
+ Governs a git branch name: its structure, its length, and the type vocabulary it draws from.
12
+
13
+ Does not govern:
14
+
15
+ - Commit subject format, which shares the type vocabulary: `commit.md`
16
+ - Pull request title and body: `pr.md`
17
+ - Whether a phase label may appear in a branch name: `versioning.md`
18
+ - Deriving an output filename from a branch name, which is a transform the skill running it owns
19
+
9
20
  ## Format
10
21
 
11
22
  - Structure: `<type>/<description>` or `<type>/<ticket>-<description>`
@@ -6,6 +6,16 @@ consumers: git-commit, git-stage
6
6
 
7
7
  # Commit message reference
8
8
 
9
+ ## Scope
10
+
11
+ Governs a git commit message: subject structure, the type and scope vocabulary, and the body.
12
+
13
+ Does not govern:
14
+
15
+ - Branch naming, which shares the type vocabulary: `branch.md`
16
+ - Pull request title and body, which share the subject form: `pr.md`
17
+ - Whether a phase label or a semver tag may appear in a subject: `versioning.md`
18
+
9
19
  ## Format
10
20
 
11
21
  - Structure: `<type>(<scope>): <subject>`
@@ -26,7 +36,7 @@ consumers: git-commit, git-stage
26
36
  - `ci`: CI/CD pipeline changes (GitHub Actions)
27
37
  - `revert`: revert a previous commit
28
38
 
29
- ## Scope
39
+ ## Scope vocabulary
30
40
 
31
41
  - Single lowercase word representing a system component
32
42
  - Prefer single word
@@ -6,6 +6,16 @@ consumers: git-issue
6
6
 
7
7
  # Issue reference
8
8
 
9
+ ## Scope
10
+
11
+ Governs a tracker issue: its title, its labels, and the sections its body carries.
12
+
13
+ Does not govern:
14
+
15
+ - Pull request title and body: `pr.md`
16
+ - Whether a phase label may appear in issue text: `versioning.md`
17
+ - Voice, punctuation, and banned words in issue prose: `prose.md`
18
+
9
19
  ## Title
10
20
 
11
21
  - Format: `<type>: <subject>`
@@ -1,11 +1,22 @@
1
1
  ---
2
2
  title: Branch reference
3
3
  description: Branch naming format and type conventions
4
- consumers: git-branch, git-split, git-pr, claude-standards-audit
4
+ consumers: git-branch, git-split, git-pr
5
5
  ---
6
6
 
7
7
  # Branch reference
8
8
 
9
+ ## Scope
10
+
11
+ Governs a git branch name: its structure, its length, and the type vocabulary it draws from.
12
+
13
+ Does not govern:
14
+
15
+ - Commit subject format, which shares the type vocabulary: `commit.md`
16
+ - Pull request title and body: `pr.md`
17
+ - Whether a phase label may appear in a branch name: `versioning.md`
18
+ - Deriving an output filename from a branch name, which is a transform the skill running it owns
19
+
9
20
  ## Format
10
21
 
11
22
  - Structure: `<type>/<description>` or `<type>/<ticket>-<description>`
@@ -1,11 +1,22 @@
1
1
  ---
2
2
  title: Pull request reference
3
3
  description: Pull request title and body conventions
4
- consumers: git-split, git-pr, claude-standards-audit
4
+ consumers: git-split, git-pr
5
5
  ---
6
6
 
7
7
  # Pull request reference
8
8
 
9
+ ## Scope
10
+
11
+ Governs a pull request title and body: their format and the sections the body carries.
12
+
13
+ Does not govern:
14
+
15
+ - Commit subject format, which shares the title form: `commit.md`
16
+ - Branch naming: `branch.md`
17
+ - Whether a phase label or a semver tag may appear in a title or body: `versioning.md`
18
+ - Voice, punctuation, and banned words in pull request prose: `prose.md`
19
+
9
20
  ## Title
10
21
 
11
22
  - Format: `<type>(<scope>): <subject>`
@@ -1,11 +1,22 @@
1
1
  ---
2
2
  title: Branch reference
3
3
  description: Branch naming format and type conventions
4
- consumers: git-branch, git-split, git-pr, claude-standards-audit
4
+ consumers: git-branch, git-split, git-pr
5
5
  ---
6
6
 
7
7
  # Branch reference
8
8
 
9
+ ## Scope
10
+
11
+ Governs a git branch name: its structure, its length, and the type vocabulary it draws from.
12
+
13
+ Does not govern:
14
+
15
+ - Commit subject format, which shares the type vocabulary: `commit.md`
16
+ - Pull request title and body: `pr.md`
17
+ - Whether a phase label may appear in a branch name: `versioning.md`
18
+ - Deriving an output filename from a branch name, which is a transform the skill running it owns
19
+
9
20
  ## Format
10
21
 
11
22
  - Structure: `<type>/<description>` or `<type>/<ticket>-<description>`
@@ -1,11 +1,22 @@
1
1
  ---
2
2
  title: Pull request reference
3
3
  description: Pull request title and body conventions
4
- consumers: git-split, git-pr, claude-standards-audit
4
+ consumers: git-split, git-pr
5
5
  ---
6
6
 
7
7
  # Pull request reference
8
8
 
9
+ ## Scope
10
+
11
+ Governs a pull request title and body: their format and the sections the body carries.
12
+
13
+ Does not govern:
14
+
15
+ - Commit subject format, which shares the title form: `commit.md`
16
+ - Branch naming: `branch.md`
17
+ - Whether a phase label or a semver tag may appear in a title or body: `versioning.md`
18
+ - Voice, punctuation, and banned words in pull request prose: `prose.md`
19
+
9
20
  ## Title
10
21
 
11
22
  - Format: `<type>(<scope>): <subject>`
@@ -6,6 +6,16 @@ consumers: git-commit, git-stage
6
6
 
7
7
  # Commit message reference
8
8
 
9
+ ## Scope
10
+
11
+ Governs a git commit message: subject structure, the type and scope vocabulary, and the body.
12
+
13
+ Does not govern:
14
+
15
+ - Branch naming, which shares the type vocabulary: `branch.md`
16
+ - Pull request title and body, which share the subject form: `pr.md`
17
+ - Whether a phase label or a semver tag may appear in a subject: `versioning.md`
18
+
9
19
  ## Format
10
20
 
11
21
  - Structure: `<type>(<scope>): <subject>`
@@ -26,7 +36,7 @@ consumers: git-commit, git-stage
26
36
  - `ci`: CI/CD pipeline changes (GitHub Actions)
27
37
  - `revert`: revert a previous commit
28
38
 
29
- ## Scope
39
+ ## Scope vocabulary
30
40
 
31
41
  - Single lowercase word representing a system component
32
42
  - Prefer single word
package/docs/agents.md CHANGED
@@ -70,7 +70,7 @@ Full help: `aitk <command> --help`.
70
70
  | `aitk sandbox reset` | Reset sandbox to baseline |
71
71
  | `aitk sandbox clean` | Wipe the sandbox |
72
72
  | `aitk sandbox check` | Score a provisioned sandbox against a scenario expectation (`--json` for the verdict) |
73
- | `aitk sandbox coverage` | Report which scenarios declare expectations (`--json`, `--strict`) |
73
+ | `aitk sandbox coverage` | Report which scenarios declare expectations (`--json`, `--strict`, `--skills`) |
74
74
  | `aitk indexes regen` | Regenerate `index.md` files from sibling frontmatter |
75
75
  | `aitk docs [topic]` | Emit toolkit reference docs (`list`, or a topic by name) |
76
76
  | `aitk design render` | Render `.claude/DESIGN.md` tokens to HTML and CSS |
@@ -228,13 +228,18 @@ Exit 0 means `pass` or `unchecked`. Exit 1 means `fail`, or a caller error: a ma
228
228
  aitk sandbox coverage --json
229
229
  ```
230
230
 
231
- | Flag | Effect |
232
- | ---------- | ------------------------------------------------- |
233
- | `--json` | Emit the coverage record on stdout |
234
- | `--strict` | Exit 1 while any scenario declares no expectation |
231
+ | Flag | Effect |
232
+ | ---------- | ----------------------------------------------------------- |
233
+ | `--json` | Emit the coverage record on stdout |
234
+ | `--strict` | Exit 1 while any scenario declares no expectation |
235
+ | `--skills` | Add a per-skill asserted, should-be-asserted, exempt census |
235
236
 
236
237
  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.
237
238
 
239
+ `--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.
240
+
241
+ A skill pairs to a scenario by filename, `<category>-<command>` first and bare `<command>` second, so `claude/setup-init.sh` reaches the `setup-init` skill. `should-be-asserted` is the default and is the work queue for writing arms. `exempt` means no arm should be written and holds only with a reason, declared in `scripts/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.
242
+
238
243
  `scripts/sandbox/run.sh` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.claude/.tmp/sandbox-runs/<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. Stdout carries the same bytes it did before the record existed.
239
244
 
240
245
  ## Docs
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "0.22.1",
4
+ "version": "0.24.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -3,6 +3,11 @@ import { homedir } from 'node:os'
3
3
  import { join } from 'node:path'
4
4
  import type { Command } from 'commander'
5
5
  import { PROJECT_ROOT, execScript } from '@/exec'
6
+ import {
7
+ assertedPercent,
8
+ collectCensus,
9
+ type CensusReport,
10
+ } from '@/sandbox/census'
6
11
  import {
7
12
  collectCoverage,
8
13
  coveragePercent,
@@ -100,6 +105,7 @@ interface CheckOptions {
100
105
  interface CoverageOptions {
101
106
  readonly json?: boolean
102
107
  readonly strict?: boolean
108
+ readonly skills?: boolean
103
109
  }
104
110
 
105
111
  function getCategories(): string[] {
@@ -245,20 +251,105 @@ function reportCoverage(report: CoverageReport): void {
245
251
  else logInfo(summary)
246
252
  }
247
253
 
254
+ /**
255
+ * Names the skills nothing can fail, which is the work queue the arm batches
256
+ * consume. Exempt entries print their reason, since an exemption with no reason
257
+ * beside it is indistinguishable from a skill nobody has got to yet.
258
+ */
259
+ function reportCensus(report: CensusReport): void {
260
+ const shouldBe = report.skills.filter(
261
+ (s) => s.verdict === 'should-be-asserted',
262
+ )
263
+ const exempt = report.skills.filter((s) => s.verdict === 'exempt')
264
+
265
+ if (shouldBe.length > 0) {
266
+ logStep('No arm can fail these')
267
+ for (const entry of shouldBe)
268
+ logWarn(
269
+ `${entry.skill} (${entry.scenarios.length === 0 ? 'no scenario' : entry.scenarios.join(', ')})`,
270
+ )
271
+ }
272
+
273
+ if (exempt.length > 0) {
274
+ logStep('Exempt')
275
+ for (const entry of exempt) logInfo(`${entry.skill}: ${entry.reason}`)
276
+ }
277
+
278
+ // A wrong exemption is the one way this report can overstate itself, so both
279
+ // kinds print as errors even on a run that is otherwise clean. They separate
280
+ // because the reader checks a different thing: whether the skill left the
281
+ // tree, or whether an arm landed and the entry outlived its reason.
282
+ if (report.staleExemptions.length > 0) {
283
+ logStep('Exemptions naming no shipped skill')
284
+ for (const skill of report.staleExemptions) logError(skill)
285
+ }
286
+
287
+ if (report.supersededExemptions.length > 0) {
288
+ logStep('Exemptions an arm now asserts')
289
+ for (const skill of report.supersededExemptions)
290
+ logError(`${skill}, delete the entry`)
291
+ }
292
+
293
+ logStep('Skills')
294
+ const summary =
295
+ `${report.asserted}/${report.totalSkills} skills asserted ` +
296
+ `(${assertedPercent(report)}%), ${report.shouldBeAsserted} should be, ` +
297
+ `${report.exempt} exempt`
298
+ if (report.shouldBeAsserted > 0) logWarn(summary)
299
+ else logInfo(summary)
300
+ }
301
+
248
302
  function runCoverage(options: CoverageOptions): void {
249
303
  if (reportAbsentScenarioTree()) return
250
304
 
251
305
  intro('aitk sandbox coverage')
252
306
 
253
307
  const report = collectCoverage(PROJECT_ROOT)
308
+
309
+ // A broken `exempt.toml` reads as a caller error, not as a crash. The parser
310
+ // throws rather than returning a smaller set, since a dropped exemption is
311
+ // invisible in the counts, and the conversion belongs here for the reason
312
+ // `resolveVerdict` catches its own parse: a typo in a declaration should read
313
+ // the way a pattern that does not compile does rather than as a stack trace.
314
+ let census: CensusReport | undefined
315
+ if (options.skills === true) {
316
+ try {
317
+ census = collectCensus(PROJECT_ROOT, report)
318
+ } catch (error) {
319
+ const reason = error instanceof Error ? error.message : String(error)
320
+ reportCoverage(report)
321
+ logStep('Skills')
322
+ logError(`Cannot read the exemptions: ${reason}`)
323
+ outro()
324
+ process.exitCode = 1
325
+
326
+ return
327
+ }
328
+ }
329
+
330
+ // The scenario view stays whichever way `--skills` is set. The two count
331
+ // different denominators, and replacing one with the other loses the scenario
332
+ // rollout the strict gate is written against.
254
333
  reportCoverage(report)
255
- if (options.json === true) process.stdout.write(`${JSON.stringify(report)}\n`)
334
+ if (census !== undefined) reportCensus(census)
335
+
336
+ if (options.json === true)
337
+ process.stdout.write(
338
+ `${JSON.stringify(census === undefined ? report : { ...report, ...census })}\n`,
339
+ )
256
340
 
257
341
  outro()
258
- process.exitCode =
342
+
343
+ // A wrong exemption exits non-zero without `--strict`. It is a wrong claim in
344
+ // committed data rather than a rollout still in progress, and the whole point
345
+ // of the verdict is that an exemption someone can no longer check is worse
346
+ // than no exemption at all.
347
+ const wrongExemption =
348
+ (census?.staleExemptions.length ?? 0) > 0 ||
349
+ (census?.supersededExemptions.length ?? 0) > 0
350
+ const rolloutIncomplete =
259
351
  options.strict === true && report.armedScenarios < report.totalScenarios
260
- ? 1
261
- : 0
352
+ process.exitCode = wrongExemption || rolloutIncomplete ? 1 : 0
262
353
  }
263
354
 
264
355
  function runCheck(
@@ -361,6 +452,7 @@ export function register(program: Command): void {
361
452
  .helpOption('-h, --help', 'Show this help message')
362
453
  .option('--json', 'Emit the report as JSON on stdout')
363
454
  .option('--strict', 'Exit non-zero while any scenario declares nothing')
455
+ .option('--skills', 'Add a per-skill asserted, should-be, or exempt census')
364
456
  .addHelpText(
365
457
  'after',
366
458
  [
@@ -368,8 +460,10 @@ export function register(program: Command): void {
368
460
  'Examples:',
369
461
  ' aitk sandbox coverage',
370
462
  ' aitk sandbox coverage --json',
463
+ ' aitk sandbox coverage --skills',
371
464
  '',
372
- 'Exit codes: 0, unless --strict and a scenario declares nothing.',
465
+ 'Exit codes: 0, unless --strict and a scenario declares nothing, or',
466
+ '--skills and an exemption names no shipped skill or one an arm asserts.',
373
467
  'Where the scenario tree does not ship, exits 1 without a report.',
374
468
  ].join('\n'),
375
469
  )