@erclx/aitk 0.63.2 → 0.64.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/docs/agents/commands.md +3 -1
- package/docs/agents/context-audit-checks.md +9 -11
- package/docs/agents/context-audit.md +3 -1
- package/docs/agents/index.md +2 -1
- package/docs/agents/markdown-audit.md +70 -0
- package/package.json +1 -1
- package/src/cli.ts +4 -0
- package/src/commands/context.ts +8 -104
- package/src/commands/markdown.ts +383 -0
- package/src/context/audit.ts +19 -296
- package/src/context/citations.ts +2 -30
- package/src/git-files.ts +31 -0
- package/src/markdown/bans.ts +241 -0
- package/src/markdown/files.ts +92 -0
- package/src/markdown/scan.ts +183 -0
- package/src/markdown/structure.ts +408 -0
- package/src/records/validate.ts +1 -37
- package/standards/markdown.md +2 -1
package/docs/agents/commands.md
CHANGED
|
@@ -32,7 +32,8 @@ Full help: `aitk <command> --help`. Behavior notes for the install and sync verb
|
|
|
32
32
|
| `aitk tasks validate` | Report board rows whose plan, task file, group, or file set does not hold (`--json`) |
|
|
33
33
|
| `aitk records validate` | Report a session record against the standard governing it, per kind (`--json`) |
|
|
34
34
|
| `aitk comments scan` | Measure comment density by language and comment kind, with a trend recomputed from git |
|
|
35
|
-
| `aitk context audit` | Report required sections, length,
|
|
35
|
+
| `aitk context audit` | Report required sections, length, cited paths, catalog tables, provenance, and index drift |
|
|
36
|
+
| `aitk markdown audit` | Report any markdown path against the character bans, word bans, and structural checkpoints |
|
|
36
37
|
| `aitk claude skills audit` | Report both skill corpora against the mechanical rules in `standards/skill.md` |
|
|
37
38
|
| `aitk capture [source]` | Render HTML capture sources to PNG, toolkit-only and absent from an installed package |
|
|
38
39
|
|
|
@@ -53,6 +54,7 @@ Each domain exposes a consistent shape where applicable: `list`, `install`, `syn
|
|
|
53
54
|
| `tasks` | `archive`, `validate` |
|
|
54
55
|
| `comments` | `scan` |
|
|
55
56
|
| `context` | `audit` |
|
|
57
|
+
| `markdown` | `audit` |
|
|
56
58
|
|
|
57
59
|
Common patterns:
|
|
58
60
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Context audit checks
|
|
3
|
-
description: What each non-gating check reports, the unit each checkpoint is measured in,
|
|
3
|
+
description: What each non-gating check reports, the unit each checkpoint is measured in, which folders each check reaches, and what moved to the attribute tier
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Context audit checks
|
|
@@ -17,19 +17,15 @@ It reports rather than gates by default, the closer call because a missing secti
|
|
|
17
17
|
|
|
18
18
|
`--gate` promotes the finding to a failing exit code, which the seed stage runs and no other caller does. That mode needs an answer to the sanctioned omission above, so a file declaring `stub: true` in its frontmatter is dropped before the check and reported nowhere. Both are described in `context-audit.md`.
|
|
19
19
|
|
|
20
|
-
## Length
|
|
20
|
+
## Length
|
|
21
21
|
|
|
22
|
-
Length quotes its checkpoint from `.claude/standards/context.md
|
|
22
|
+
Length quotes its checkpoint from `.claude/standards/context.md`: roughly 150 rendered lines for an entry. It counts rendered lines rather than source lines, wrapping each line at 80 columns and summing the heights. Entries here are authored one line per bullet, so a block of fifteen paragraph-bullets occupies fifteen source lines and renders past sixty, which source counting cannot see. The measure counts fenced blocks and frontmatter, so a reference-heavy entry ranks by its examples, which the legend states on every run alongside the width, since a number in rendered lines cannot be reproduced without it.
|
|
23
23
|
|
|
24
|
-
Depth
|
|
24
|
+
Depth and bullet weight are quoted from `.claude/standards/markdown.md`, which states both over every markdown file rather than over a context entry, so `aitk markdown audit` measures them and this command no longer does. They share `renderedHeight` with the length measure, since the two checkpoints sit in one section of that standard and a reader compares them. What the split costs is that a session wanting both numbers for one entry runs two commands, and what it buys is that either number can be had for a file in a folder this audit refuses to resolve.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
## Tables
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
The bullet check reports a top-level bullet past roughly 400 characters, which is where the overflow belongs in prose rather than in the bullet. A context entry specializes that remedy, sending the incident that motivated a decision to the change, issue, or research record behind it. Continuation lines fold into the bullet they belong to, so a heavy bullet cannot fall under the checkpoint by wrapping across two source lines, while a nested item is left out because the parent's own text is what the checkpoint asks about. Findings group by entry for the reason the provenance ones do, and reach every audited folder, since `.claude/standards/markdown.md` states the checkpoint over every markdown file. Unlike the peer-list threshold above it, this corpus has no gap behind the number: bullet weight decays smoothly from a median near 170, so the number is a judgment where that one was a measurement, and a bullet reading well past it means the number is wrong rather than the rule.
|
|
31
|
-
|
|
32
|
-
The table check reports a catalog that grows a row per shipped thing, not a table count. A fixed comparison table never reflows, so its size costs nothing. A table qualifies at six or more body rows whose first column mostly carries a path, command, or link, which is what separates a catalog from a comparison without reading the prose.
|
|
28
|
+
The table check reports a catalog that grows a row per shipped thing, not a table count. A fixed comparison table never reflows, so its size costs nothing. A table qualifies at six or more body rows whose first column mostly carries a path, command, or link, which is what separates a catalog from a comparison without reading the prose. It stays here rather than moving with depth, because the shape it routes a catalog into is a judgment the context standard makes about an entry.
|
|
33
29
|
|
|
34
30
|
## Provenance
|
|
35
31
|
|
|
@@ -37,7 +33,9 @@ The provenance check reports the markers narrating how a domain reached its shap
|
|
|
37
33
|
|
|
38
34
|
## Which folders each check reaches
|
|
39
35
|
|
|
40
|
-
The provenance and required-section checks cover `.claude/context/` alone, while length
|
|
36
|
+
The provenance and required-section checks cover `.claude/context/` alone, while length and the table finding reach every audited folder. What narrows the two is stated in `.claude/standards/context.md`, which opens its scope by handing diagrams and wireframes to `diagrams.md` and `wireframes.md`, and the sibling standards do not restate it. A marker reported in a diagram entry would cite a rule that entry's own standard routes elsewhere. The split is between kinds of rule rather than kinds of folder, and what decides it is which tier states the rule rather than what the check measures. Length and the table finding generalize as judgments about how far a reader travels, so both reach wherever the audit is pointed. Required sections narrow for a plainer reason: the names are the context standard's own, and neither sibling standard states a required section at all.
|
|
37
|
+
|
|
38
|
+
The same test is what moved depth and bullet weight out of this command entirely. A rule stated at the attribute tier reaches every markdown file, and a check reaching every markdown file has no reason to require a folder that resolves. The scoping key is the folder an entry was audited under, so `--folder` still reaches a folder the default list does not carry, and a domain split into `context/<sub-area>/` is governed as `context`. Every run states the reach, including a run where no audited folder is the governed one. The JSON record carries it as `checkpoints.provenanceFolder` and a per-folder `governsContent`.
|
|
41
39
|
|
|
42
40
|
## Index drift
|
|
43
41
|
|
|
@@ -7,6 +7,8 @@ description: Running the audit, its flags and folder scope, the exit codes, the
|
|
|
7
7
|
|
|
8
8
|
`aitk context audit [path]` reports the structural state of the folders following the index-plus-entry contract, meaning a generated `index.md` beside entries carrying frontmatter. It reads and reports. Fixing what it finds is separate work. What each finding means is in `context-audit-checks.md`.
|
|
9
9
|
|
|
10
|
+
Findings stated over every markdown file rather than over a context entry are measured by `aitk markdown audit`, described in `markdown-audit.md`. That command resolves no folder, so it reaches trees this one refuses.
|
|
11
|
+
|
|
10
12
|
```bash
|
|
11
13
|
aitk context audit
|
|
12
14
|
aitk context audit --json
|
|
@@ -35,7 +37,7 @@ A run where no requested name resolves refuses, whichever list it read. Naming t
|
|
|
35
37
|
|
|
36
38
|
## Exit codes
|
|
37
39
|
|
|
38
|
-
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a gating finding. An unresolved citation gates under every mode. Length,
|
|
40
|
+
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a gating finding. An unresolved citation gates under every mode. Length, table, and provenance findings print and return `0` under every mode, because each is a judgment and failing a push on one would make the check something to route around.
|
|
39
41
|
|
|
40
42
|
Required-section and index findings sit between the two. Both are answerable from the file rather than weighed, so `--gate` promotes them to failing codes while a bare run leaves them advisory. The toolkit runs the bare form against itself and the widened form against the seed tree, described below.
|
|
41
43
|
|
package/docs/agents/index.md
CHANGED
|
@@ -11,11 +11,12 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
|
|
|
11
11
|
- [Capture](capture.md): Rendering committed HTML sources to PNG, what the command asserts about fonts, and why it is toolkit-only
|
|
12
12
|
- [Command catalog](commands.md): Every project-level command and every domain subcommand, plus the shape each domain exposes
|
|
13
13
|
- [Comments](comments.md): Comment density by language and kind, the two structural exclusions, and how the degradation sweep finds its vocabulary
|
|
14
|
-
- [Context audit checks](context-audit-checks.md): What each non-gating check reports, the unit each checkpoint is measured in,
|
|
14
|
+
- [Context audit checks](context-audit-checks.md): What each non-gating check reports, the unit each checkpoint is measured in, which folders each check reaches, and what moved to the attribute tier
|
|
15
15
|
- [Context audit](context-audit.md): Running the audit, its flags and folder scope, the exit codes, the citation gate, and the widened gate the seed stage runs
|
|
16
16
|
- [Docs](docs.md): How aitk docs resolves the toolkit's own reference surface from an install root, and how a split domain is named
|
|
17
17
|
- [Indexes](indexes.md): Flags, exit codes, and JSON shape for aitk indexes regen, plus when it auto-stages what it rewrote
|
|
18
18
|
- [Install and sync](install-and-sync.md): What each install and sync verb writes, refuses, or leaves alone, and how drift is attributed in a target project
|
|
19
|
+
- [Markdown audit](markdown-audit.md): Running the audit over any markdown path, where its bans and checkpoints are read from, what each check reports, and why nothing gates yet
|
|
19
20
|
- [Output shape](output-shape.md): Two framed shapes every command renders into, how JSON and --names modes keep stdout clean, and the exit discipline that lets piped output drain
|
|
20
21
|
- [Overview](overview.md): What this folder covers, the invocation rules every command inherits, and where domain behavior is documented instead
|
|
21
22
|
- [Records](records.md): Validating the gitignored session records under .claude/, the per-kind checks, the refusal reasons, and why the root defaults to the main worktree
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Markdown audit
|
|
3
|
+
description: Running the audit over any markdown path, where its bans and checkpoints are read from, what each check reports, and why nothing gates yet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Markdown audit
|
|
7
|
+
|
|
8
|
+
`aitk markdown audit [path...]` reports any markdown file against the two attribute standards, `markdown.md` and `prose.md`. An attribute standard governs a file rather than a folder, so this resolves no folder and requires no `index.md`, which is what puts `.claude/rules/`, `governance/`, and `snippets/` in reach. Folder-shaped findings stay in `aitk context audit`, described in `context-audit.md`.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
aitk markdown audit
|
|
12
|
+
aitk markdown audit --json
|
|
13
|
+
aitk markdown audit .claude/rules governance
|
|
14
|
+
aitk markdown audit docs/agents/commands.md
|
|
15
|
+
aitk markdown audit 'snippets/**/*.md'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| Option | Behavior |
|
|
19
|
+
| -------- | ---------------------------------------------------------- |
|
|
20
|
+
| `--json` | Add a machine-readable record on stdout, keeping the frame |
|
|
21
|
+
|
|
22
|
+
## Scope
|
|
23
|
+
|
|
24
|
+
An argument is a file, a directory, or a glob. A directory narrows to everything under it and a glob narrows by match, both against the corpus git lists, which is what keeps `node_modules/` and the gitignored session-scratch folders out without naming either. An explicit file path is taken as given, so a gitignored draft can be measured before it is committed. Quote a glob the shell would expand first.
|
|
25
|
+
|
|
26
|
+
A bare run measures every markdown file git lists, tracked plus untracked-and-not-ignored, so a file added on this branch is in scope on the branch that adds it. An argument matching no markdown file is named on the scope line rather than passed over, since a run measuring the paths that did resolve otherwise reads as a pass over one it never opened.
|
|
27
|
+
|
|
28
|
+
## Where the rules come from
|
|
29
|
+
|
|
30
|
+
Both ban sets and all five checkpoints are read out of the standards per run, resolved under `.claude/standards/` first and the authoring root second, so a target project measures against the copy it actually has. Holding the lists in code was the alternative and it puts each ban in two places, where an author adding one gets no enforcement until someone edits TypeScript.
|
|
31
|
+
|
|
32
|
+
The trade is a reader of prose that a reformat can break. `src/markdown/bans.test.ts` answers it by asserting the parsed sets against the shipped standards, so a rewrite that narrows a set fails there rather than passing quietly. A checkpoint falls back per number rather than per file, and the depth legend names every checkpoint that fell back on the run that used one.
|
|
33
|
+
|
|
34
|
+
## What each check reports
|
|
35
|
+
|
|
36
|
+
### Bans
|
|
37
|
+
|
|
38
|
+
Three closed sets report a hit: the characters `markdown.md` bans under `## Punctuation`, the single lowercase words `prose.md` bans under `## Language`, and the British spellings derived by applying that section's own suffix rules to its own examples.
|
|
39
|
+
|
|
40
|
+
Deriving the spellings rather than pattern-matching a suffix is what keeps `exercises`, `promises`, and `revised` out of the report. A suffix pattern over the same corpus produced 46 false positives from words of that shape, and a closed set of whole words reaches none of them.
|
|
41
|
+
|
|
42
|
+
Frontmatter, fenced blocks, inline code spans, and link destinations are excluded. Without the code-span exclusion each standard would report its own backticked examples, and without the link exclusion a semicolon in a query string would report as prose no rewrite can fix.
|
|
43
|
+
|
|
44
|
+
Two ban shapes stay unmeasured and the report says so on every run. A phrase ban carries a placeholder standing in for the rest of the sentence, so no literal match reaches it, and every rule under `## Voice` is a judgment. A report listing hits without naming those would read as a verdict on the whole standard.
|
|
45
|
+
|
|
46
|
+
### Bullets, paragraphs, and depth
|
|
47
|
+
|
|
48
|
+
Bullet weight and depth are the checks that moved off `aitk context audit`, unchanged in what they measure. A top-level bullet reports past roughly 400 characters with continuation lines folded in and nested items left out. A run of lines no heading breaks reports past roughly 40 rendered lines, measured at 80 columns, skipping fenced blocks and exempting a flat peer list averaging under 130 characters a bullet and a run that is entirely table rows.
|
|
49
|
+
|
|
50
|
+
The paragraph check is new and measures both halves of one rule. `markdown.md` caps a paragraph at four sentences, and a sentence cap on its own is satisfied by writing fewer and longer ones: measured across 2906 paragraphs, 344 sit inside four sentences and past 400 characters, and the heaviest runs 1159 characters in two. The standard therefore states a weight beside the sentence cap, and the verb reads it as its own checkpoint.
|
|
51
|
+
|
|
52
|
+
The paragraph weight and the bullet weight are the same number today because the two shapes measure one population, sharing a median near 170 characters with no gap behind either candidate. They are separate checkpoints in the standard and separate patterns in the parser, so retuning one leaves the other where it is.
|
|
53
|
+
|
|
54
|
+
A bullet, a heading, a table row, a blockquote, a blank line, and a fence each end a paragraph, so a heavy bullet is reported by the bullet check alone and never counted twice.
|
|
55
|
+
|
|
56
|
+
## Exit codes
|
|
57
|
+
|
|
58
|
+
Exit codes are `0` for a completed run and `1` for a refusal. Every finding reports and none gates.
|
|
59
|
+
|
|
60
|
+
A banned character is a fact rather than a judgment, which is the test that would ordinarily make it gate. What holds it back is that gating on day one against a corpus never checked mechanically fails loudly on work nobody has had a chance to fix. The order is to land the verb reporting, measure the corpus once, fix what it finds, and turn the gate on as its own change. Bullet, paragraph, and depth weight are judgments and stay advisory under any later gate.
|
|
61
|
+
|
|
62
|
+
Measured across 443 files on the day this shipped: 9 word hits, no character or spelling hits, 109 heavy bullets, 610 heavy paragraphs, and 42 files carrying a run past the depth checkpoint. The ban count is what a gate would have to hold at zero, and it is the only one of the five a gate should ever read.
|
|
63
|
+
|
|
64
|
+
## What it does not cover
|
|
65
|
+
|
|
66
|
+
The verb reads the two attribute standards and nothing else. The five standards declaring `appliesTo: ["*"]` also include `publish.md`, `slug.md`, and `versioning.md`, none of which this implements.
|
|
67
|
+
|
|
68
|
+
`publish.md` describes a scan applying the same punctuation bans to finished text on its way out. No code implemented that scan before this command, so nothing is duplicated, and a later surface should call this verb rather than build a second one.
|
|
69
|
+
|
|
70
|
+
The list-density rule at `standards/markdown.md` is out of scope on purpose, since it carries no number and what a density figure should measure is still open.
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { register as transcripts } from '@/commands/transcripts'
|
|
|
22
22
|
import { register as tasks } from '@/commands/tasks'
|
|
23
23
|
import { register as comments } from '@/commands/comments'
|
|
24
24
|
import { register as context } from '@/commands/context'
|
|
25
|
+
import { register as markdown } from '@/commands/markdown'
|
|
25
26
|
import { register as records } from '@/commands/records'
|
|
26
27
|
import { PROJECT_ROOT } from '@/exec'
|
|
27
28
|
|
|
@@ -54,6 +55,7 @@ function showHelp(): void {
|
|
|
54
55
|
`${GREY}│${NC} tasks [cmd] ${GREY}# Task board commands (archive)${NC}`,
|
|
55
56
|
`${GREY}│${NC} comments [cmd] ${GREY}# Measure comment density and trend (scan)${NC}`,
|
|
56
57
|
`${GREY}│${NC} context [cmd] ${GREY}# Report context folder health (audit)${NC}`,
|
|
58
|
+
`${GREY}│${NC} markdown [cmd] ${GREY}# Report markdown against the attribute standards (audit)${NC}`,
|
|
57
59
|
`${GREY}│${NC} records [cmd] ${GREY}# Check session records under .claude/ (validate)${NC}`,
|
|
58
60
|
`${GREY}│${NC}`,
|
|
59
61
|
`${GREY}│${NC} ${WHITE}Sandbox:${NC}`,
|
|
@@ -87,6 +89,7 @@ function showHelp(): void {
|
|
|
87
89
|
`${GREY}│${NC} aitk tasks archive --pull-request 673 --json`,
|
|
88
90
|
`${GREY}│${NC} aitk comments scan src --json`,
|
|
89
91
|
`${GREY}│${NC} aitk context audit --json`,
|
|
92
|
+
`${GREY}│${NC} aitk markdown audit .claude/rules --json`,
|
|
90
93
|
`${GREY}│${NC} aitk records validate plans`,
|
|
91
94
|
`${GREY}└${NC}`,
|
|
92
95
|
]
|
|
@@ -141,6 +144,7 @@ transcripts(program)
|
|
|
141
144
|
tasks(program)
|
|
142
145
|
comments(program)
|
|
143
146
|
context(program)
|
|
147
|
+
markdown(program)
|
|
144
148
|
records(program)
|
|
145
149
|
|
|
146
150
|
program.parse()
|
package/src/commands/context.ts
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
2
|
import type { Command } from 'commander'
|
|
3
3
|
import {
|
|
4
|
-
BULLET_CHECKPOINT,
|
|
5
4
|
type EntryReport,
|
|
6
5
|
governsContent,
|
|
7
6
|
LENGTH_CHECKPOINT,
|
|
8
7
|
measureFolders,
|
|
9
8
|
missingSections,
|
|
10
|
-
PEER_BULLET_CHECKPOINT,
|
|
11
9
|
PROVENANCE_FOLDER,
|
|
12
|
-
RENDER_WIDTH,
|
|
13
10
|
REQUIRED_SECTIONS,
|
|
14
|
-
RUN_CHECKPOINT,
|
|
15
11
|
type SectionFinding,
|
|
16
12
|
} from '@/context/audit'
|
|
17
13
|
import { auditCitations, type CitationReport } from '@/context/citations'
|
|
@@ -23,6 +19,7 @@ import {
|
|
|
23
19
|
} from '@/context/folders'
|
|
24
20
|
import { isGating } from '@/context/gate'
|
|
25
21
|
import { auditIndexes, type FolderDrift } from '@/context/index-drift'
|
|
22
|
+
import { RENDER_WIDTH } from '@/markdown/structure'
|
|
26
23
|
import {
|
|
27
24
|
frameError,
|
|
28
25
|
intro,
|
|
@@ -57,7 +54,7 @@ export function register(program: Command): void {
|
|
|
57
54
|
context
|
|
58
55
|
.command('audit')
|
|
59
56
|
.description(
|
|
60
|
-
'Report required sections, entry length,
|
|
57
|
+
'Report required sections, entry length, citations, catalog tables, provenance, and index drift',
|
|
61
58
|
)
|
|
62
59
|
.argument('[path]', 'Project root, defaulting to the current directory')
|
|
63
60
|
.helpOption('-h, --help', 'Show this help message')
|
|
@@ -82,8 +79,11 @@ export function register(program: Command): void {
|
|
|
82
79
|
'',
|
|
83
80
|
'An unresolved citation always gates. --gate widens the gate to the',
|
|
84
81
|
'other two findings that are facts rather than judgments: a missing',
|
|
85
|
-
'required section and index drift. Length,
|
|
86
|
-
'
|
|
82
|
+
'required section and index drift. Length, table, and provenance',
|
|
83
|
+
'findings are thresholds and stay advisory under both.',
|
|
84
|
+
'',
|
|
85
|
+
'Depth and bullet weight are stated over every markdown file rather',
|
|
86
|
+
'than over a context entry, so `aitk markdown audit` measures them.',
|
|
87
87
|
'',
|
|
88
88
|
'Examples:',
|
|
89
89
|
' aitk context audit',
|
|
@@ -192,8 +192,6 @@ async function runAudit(
|
|
|
192
192
|
reportCitations(citations, cited)
|
|
193
193
|
reportSections(sections, folders)
|
|
194
194
|
reportLength(entries)
|
|
195
|
-
reportDepth(entries)
|
|
196
|
-
reportBullets(entries)
|
|
197
195
|
reportTables(entries)
|
|
198
196
|
reportProvenance(entries, folders)
|
|
199
197
|
reportDrift(drift)
|
|
@@ -221,11 +219,7 @@ async function runAudit(
|
|
|
221
219
|
indexDrift: drift,
|
|
222
220
|
checkpoints: {
|
|
223
221
|
lines: LENGTH_CHECKPOINT,
|
|
224
|
-
run: RUN_CHECKPOINT,
|
|
225
|
-
runCountsBlankLines: true,
|
|
226
222
|
renderWidth: RENDER_WIDTH,
|
|
227
|
-
peerBullet: PEER_BULLET_CHECKPOINT,
|
|
228
|
-
bullet: BULLET_CHECKPOINT,
|
|
229
223
|
provenanceFolder: PROVENANCE_FOLDER,
|
|
230
224
|
requiredSections: REQUIRED_SECTIONS,
|
|
231
225
|
},
|
|
@@ -395,7 +389,7 @@ function reportLength(entries: readonly EntryReport[]): void {
|
|
|
395
389
|
`Entries measure rendered lines at ${RENDER_WIDTH} columns, counting frontmatter and fenced blocks.`,
|
|
396
390
|
)
|
|
397
391
|
logInfo(
|
|
398
|
-
'A reference-heavy entry therefore ranks by its examples, which the depth check excludes.',
|
|
392
|
+
'A reference-heavy entry therefore ranks by its examples, which the depth check in `aitk markdown audit` excludes.',
|
|
399
393
|
)
|
|
400
394
|
|
|
401
395
|
const over = entries
|
|
@@ -415,96 +409,6 @@ function reportLength(entries: readonly EntryReport[]): void {
|
|
|
415
409
|
)
|
|
416
410
|
}
|
|
417
411
|
|
|
418
|
-
/**
|
|
419
|
-
* Names the render width and the blank-line convention on every run.
|
|
420
|
-
*
|
|
421
|
-
* The standard settles heading level and fenced blocks and stops there, so a
|
|
422
|
-
* hand reader who drops blank lines lands a line or two below this number.
|
|
423
|
-
* Stating both is what keeps the two measurements reconcilable, and the width
|
|
424
|
-
* matters more than the blank lines because a number counted in rendered lines
|
|
425
|
-
* cannot be reproduced without it.
|
|
426
|
-
*/
|
|
427
|
-
function reportDepth(entries: readonly EntryReport[]): void {
|
|
428
|
-
logStep('Depth')
|
|
429
|
-
logInfo(
|
|
430
|
-
`Runs measure rendered lines at ${RENDER_WIDTH} columns and count blank lines.`,
|
|
431
|
-
)
|
|
432
|
-
logInfo(
|
|
433
|
-
`Fenced blocks are excluded, and so are peer lists averaging under ${PEER_BULLET_CHECKPOINT} characters a bullet.`,
|
|
434
|
-
)
|
|
435
|
-
logInfo(
|
|
436
|
-
'A run that is entirely table rows is excluded too, since a heading inside a table splits the table rather than the run.',
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
const over = entries
|
|
440
|
-
.filter((entry) => entry.longestRun > RUN_CHECKPOINT)
|
|
441
|
-
.sort((a, b) => b.longestRun - a.longestRun)
|
|
442
|
-
|
|
443
|
-
if (over.length === 0) {
|
|
444
|
-
logInfo(`No run past the ${RUN_CHECKPOINT}-line checkpoint.`)
|
|
445
|
-
return
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
logWarn(`${over.length} past the ${RUN_CHECKPOINT}-line checkpoint`)
|
|
449
|
-
pipeOutput(
|
|
450
|
-
over
|
|
451
|
-
.map(
|
|
452
|
-
(entry) =>
|
|
453
|
-
`${entry.rel}:${entry.longestRunLine} ${entry.longestRun} rendered lines unbroken`,
|
|
454
|
-
)
|
|
455
|
-
.join('\n'),
|
|
456
|
-
)
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Groups by entry, and states its reach on every run, for the reasons the
|
|
461
|
-
* provenance report does both.
|
|
462
|
-
*
|
|
463
|
-
* Bullets past the checkpoint cluster in a handful of entries and the heaviest
|
|
464
|
-
* entries carry them a dozen at a time, so a flat list of bullets buries the
|
|
465
|
-
* entry holding one. What a reader acts on is which file to open.
|
|
466
|
-
*/
|
|
467
|
-
function reportBullets(entries: readonly EntryReport[]): void {
|
|
468
|
-
logStep('Bullets')
|
|
469
|
-
|
|
470
|
-
logInfo(
|
|
471
|
-
'Covers every audited folder, since the rule is stated in the standard governing every markdown file.',
|
|
472
|
-
)
|
|
473
|
-
logInfo(
|
|
474
|
-
'Top-level bullets measure characters, folding in continuation lines.',
|
|
475
|
-
)
|
|
476
|
-
logInfo(
|
|
477
|
-
'Nested items and fenced blocks are excluded. Weight is a judgment, never a defect.',
|
|
478
|
-
)
|
|
479
|
-
|
|
480
|
-
const carrying = entries
|
|
481
|
-
.filter((entry) => entry.heavyBullets.length > 0)
|
|
482
|
-
.sort((a, b) => b.heavyBullets.length - a.heavyBullets.length)
|
|
483
|
-
|
|
484
|
-
if (carrying.length === 0) {
|
|
485
|
-
logInfo(`No bullet past the ${BULLET_CHECKPOINT}-character checkpoint.`)
|
|
486
|
-
return
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
const total = carrying.reduce(
|
|
490
|
-
(sum, entry) => sum + entry.heavyBullets.length,
|
|
491
|
-
0,
|
|
492
|
-
)
|
|
493
|
-
logWarn(
|
|
494
|
-
`${plural(total, 'bullet')} past the ${BULLET_CHECKPOINT}-character checkpoint across ${carrying.length} ${carrying.length === 1 ? 'entry' : 'entries'}`,
|
|
495
|
-
)
|
|
496
|
-
pipeOutput(
|
|
497
|
-
carrying
|
|
498
|
-
.map(
|
|
499
|
-
(entry) =>
|
|
500
|
-
`${entry.rel} ${plural(entry.heavyBullets.length, 'bullet')}\n${entry.heavyBullets
|
|
501
|
-
.map((found) => ` :${found.line} ${found.characters} characters`)
|
|
502
|
-
.join('\n')}`,
|
|
503
|
-
)
|
|
504
|
-
.join('\n'),
|
|
505
|
-
)
|
|
506
|
-
}
|
|
507
|
-
|
|
508
412
|
function reportTables(entries: readonly EntryReport[]): void {
|
|
509
413
|
logStep('Tables')
|
|
510
414
|
|