@erclx/aitk 0.100.0 → 0.102.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/claude-orchestrate/SKILL.md +11 -4
- package/claude/skills/claude-orchestrate/references/orchestrator-poll.md +4 -4
- package/claude/skills/claude-orchestrate/references/orchestrator-sweep.md +1 -1
- package/claude/skills/claude-orchestrate/scripts/poll.sh +11 -10
- package/claude/skills/claude-pr-review/SKILL.md +16 -16
- package/claude/skills/claude-tasks/SKILL.md +10 -1
- package/claude/skills/claude-teach/REQUIREMENT.md +2 -1
- package/claude/skills/claude-teach/SKILL.md +40 -9
- package/claude/skills/session-resume/SKILL.md +1 -1
- package/docs/agents/commands.md +5 -0
- package/docs/agents/index.md +2 -1
- package/docs/agents/markdown-audit.md +21 -11
- package/docs/agents/tasks.md +8 -4
- package/docs/agents/teach.md +119 -0
- package/docs/ai-workflow.md +1 -1
- package/docs/operating-model.md +9 -8
- package/docs/target-projects.md +1 -1
- package/package.json +1 -1
- package/scripts/core/verify.sh +3 -0
- package/src/cli.ts +4 -0
- package/src/commands/markdown.ts +44 -28
- package/src/commands/tasks.ts +6 -3
- package/src/commands/teach.ts +650 -0
- package/src/markdown/bans.ts +71 -210
- package/src/markdown/structure.ts +9 -71
- package/src/records/validate.ts +8 -7
- package/src/tasks/archive.ts +9 -4
- package/src/tasks/validate.ts +103 -3
- package/src/teach/workspace.ts +797 -0
- package/standards/prose.md +0 -2
- package/standards/tasks.md +51 -8
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +43 -47
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Teach
|
|
3
|
+
description: Listing learning workspaces and the ordinal a new one takes, opening one with its required files, recording sources and glossary terms, the refusal reasons, and why every write here runs through a verb
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Teach
|
|
7
|
+
|
|
8
|
+
Learning workspaces sit under `.claude/teach/<nn>-<topic>/`, and `standards/teach.md` fixes their layout, naming, and file formats. Every verb here resolves that folder against the main worktree root rather than against the working directory, so a session standing in a linked worktree reaches the one workspace the learner has rather than opening a second.
|
|
9
|
+
|
|
10
|
+
That root resolution is also why the writing verbs exist at all. The file-editing tools refuse a main-root path from a linked worktree and offer a worktree copy instead, and a caller naming only the destination reports a success that did not happen. A whole-file create still goes out as a shell heredoc. Changing a line inside a file that already exists has no shell route, because the stream editors are banned, so `resource` and `glossary` are the route for the two files a running workspace edits.
|
|
11
|
+
|
|
12
|
+
## List
|
|
13
|
+
|
|
14
|
+
`aitk teach list` reports the workspaces under `.claude/teach/`, or what one workspace holds. It reads and never writes.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
aitk teach list
|
|
18
|
+
aitk teach list regular-expressions --json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Option | Behavior |
|
|
22
|
+
| --------------- | ------------------------------------------- |
|
|
23
|
+
| `--json` | Emit a machine-readable record on stdout |
|
|
24
|
+
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
25
|
+
|
|
26
|
+
With no topic it reports one line per workspace, carrying the lesson, learning-record, reference-page, and glossary-term counts, plus `next`, the ordinal an open would take. With one it reports the filenames behind each count and the glossary entries themselves.
|
|
27
|
+
|
|
28
|
+
A folder not named `NN-<topic>` is still listed rather than dropped, since dropping it hides the one folder that needs a fix. Its ordinal reads as absent, it sorts last, and it moves no ordinal, so a malformed name cannot push a new workspace into a number a reader already cites.
|
|
29
|
+
|
|
30
|
+
The listing also names the required files a workspace does not carry, which is `MISSION.md`, `RESOURCES.md`, and `GLOSSARY.md`. That is a report rather than a refusal, because a workspace missing one is still a workspace a session can resume.
|
|
31
|
+
|
|
32
|
+
## Open
|
|
33
|
+
|
|
34
|
+
`aitk teach open` creates a workspace at the next ordinal and writes all three required files.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
aitk teach open regular-expressions \
|
|
38
|
+
--subject "Reading and writing regular expressions" \
|
|
39
|
+
--starting-point "Comfortable with the shell, has never written a group" \
|
|
40
|
+
--success "Write a pattern matching a date" \
|
|
41
|
+
--success "Explain what a backreference does"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Option | Behavior |
|
|
45
|
+
| ------------------------- | ----------------------------------------------------------- |
|
|
46
|
+
| `--subject <line>` | One line stating what the workspace covers, required |
|
|
47
|
+
| `--starting-point <text>` | What the learner already knows, required |
|
|
48
|
+
| `--success <line>` | Observable thing the learner will be able to do, repeatable |
|
|
49
|
+
| `--out-of-scope <line>` | What the workspace does not cover, repeatable |
|
|
50
|
+
| `--title <text>` | Title, defaulting to the topic in sentence case |
|
|
51
|
+
| `--date <YYYY-MM-DD>` | Opening date, defaulting to today |
|
|
52
|
+
| `--json` | Emit a machine-readable record on stdout |
|
|
53
|
+
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
54
|
+
|
|
55
|
+
The ordinal comes from the highest already present, incremented, so the caller derives no name and composes no path. A topic another workspace already covers is refused rather than opened beside it, since two workspaces on one subject fork the learning records the folder exists to keep whole.
|
|
56
|
+
|
|
57
|
+
`--starting-point` is required rather than defaulted because difficulty with no floor under it teaches nobody, and a mission written without one cannot say what the lessons sit above. At least one `--success` line is required for the same reason in the other direction: a mission nothing can test is a mission nothing can call finished.
|
|
58
|
+
|
|
59
|
+
## Resource
|
|
60
|
+
|
|
61
|
+
`aitk teach resource` records sources in a workspace `RESOURCES.md`, keeping what was read apart from what was only found.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
aitk teach resource regular-expressions \
|
|
65
|
+
--read "MDN regular expressions=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions" \
|
|
66
|
+
--lead "RE2 syntax=https://github.com/google/re2/wiki/Syntax"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Option | Behavior |
|
|
70
|
+
| -------------------- | -------------------------------------------------- |
|
|
71
|
+
| `--read <title=url>` | Source that stands behind the material, repeatable |
|
|
72
|
+
| `--lead <title=url>` | Source found and not opened, repeatable |
|
|
73
|
+
| `--json` | Emit a machine-readable record on stdout |
|
|
74
|
+
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
75
|
+
|
|
76
|
+
The pair splits on the first `=`, so a URL carrying its own separator survives intact. Say in the title which claims rest on the source, since the entry is the only place a later session reads that from.
|
|
77
|
+
|
|
78
|
+
A URL either heading already lists is refused rather than written twice. Two entries for one source split what rests on it across two lines, and a reader checking a claim then finds half of the answer.
|
|
79
|
+
|
|
80
|
+
A URL a file displays inside a fence does not count as listed, which is how a workspace quoting the entry format in its own prose is read as the sample it is.
|
|
81
|
+
|
|
82
|
+
## Glossary
|
|
83
|
+
|
|
84
|
+
`aitk teach glossary` adds terms to a workspace `GLOSSARY.md`, alphabetically.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
aitk teach glossary regular-expressions \
|
|
88
|
+
--term "capture group=A parenthesised part of a pattern whose match is kept" \
|
|
89
|
+
--first-seen 0002-groups.html
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| Option | Behavior |
|
|
93
|
+
| -------------------------- | --------------------------------------------------------- |
|
|
94
|
+
| `--term <term=definition>` | Term the subject defines, repeatable |
|
|
95
|
+
| `--first-seen <file>` | Lesson or reference page the batch first defines these in |
|
|
96
|
+
| `--json` | Emit a machine-readable record on stdout |
|
|
97
|
+
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
98
|
+
|
|
99
|
+
One call writes one file, which keeps a batch of terms from racing on the glossary every one of them shares. `--first-seen` names one page for the whole batch rather than one per term, because a batch comes from one lesson.
|
|
100
|
+
|
|
101
|
+
A term already defined is refused rather than replaced. A definition the subject has moved under is a revision of the entry that exists, and nothing on the command line tells that apart from a second definition arriving by mistake.
|
|
102
|
+
|
|
103
|
+
The entry lands as the standard's shape, leading with the term as a bolded span. A definition not ending in sentence punctuation is terminated before the citation is appended, so a bare phrase does not run into the sentence naming where the term first appears.
|
|
104
|
+
|
|
105
|
+
## Refusal reasons
|
|
106
|
+
|
|
107
|
+
| Reason | Raised when |
|
|
108
|
+
| -------------- | --------------------------------------------------------------- |
|
|
109
|
+
| `no-teach` | The root carries no `.claude/teach/` folder |
|
|
110
|
+
| `no-workspace` | No workspace matches the topic, with the folder names as detail |
|
|
111
|
+
| `ambiguous` | Two workspaces claim one topic, with both names as detail |
|
|
112
|
+
| `exists` | A workspace already covers the topic an open names |
|
|
113
|
+
| `no-file` | The workspace carries no file the verb writes into |
|
|
114
|
+
| `no-section` | `RESOURCES.md` carries no heading the entries belong under |
|
|
115
|
+
| `listed` | A URL is already listed under either heading |
|
|
116
|
+
| `defined` | A term already carries a glossary entry |
|
|
117
|
+
| `bad-input` | The command line is malformed, before any folder is read |
|
|
118
|
+
|
|
119
|
+
A `bad-input` refusal reports the working directory as its root rather than the resolved one, since the command line is rejected before the root is worth resolving.
|
package/docs/ai-workflow.md
CHANGED
|
@@ -73,7 +73,7 @@ When features are independent, run them in parallel instead of sequentially. Use
|
|
|
73
73
|
- Ship each worktree separately with `aitk:git-ship`
|
|
74
74
|
- For full autonomy per worktree, invoke `aitk:claude-autoship` instead of the manual chain. Approve the plan, walk away, come back to draft PRs.
|
|
75
75
|
|
|
76
|
-
To run several worktrees as a coordinated flow rather than ad hoc, assert the orchestrator role in one warm session with `aitk:claude-orchestrate`. It owns the roadmap, handing a needed draft or resequence to a worker that runs `aitk:claude-roadmap` in its branch, plans each feature, refills the ready queue so a free worker never waits, and reviews each worker's PR with `aitk:claude-pr-review`, then tells the session holding that branch to run `aitk:claude-address-review` whenever the pass posted a finding at any severity,
|
|
76
|
+
To run several worktrees as a coordinated flow rather than ad hoc, assert the orchestrator role in one warm session with `aitk:claude-orchestrate`. It owns the roadmap, handing a needed draft or resequence to a worker that runs `aitk:claude-roadmap` in its branch, plans each feature, refills the ready queue so a free worker never waits, and reviews each worker's PR with `aitk:claude-pr-review`, then tells the session holding that branch to run `aitk:claude-address-review` whenever the pass posted a finding at any severity, which is the same threshold `aitk:claude-pr-review` states and posts its open heading under. The human launches workers and merges. See [operating model](operating-model.md) for the full loop.
|
|
77
77
|
|
|
78
78
|
Roadmap ownership holds while a scope exists to sequence. Once the MVP list in `.claude/REQUIREMENTS.md` has shipped, later work arrives as discrete items and the orchestrator reads `.claude/tasks/priority.md` for execution order instead.
|
|
79
79
|
|
package/docs/operating-model.md
CHANGED
|
@@ -40,8 +40,8 @@ One feature travels this path end to end.
|
|
|
40
40
|
2. Orchestrator plans the next feature with `claude-feature`, writing a plan to `.claude/plans/`. Planning stays in the warm session because good planning is cross-feature. It needs the contract other features consume and the shared wiring seam. A cold session would re-derive or guess.
|
|
41
41
|
3. The human opens a worker worktree with `claude-worktree` and runs `claude-autoship` against the plan. The worker builds, self-checks, opens a PR, and stops at the PR boundary.
|
|
42
42
|
4. Orchestrator reviews the PR with `claude-pr-review` and posts findings to it.
|
|
43
|
-
5. Orchestrator tells the session holding that branch to run `claude-address-review` once the pass posted a finding at any severity, resolving the target from a session listing taken at that moment and reporting the invocation for the human when no live session holds it. The worker addresses the findings, rebases onto `origin/main` when a sibling landed first and left the branch unable to merge, then pushes a follow-up. A pass carrying only minor findings dispatches too, since the grade runs low often enough that a floor at should-fix loses fixes a worker would have made.
|
|
44
|
-
6. Orchestrator closes the review out with `claude-pr-review` again. The second pass reads only the commits the follow-up added, or the worker's response alone when the follow-up added none, and posts under `## Review` when it finds
|
|
43
|
+
5. Orchestrator tells the session holding that branch to run `claude-address-review` once the pass posted a finding at any severity, resolving the target from a session listing taken at that moment and reporting the invocation for the human when no live session holds it. The worker addresses the findings, rebases onto `origin/main` when a sibling landed first and left the branch unable to merge, then pushes a follow-up. A pass carrying only minor findings dispatches too, since the grade runs low often enough that a floor at should-fix loses fixes a worker would have made. `claude-pr-review` states that threshold and the heading follows it, so an open heading is itself the signal to send.
|
|
44
|
+
6. Orchestrator closes the review out with `claude-pr-review` again. The second pass reads only the commits the follow-up added, or the worker's response alone when the follow-up added none, and posts under `## Review` when it finds anything and under `## Review closed` when it finds nothing, so a reader learns from the heading whether work is still owed and takes the merge decision from the counts on the line under it. Repeat from step 5 until a pass closes the review.
|
|
45
45
|
7. The human reads the result and merges. The orchestrator tells any trailing worker whose branch shares a seam with the merged one to run `claude-address-review`, which rebases whether or not the review left anything open.
|
|
46
46
|
|
|
47
47
|
There is no loop construct here. Each worker is a single build that halts at the
|
|
@@ -103,12 +103,13 @@ re-read costs a full pass rather than a delta, since the prior reviewed commit n
|
|
|
103
103
|
longer reaches the head, and `claude-pr-review` detects that itself.
|
|
104
104
|
|
|
105
105
|
The heading carries the state rather than the pass number. A pass carrying a
|
|
106
|
-
|
|
107
|
-
`## Review closed`, so a thread can be scanned for what still
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
finding at any severity takes `## Review` and a pass carrying nothing takes
|
|
107
|
+
`## Review closed`, so a thread can be scanned for what still owes work without
|
|
108
|
+
opening a comment. One threshold governs the heading and the dispatch alike, and
|
|
109
|
+
`claude-pr-review` is where it is stated, so every other surface cites that skill
|
|
110
|
+
rather than restating the grades. The merge decision comes off the counts on the
|
|
111
|
+
summary line, since an open heading now covers a minor as well as a critical.
|
|
112
|
+
A minor the worker declines goes to the findings of the task the
|
|
112
113
|
branch closes, since a thread does not survive the merge. The feedback
|
|
113
114
|
becomes a durable artifact both sessions read, survives a session ending, and
|
|
114
115
|
anchors to the change. That removes the copy-paste that otherwise routes review
|
package/docs/target-projects.md
CHANGED
|
@@ -89,7 +89,7 @@ Run `aitk tooling list --json` and `aitk gov list --json` to see the current cat
|
|
|
89
89
|
`governance`, `standards`, and `wiki` are skippable:
|
|
90
90
|
|
|
91
91
|
- `--skip governance`: leave `.claude/rules/` empty. Standards still install, so `.claude/standards/prose.md` lands with nothing pointing at it and no coding standard loads on a file match. The preview names any `--add` rules the skip drops, and the run prints the `aitk gov install <stack> <path>` command to add rules afterward, carrying those extras so one paste restores what the skip declined.
|
|
92
|
-
- `--skip standards`: leave standards out. The governance rules still reference `.claude/standards/`, so their authority lines resolve to nothing for a reader following the path. Toolkit skills are unaffected, since each falls back to the copy in its own plugin root
|
|
92
|
+
- `--skip standards`: leave standards out. The governance rules still reference `.claude/standards/`, so their authority lines resolve to nothing for a reader following the path. Toolkit skills are unaffected, since each falls back to the copy in its own plugin root. `aitk standards <name>` searches the corpus inside the CLI's own package behind both project roots, so it prints a standard in a project that skipped the install, and `aitk markdown audit` needs no standard at all, its ban sets and checkpoints shipping with the package as data. What the skip costs is the editable copy rather than the enforcement.
|
|
93
93
|
- `--skip wiki`: skip the `.claude/wiki/` scaffold. A target that already carries a root `wiki/` keeps it, since the verb reports that folder rather than migrating it.
|
|
94
94
|
|
|
95
95
|
That standards fallback carries runtime behavior rather than reference prose alone, because the pre-publish scan and the branch-slug transform each have a standard of their own, `publish.md` and `slug.md`, cited by the skills that run them.
|
package/package.json
CHANGED
package/scripts/core/verify.sh
CHANGED
|
@@ -363,6 +363,9 @@ main() {
|
|
|
363
363
|
1)
|
|
364
364
|
log_warn "Skipped, the markdown audit refused and measured nothing"
|
|
365
365
|
;;
|
|
366
|
+
3)
|
|
367
|
+
log_error "The markdown audit shipped an empty ban set, so the corpus was walked and nothing was looked for. Check src/markdown/bans.ts."
|
|
368
|
+
;;
|
|
366
369
|
2)
|
|
367
370
|
# `|| true` because the re-run exits non-zero by construction, and `set -e`
|
|
368
371
|
# would take the script down before log_error names the remedy.
|
package/src/cli.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { register as feedback } from '@/commands/feedback'
|
|
|
21
21
|
import { register as transcripts } from '@/commands/transcripts'
|
|
22
22
|
import { register as tasks } from '@/commands/tasks'
|
|
23
23
|
import { register as intake } from '@/commands/intake'
|
|
24
|
+
import { register as teach } from '@/commands/teach'
|
|
24
25
|
import { register as comments } from '@/commands/comments'
|
|
25
26
|
import { register as context } from '@/commands/context'
|
|
26
27
|
import { register as markdown } from '@/commands/markdown'
|
|
@@ -56,6 +57,7 @@ function showHelp(): void {
|
|
|
56
57
|
`${GREY}│${NC} transcripts <url> ${GREY}# Fetch a YouTube transcript with metadata frontmatter${NC}`,
|
|
57
58
|
`${GREY}│${NC} tasks [cmd] ${GREY}# Task board commands (archive)${NC}`,
|
|
58
59
|
`${GREY}│${NC} intake [cmd] ${GREY}# Intake folders under .claude/intake/ (list, answer)${NC}`,
|
|
60
|
+
`${GREY}│${NC} teach [cmd] ${GREY}# Learning workspaces under .claude/teach/ (list, open, resource, glossary)${NC}`,
|
|
59
61
|
`${GREY}│${NC} comments [cmd] ${GREY}# Measure comment density and trend (scan)${NC}`,
|
|
60
62
|
`${GREY}│${NC} context [cmd] ${GREY}# Report context folder health (audit)${NC}`,
|
|
61
63
|
`${GREY}│${NC} markdown [cmd] ${GREY}# Report markdown against the attribute standards (audit)${NC}`,
|
|
@@ -92,6 +94,7 @@ function showHelp(): void {
|
|
|
92
94
|
`${GREY}│${NC} aitk transcripts https://youtu.be/VIDEO_ID`,
|
|
93
95
|
`${GREY}│${NC} aitk tasks archive --pull-request 673 --json`,
|
|
94
96
|
`${GREY}│${NC} aitk intake list toolkit-overview --unread --json`,
|
|
97
|
+
`${GREY}│${NC} aitk teach list --json`,
|
|
95
98
|
`${GREY}│${NC} aitk comments scan src --json`,
|
|
96
99
|
`${GREY}│${NC} aitk context audit --json`,
|
|
97
100
|
`${GREY}│${NC} aitk markdown audit .claude/rules --json`,
|
|
@@ -150,6 +153,7 @@ feedback(program)
|
|
|
150
153
|
transcripts(program)
|
|
151
154
|
tasks(program)
|
|
152
155
|
intake(program)
|
|
156
|
+
teach(program)
|
|
153
157
|
comments(program)
|
|
154
158
|
context(program)
|
|
155
159
|
markdown(program)
|
package/src/commands/markdown.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises'
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
3
|
import type { Command } from 'commander'
|
|
4
|
-
import {
|
|
4
|
+
import { BAN_SETS, emptyBanSets } from '@/markdown/bans'
|
|
5
5
|
import { resolveMarkdown } from '@/markdown/files'
|
|
6
6
|
import { isGating } from '@/markdown/gate'
|
|
7
|
-
import { type BanFinding, bodyLines, scanBans } from '@/markdown/scan'
|
|
8
7
|
import {
|
|
8
|
+
type BanFinding,
|
|
9
|
+
type BanSets,
|
|
10
|
+
bodyLines,
|
|
11
|
+
scanBans,
|
|
12
|
+
} from '@/markdown/scan'
|
|
13
|
+
import {
|
|
14
|
+
CHECKPOINTS,
|
|
9
15
|
type Checkpoints,
|
|
10
16
|
measureStructure,
|
|
11
|
-
parseCheckpoints,
|
|
12
17
|
type StructureReport,
|
|
13
18
|
} from '@/markdown/structure'
|
|
14
19
|
import {
|
|
@@ -21,8 +26,19 @@ import {
|
|
|
21
26
|
plural,
|
|
22
27
|
} from '@/ui'
|
|
23
28
|
|
|
29
|
+
const EXIT_REFUSED = 1
|
|
24
30
|
const EXIT_GATE = 2
|
|
25
31
|
|
|
32
|
+
/**
|
|
33
|
+
* A set shipped empty, so the run measured a corpus against nothing.
|
|
34
|
+
*
|
|
35
|
+
* Distinct from `EXIT_REFUSED` because the two want different responses. A
|
|
36
|
+
* refusal means no corpus was built and a push stage is right to skip, while an
|
|
37
|
+
* empty set means the corpus was walked and nothing was looked for, which is a
|
|
38
|
+
* defect in the build and has to fail.
|
|
39
|
+
*/
|
|
40
|
+
const EXIT_UNUSABLE = 3
|
|
41
|
+
|
|
26
42
|
interface AuditCommandOptions {
|
|
27
43
|
readonly json?: boolean
|
|
28
44
|
}
|
|
@@ -58,6 +74,7 @@ export function register(program: Command): void {
|
|
|
58
74
|
' 0 the audit completed with no gating finding',
|
|
59
75
|
' 1 refused, with the reason on stderr',
|
|
60
76
|
' 2 a banned character, word, or spelling is present',
|
|
77
|
+
' 3 a shipped ban set is empty, so the run measured nothing',
|
|
61
78
|
'',
|
|
62
79
|
'A ban hit is a fact and gates unconditionally. Bullet, paragraph, and',
|
|
63
80
|
'depth weight are judgments a reader settles, so all three report and',
|
|
@@ -68,12 +85,11 @@ export function register(program: Command): void {
|
|
|
68
85
|
'where the token is genuinely an identifier under discussion, which is',
|
|
69
86
|
'what markdown.md reserves the span for.',
|
|
70
87
|
'',
|
|
71
|
-
'Bans and checkpoints
|
|
72
|
-
'
|
|
73
|
-
'
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'are in reach.',
|
|
88
|
+
'Bans and checkpoints ship with the aitk package rather than being read',
|
|
89
|
+
'out of a standards file, so a project that installed no standards is',
|
|
90
|
+
'measured the same as one that did. markdown.md and prose.md still state',
|
|
91
|
+
'every rule for a reader. No folder has to resolve and no index.md has',
|
|
92
|
+
'to exist, so .claude/rules/, governance/, and snippets/ are in reach.',
|
|
77
93
|
'',
|
|
78
94
|
'Examples:',
|
|
79
95
|
' aitk markdown audit',
|
|
@@ -110,9 +126,9 @@ async function runAudit(
|
|
|
110
126
|
)
|
|
111
127
|
}
|
|
112
128
|
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
const checkpoints =
|
|
129
|
+
const bans = BAN_SETS
|
|
130
|
+
const empty = emptyBanSets(bans)
|
|
131
|
+
const checkpoints = CHECKPOINTS
|
|
116
132
|
|
|
117
133
|
const reports: FileReport[] = await Promise.all(
|
|
118
134
|
scope.files.map(async (rel) => {
|
|
@@ -127,7 +143,7 @@ async function runAudit(
|
|
|
127
143
|
|
|
128
144
|
intro('aitk markdown audit')
|
|
129
145
|
reportScope(scope.files, scope.unmatched)
|
|
130
|
-
reportBans(reports, bans)
|
|
146
|
+
reportBans(reports, bans, empty)
|
|
131
147
|
reportBullets(reports, checkpoints)
|
|
132
148
|
reportParagraphs(reports, checkpoints)
|
|
133
149
|
reportDepth(reports, checkpoints)
|
|
@@ -143,8 +159,7 @@ async function runAudit(
|
|
|
143
159
|
characters: bans.characters,
|
|
144
160
|
words: bans.words,
|
|
145
161
|
spellings: bans.spellings,
|
|
146
|
-
|
|
147
|
-
missingStandards: bans.missing,
|
|
162
|
+
emptySets: empty,
|
|
148
163
|
},
|
|
149
164
|
checkpoints: {
|
|
150
165
|
run: checkpoints.run,
|
|
@@ -153,7 +168,6 @@ async function runAudit(
|
|
|
153
168
|
paragraph: checkpoints.paragraph,
|
|
154
169
|
sentences: checkpoints.sentences,
|
|
155
170
|
renderWidth: checkpoints.renderWidth,
|
|
156
|
-
fellBack: checkpoints.fellBack,
|
|
157
171
|
},
|
|
158
172
|
entries: reports.map((report) => ({
|
|
159
173
|
path: report.rel,
|
|
@@ -167,6 +181,10 @@ async function runAudit(
|
|
|
167
181
|
)
|
|
168
182
|
}
|
|
169
183
|
|
|
184
|
+
// An empty set finds nothing and would exit clean, which reports a corpus
|
|
185
|
+
// nobody checked as a corpus carrying no violation.
|
|
186
|
+
if (empty.length > 0) return EXIT_UNUSABLE
|
|
187
|
+
|
|
170
188
|
const gating = isGating({
|
|
171
189
|
bans: reports.flatMap((report) => report.bans),
|
|
172
190
|
structure: reports.map((report) => report.structure),
|
|
@@ -180,7 +198,7 @@ function refuse(message: string): number {
|
|
|
180
198
|
logStep('Refused')
|
|
181
199
|
logWarn(message)
|
|
182
200
|
outro()
|
|
183
|
-
return
|
|
201
|
+
return EXIT_REFUSED
|
|
184
202
|
}
|
|
185
203
|
|
|
186
204
|
function reportScope(
|
|
@@ -203,18 +221,22 @@ function reportScope(
|
|
|
203
221
|
* the sentence and every voice rule is a judgment, so a report listing hits
|
|
204
222
|
* without naming those would read as a verdict on the whole standard.
|
|
205
223
|
*/
|
|
206
|
-
function reportBans(
|
|
224
|
+
function reportBans(
|
|
225
|
+
reports: readonly FileReport[],
|
|
226
|
+
bans: BanSets,
|
|
227
|
+
empty: readonly string[],
|
|
228
|
+
): void {
|
|
207
229
|
logStep('Bans')
|
|
208
230
|
|
|
209
|
-
if (
|
|
231
|
+
if (empty.length > 0) {
|
|
210
232
|
logWarn(
|
|
211
|
-
`Not measured.
|
|
233
|
+
`Not measured. The shipped set is empty for: ${empty.join(', ')}. The sets ship with the aitk package, so an empty one is a defect in the build rather than a missing install.`,
|
|
212
234
|
)
|
|
213
|
-
|
|
235
|
+
return
|
|
214
236
|
}
|
|
215
237
|
|
|
216
238
|
logInfo(
|
|
217
|
-
`${plural(bans.characters.length, 'character')}, ${plural(bans.words.length, 'word')}, and ${plural(bans.spellings.length, 'spelling')}
|
|
239
|
+
`${plural(bans.characters.length, 'character')}, ${plural(bans.words.length, 'word')}, and ${plural(bans.spellings.length, 'spelling')} shipped with the aitk package`,
|
|
218
240
|
)
|
|
219
241
|
logInfo(
|
|
220
242
|
'Frontmatter, fenced blocks, code spans, and link destinations are excluded.',
|
|
@@ -379,12 +401,6 @@ function reportDepth(
|
|
|
379
401
|
'A run that is entirely table rows is excluded too, since a heading inside a table splits the table rather than the run.',
|
|
380
402
|
)
|
|
381
403
|
|
|
382
|
-
if (checkpoints.fellBack.length > 0) {
|
|
383
|
-
logWarn(
|
|
384
|
-
`Read no number from the standard for: ${checkpoints.fellBack.join(', ')}. Measured against the shipped default instead.`,
|
|
385
|
-
)
|
|
386
|
-
}
|
|
387
|
-
|
|
388
404
|
const over = reports
|
|
389
405
|
.filter((report) => report.structure.longestRun > checkpoints.run)
|
|
390
406
|
.sort((a, b) => b.structure.longestRun - a.structure.longestRun)
|
package/src/commands/tasks.ts
CHANGED
|
@@ -104,7 +104,7 @@ export function register(program: Command): void {
|
|
|
104
104
|
'',
|
|
105
105
|
'Checks:',
|
|
106
106
|
' every Run now row points at a plan file that resolves',
|
|
107
|
-
' every
|
|
107
|
+
' every task file carries a board row or a backlog line, never both',
|
|
108
108
|
' no task carries more than one row',
|
|
109
109
|
' no two Run now rows touch the same file',
|
|
110
110
|
'',
|
|
@@ -450,12 +450,14 @@ function reportValidation(
|
|
|
450
450
|
intro('aitk tasks validate')
|
|
451
451
|
logStep('Board')
|
|
452
452
|
logInfo(
|
|
453
|
-
`${outcome.rows} row(s) across the readiness groups, ${outcome.tasks} task file(s)`,
|
|
453
|
+
`${outcome.rows} row(s) across the readiness groups, ${outcome.backlog} backlog line(s), ${outcome.tasks} task file(s)`,
|
|
454
454
|
)
|
|
455
455
|
|
|
456
456
|
logStep(outcome.findings.length === 0 ? 'Clean' : 'Findings')
|
|
457
457
|
if (outcome.findings.length === 0) {
|
|
458
|
-
logInfo(
|
|
458
|
+
logInfo(
|
|
459
|
+
'every row resolves, every task sits on one surface, and each touches its own files',
|
|
460
|
+
)
|
|
459
461
|
} else {
|
|
460
462
|
for (const finding of outcome.findings) logWarn(describe(finding))
|
|
461
463
|
}
|
|
@@ -481,6 +483,7 @@ function reportValidation(
|
|
|
481
483
|
ok: true,
|
|
482
484
|
root,
|
|
483
485
|
rows: outcome.rows,
|
|
486
|
+
backlog: outcome.backlog,
|
|
484
487
|
tasks: outcome.tasks,
|
|
485
488
|
findings: outcome.findings,
|
|
486
489
|
untested: outcome.untested,
|