@erclx/aitk 3.35.0 โ†’ 3.36.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aitk",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "3.35.0",
4
+ "version": "3.36.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -61,7 +61,7 @@ Two ban shapes stay unmeasured and the report says so on every run. A multi-word
61
61
 
62
62
  ### Bullets, paragraphs, and depth
63
63
 
64
- Bullet weight and depth are the checks that moved off `aitk context audit`, carrying what they measured at the time. A top-level bullet reports past roughly 400 characters with continuation lines folded in and nested items left out. A run reports past roughly 40 rendered lines, measured at 80 columns, where a heading breaks one and so does a bold section marker taking the whole line at column zero and ending in a colon, skipping fenced blocks and exempting a flat peer list averaging under 130 characters a bullet and a run that is entirely table rows. Each file reports its longest run alone, so a second run past the checkpoint in the same file is never named.
64
+ Bullet weight and depth are the checks that moved off `aitk context audit`, carrying what they measured at the time. A top-level bullet reports past roughly 400 characters with continuation lines folded in and nested items left out. A run reports past roughly 40 rendered lines, measured at 80 columns, where a heading breaks one and so does a bold section marker taking the whole line at column zero, either ending in a colon, or holding one whole code span at any width, or running to 20 characters or fewer, skipping fenced blocks and exempting a flat peer list averaging under 130 characters a bullet and a run that is entirely table rows. Each file reports its longest run alone, so a second run past the checkpoint in the same file is never named.
65
65
 
66
66
  Every weight and depth measure counts the text a reader is shown. A link reduces to its anchor text and an autolink drops whole, since no reader is shown either destination. A backticked path stays counted, which is where these measures part from the ban scan above: that one blanks a code span so a standard quoting its own banned character does not report itself, and discounting the same span here would under-report a paragraph carrying several. One file holds both span sets and each answers its own question.
67
67
 
@@ -136,7 +136,7 @@ Measured at `4b7b13a2` across 444 files with the paragraph checkpoint at 600: 8
136
136
 
137
137
  The ban half reached zero, which was the precondition the gate waited on, and it was re-measured against the same corpus at the moment the gate landed. Three sweep slices then took the structural half, and the corpus reports 1 heavy bullet and 22 heavy paragraphs across 6 files on 2026-08-06, every one on a ground the triage record holds as exempt.
138
138
 
139
- Depth is the one measure no slice triaged, so its figure moved with the sweep rather than with the corpus. The break rule widened to bold section markers on 2026-08-28, which drops the count again with no edit to any file, so the 41 above reads against a rule the command no longer runs. Read all four structural counts from a run rather than from this paragraph.
139
+ Depth is the one measure no slice triaged, so its figure moved with the sweep rather than with the corpus. The break rule widened to bold section markers on 2026-08-28 and again to the colon-less ones the same day, and each drop came with no edit to any file, so the 41 above reads against a rule the command no longer runs. Read all four structural counts from a run rather than from this paragraph.
140
140
 
141
141
  ### What a hit asks of an author
142
142
 
@@ -0,0 +1,44 @@
1
+ ---
2
+ description: Enforce GitHub Actions job naming, granularity, dependency wiring, and bun pinning
3
+ paths:
4
+ - '.github/workflows/**'
5
+ ---
6
+
7
+ # CI workflow standards
8
+
9
+ ## Triggers and pinning
10
+
11
+ - Include `workflow_dispatch` alongside the primary trigger on every workflow.
12
+ - Pin every action to a major version tag. Never `@latest` or `@main`.
13
+ - Run every job on `ubuntu-latest`.
14
+
15
+ ## Job naming
16
+
17
+ - Name every job as an emoji followed by a title, such as `๐Ÿ›ก๏ธ Checks`, `๐Ÿงช Unit Tests`, `๐Ÿ“ฆ Build Check`, `๐ŸŽญ E2E Tests`, or `๐Ÿš€ Deploy`.
18
+
19
+ ## Job granularity
20
+
21
+ - Fold static analysis, unit tests, and build into one job while the gate runs under two minutes end to end.
22
+ - Split them into parallel jobs once a run log puts the gate past two minutes.
23
+ - Give E2E, release, and deploy a job each from the start.
24
+
25
+ ## Job dependencies
26
+
27
+ - Use `needs` for a data dependency, where a job consumes another job's artifact, or for a job whose cost is prohibitive against the gate in front of it. Leave every other job unwired so it runs in parallel.
28
+ - Gate E2E on the job that uploads the build artifact.
29
+ - Gate release and deploy on E2E.
30
+ - Emit a deploy, publish, or release job with a placeholder step and name what the caller fills in. Never guess a deploy command.
31
+
32
+ ## Artifacts
33
+
34
+ - Upload an artifact on `if: failure()` alone, with `retention-days: 7`.
35
+
36
+ ## Bun stack
37
+
38
+ - Use `oven-sh/setup-bun@v2` with `bun-version: latest`.
39
+ - Install with `bun install --frozen-lockfile`.
40
+ - Key the Playwright browser cache on the Playwright version string, never a static key.
41
+
42
+ ## Authority
43
+
44
+ - Load the `aitk:ci-workflow` skill for the workflow template and the per-project adaptation. Report it rather than proceeding silently when the skill does not resolve.
@@ -14,4 +14,5 @@ description: Enforce planning standards before implementation
14
14
  - Write or update tests as part of every implementation plan.
15
15
  - Write the test for a behavior before the code that implements it. Confirm visual output after implementing it, not before.
16
16
  - Run `aitk gov test-order` before shipping a branch. Fix what it names as reaching history ahead of its test.
17
+ - Load the `aitk:systematic-debugging` skill before proposing a fix for a failing test, a surfaced bug, or behavior nobody has explained yet, and report it rather than proceeding silently when the skill does not resolve.
17
18
  - Do not modify code without a confirmed plan.
@@ -1,6 +1,8 @@
1
1
  extends = ""
2
- # All three folders whole. Every rule authored under them ships to a base
2
+ # All four folders whole. Every rule authored under them ships to a base
3
3
  # consumer, so adding one is a single edit rather than a file and a name here.
4
4
  # `snippets` carries the `@`-reference convention rule, which lost its only
5
- # other delivery path when `aitk snippets install` retired.
6
- rules = ["core", "claude", "snippets", "120-bash"]
5
+ # other delivery path when `aitk snippets install` retired. `ci` carries the
6
+ # workflow rule, whose glob is the only inbound route to the `ci-workflow`
7
+ # skill.
8
+ rules = ["core", "claude", "snippets", "ci", "120-bash"]
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "3.35.0",
4
+ "version": "3.36.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -520,7 +520,7 @@ function reportDepth(
520
520
  'A run that is entirely table rows is excluded too, since a heading inside a table splits the table rather than the run.',
521
521
  )
522
522
  logInfo(
523
- 'A heading breaks a run and so does a bold section marker at column zero ending in a colon, such as a line reading only Risks in bold. A colon-less or indented one stays prose.',
523
+ 'A heading breaks a run and so does a bold section marker at column zero, such as a line reading only Risks in bold. A colon-less one breaks when it holds one whole code span, or when it runs to 20 characters or fewer, and an indented one stays prose.',
524
524
  )
525
525
 
526
526
  const over = reports
@@ -19,19 +19,51 @@ const HEADING = /^#{1,6}\s/
19
19
  * headings was the alternative and it clears no plan already written, leaving
20
20
  * each flagged until someone rewrites it.
21
21
  *
22
- * The marker starts at column zero and carries a colon, and the whole line is
23
- * the marker or none of it is. A bold phrase opening a sentence is emphasis
24
- * rather than a seam, and an indented one is a label inside a list item, so
25
- * both stay prose. This ships as package data every project reads, where a
26
- * missed break costs one unbroken run and a false one shortens every run around
27
- * it until the measure stops reporting, which is the dearer of the two.
28
- *
29
- * The colon is what a colon-less `**Testing**` is held out by, and that shape
30
- * is a real section marker in a review body rather than a hypothetical. Widening
31
- * to reach it moves the shipped pattern rather than the wording, so the rule
32
- * states the colon and the widening stays open for a decision of its own.
22
+ * The marker starts at column zero and the whole line is the marker or none of
23
+ * it is. A bold phrase opening a sentence is emphasis rather than a seam, and
24
+ * an indented one is a label inside a list item, so both stay prose. This ships
25
+ * as package data every project reads, where a missed break costs one unbroken
26
+ * run and a false one shortens every run around it until the measure stops
27
+ * reporting, which is the dearer of the two.
28
+ *
29
+ * A colon ends most markers and not all of them. `claude-pr-review` writes
30
+ * three colon-less ones into every body it posts and a bold path heading for
31
+ * each file it reviews, so requiring the colon held a real seam out. Two
32
+ * signals stand in where the colon is absent, because the shape a marker has to
33
+ * be told from is a sentence set in bold and no one test separates both kinds
34
+ * of marker from it.
35
+ *
36
+ * A label that is one whole code span breaks at any width. A path runs long and
37
+ * a sentence set in bold is never a single span, so shape settles this half
38
+ * where width cannot. Across 81 colon-less markers in the review bodies posted
39
+ * on this repository, the 30 path headings run from 20 to 70 characters, and no
40
+ * colon-less line in the records tree is a whole span at all, so the rule adds
41
+ * reach without adding a false break.
42
+ *
43
+ * `MARKER_WIDTH` covers the rest and is read off the corpus rather than picked.
44
+ * Of the remaining 51 markers in those review bodies, 50 sit at or under 20
45
+ * visible characters, and so do 48 of the 94 colon-less lines in the records
46
+ * tree at `9960a4d7`, every one of them a label. The 21 to 30 band above it
47
+ * holds 8 lines nobody can classify on sight, where `One change across four
48
+ * files.` reads as a sentence and `Rule plus a mechanical half` reads as a
49
+ * seam, so the ceiling sits under that band rather than over it. That is where
50
+ * the asymmetry above points, a false break being the dearer error. Terminal
51
+ * punctuation separates nothing, since 45 of the 48 shortest colon-less lines
52
+ * end in one.
53
+ *
54
+ * Both signals govern the colon-less shape alone. A colon is its own evidence
55
+ * of a label, and capping the colon form as well takes the break back from four
56
+ * markers between 31 and 50 characters that already have it, every one of them
57
+ * a genuine section marker.
58
+ *
59
+ * One marker in that review corpus is reached by neither signal, a 48-character
60
+ * heading a session wrote by hand rather than from the template. Widening to
61
+ * catch it means raising the ceiling back through the band, so it is left as
62
+ * the cheap error the asymmetry above already names.
33
63
  */
34
- const SECTION_MARKER = /^\*\*[^*]+:\*\*\s*$/
64
+ const BOLD_LINE = /^\*\*([^*]+)\*\*\s*$/
65
+ const SPAN_LABEL = /^`[^`]+`$/
66
+ const MARKER_WIDTH = 20
35
67
 
36
68
  const LIST_ITEM = /^(\s*)([-*+]|\d+\.)\s+/
37
69
  const TABLE_ROW = /^\s*\|/
@@ -272,11 +304,33 @@ function isTableRun(run: readonly BodyLine[]): boolean {
272
304
  return separators > 0
273
305
  }
274
306
 
307
+ /**
308
+ * Reports whether a line is a section marker rather than emphasis.
309
+ *
310
+ * `BOLD_LINE` fixes the shape, and a colon-less label then answers to
311
+ * `SPAN_LABEL` or to `MARKER_WIDTH`, per the record above. Both read visible
312
+ * text, which reduces a link to its anchor text and leaves a backticked path
313
+ * counted whole, the same reading `isScannablePeerList` takes. The reduction is
314
+ * what lets a linked path reach the span test at all, since the markup around
315
+ * it would fail the pattern the destination is still attached.
316
+ */
317
+ function isSectionMarker(text: string): boolean {
318
+ const marker = text.match(BOLD_LINE)
319
+ if (!marker) return false
320
+
321
+ const label = marker[1]
322
+ if (label.endsWith(':')) return true
323
+
324
+ const visible = visibleText(label)
325
+
326
+ return SPAN_LABEL.test(visible) || visible.length <= MARKER_WIDTH
327
+ }
328
+
275
329
  /**
276
330
  * Measures the longest run of lines no signpost breaks, in rendered lines.
277
331
  *
278
332
  * A heading breaks a run and so does a section marker, which is the same
279
- * signpost written the way a template asked for it. `SECTION_MARKER` above
333
+ * signpost written the way a template asked for it. `isSectionMarker` above
280
334
  * fixes which lines qualify.
281
335
  *
282
336
  * Fenced lines are skipped rather than treated as breaks, per the standard:
@@ -325,7 +379,7 @@ export function longestRun(
325
379
  for (const line of lines) {
326
380
  if (line.fenced) continue
327
381
 
328
- if (HEADING.test(line.text) || SECTION_MARKER.test(line.text)) {
382
+ if (HEADING.test(line.text) || isSectionMarker(line.text)) {
329
383
  close()
330
384
  continue
331
385
  }
@@ -37,7 +37,8 @@ Does not govern:
37
37
  - Exempt a block whose lines are all table rows, at any length. The peer list above is exempt because it is already navigable, and a table because the remedy does not exist: a subheading dropped inside one splits the table rather than the run, so no edit short of rewriting it as a list clears the checkpoint.
38
38
  - Prose either side of the table ends that exemption, since the block has a seam and a heading breaks it there.
39
39
  - Break a run on a bold section marker holding its line alone, the way a heading breaks one. A document whose template separates sections with `**Risks:**` rather than `## Risks` has written the seam a reader lands on, and reading only the heading reported every such file at its full length whatever it carried.
40
- - Require the marker to start at column zero and to end in a colon inside the bold, and to be the whole line. `**Risks:**` breaks a run, a bold phrase opening a sentence stays emphasis, and an indented one stays a label inside its list item. A colon-less `**Testing**` does not break either, which holds a real section marker out and is the cost of keeping the pattern narrow on data every project is measured against.
40
+ - Require the marker to start at column zero and to be the whole line. `**Risks:**` breaks a run, a bold phrase opening a sentence stays emphasis, and an indented one stays a label inside its list item.
41
+ - Break a colon-less marker on either of two signals, since no one test separates every marker from a sentence set in bold. A label that is one whole code span breaks at any width, which is how a bold line holding only a backticked path reads as the file heading it is. Anything else breaks at 20 characters or fewer, which reaches `**Testing**` and leaves a bold sentence as prose.
41
42
 
42
43
  ## Paragraphs and lists
43
44
 
package/standards/rule.md CHANGED
@@ -32,14 +32,14 @@ Write both when both apply. A rule stating the directive and a skill stating how
32
32
  ## Location
33
33
 
34
34
  - Rules live at `.claude/rules/<subdirectory>/<n>-<slug>.md`
35
- - Subdirectories group by domain: `core/`, `lang/`, `framework/`, `lib/`, `ui/`, `claude/`
35
+ - Subdirectories group by domain: `core/`, `lang/`, `framework/`, `lib/`, `ui/`, `claude/`, `snippets/`, `ci/`
36
36
  - `<n>` is a number from the band reserved for the rule's source and `<slug>` is a one-to-three-word kebab topic
37
37
  - A rule the project authored itself lives at `.claude/rules/project/<subdirectory>/<n>-<slug>.md`, keeping the same subdirectory names
38
38
  - Give every rule a numeric prefix. A bare-word filename reads as a folder name where a stack names its rules, so a rule without one is unreachable from a stack entry.
39
39
 
40
40
  ## Reserved numbers
41
41
 
42
- `000-899` is reserved for a rule set that ships to targets. `000-599` is what the six subdirectories above already divide, at 100 per subdirectory, and `600-899` is held for subdirectories a shipped set has not added yet.
42
+ `000-899` is reserved for a rule set that ships to targets. `000-599` is what the first six subdirectories above already divide, at 100 per subdirectory. `ci/` was the first to draw from the headroom past that, taking `700-799`, and `600-699` with `800-899` is what remains for a subdirectory a shipped set has not added yet.
43
43
 
44
44
  A project-authored rule takes `900-999`, one sequence across every subdirectory under `.claude/rules/project/`. Scanning for a free number instead is what fails, because a shipped set fills its own band release by release, so what reads as free today is what a later release lands on. One target authored `claude/561-self-check.md` on a day nothing shipped at 561, met `claude/561-teach.md` on its next install, and now reads two numbers differently from everywhere else.
45
45