@erclx/aitk 3.37.1 → 3.38.1

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.37.1",
4
+ "version": "3.38.1",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -88,7 +88,7 @@ Behavior paths carry two spellings, the one a surface authors at and the one it
88
88
 
89
89
  - `claude/skills/` and `.claude/skills/`
90
90
  - `governance/rules/` and `.claude/rules/`
91
- - `standards/` and `.claude/standards/`, which is the authoring root and this repository's generated mirror rather than an install destination, since the corpus installs into no project
91
+ - `standards/`, which is the authoring root and reaches a reader by resolution rather than by an install, so it carries no `.claude/` spelling
92
92
  - `snippets/` and `.claude/snippets/`
93
93
  - `internal/` and `tooling/`, which hold the stack references and the seed documents a target is handed
94
94
  - `CLAUDE.md` at the repository root, named as a file because a path prefix reaches nothing that sits in no folder
@@ -77,7 +77,7 @@ Where an item's call is the operator's, pair it with a suggestion rather than a
77
77
 
78
78
  ## Step 6: write the index
79
79
 
80
- Write `00-overview.md` last, once the clusters are filed and the counts are real. It carries the format block, the cluster table, the verdict counts, the ready list, and the open questions.
80
+ Write `00-overview.md` last, once the clusters are filed and the counts are real. It carries the cluster table, the verdict counts, the ready list, and the open questions.
81
81
 
82
82
  Each open question in the index is a labeled markdown link to its owning item's heading anchor. The index points and the item owns, so no answer slot appears in the index. One question in two answerable places has no rule for which wins, and retrieval walks item headings, so an answer typed into the index is found by nothing and lost silently.
83
83
 
@@ -9,7 +9,7 @@ description: Why a standard needs its shape read from the meta-standard before a
9
9
 
10
10
  Without this skill, a standard is written from memory of what other standards look like. It arrives without the scope section that says what the file does not govern, so the next author cannot tell whether a rule belongs to it or to a sibling, and two standards end up claiming the same subject with no way to settle which one wins.
11
11
 
12
- The consumed copy is the second failure. This repository mirrors `standards/` into `.claude/standards/` and asserts the two match, so a toolkit standard written into the mirror fails that assertion while looking like a correct file. One write surface removes the choice: `standards/` is what the resolver reads first and what every author writes to, in the toolkit and in a project alike.
12
+ The second write surface is the second failure. A standard put under `.claude/standards/` looks like a correct file and reaches no resolver, since nothing installs the corpus into a project and no repository generates a copy there. One write surface removes the choice: `standards/` is what the resolver reads first and what every author writes to, in the toolkit and in a project alike.
13
13
 
14
14
  A standard written and left there is the third. The index entry and the context table are both generated or maintained downstream of the write, so a file that ships without them is catalogued nowhere and reached only by whoever already knew the path.
15
15
 
@@ -19,13 +19,13 @@ A standard written and left there is the third. The index entry and the context
19
19
  - Write to `standards/`, creating it when absent, since it is the one root the resolver reads and the only surface either kind of author has
20
20
  - Confirm the slug and the full body with the user before writing
21
21
  - Emit the written path in full, so the terminal can resolve it
22
- - State what the write implies for whoever owns the folder, since a toolkit standard needs the consumed copy and the index regenerated and a project-local one needs copying to the toolkit to ship
22
+ - State what the write implies for whoever owns the folder, since a toolkit standard needs the index regenerated and a project-local one needs copying to the toolkit to ship
23
23
 
24
24
  ## Must not
25
25
 
26
26
  - Work the standard's shape or frontmatter from memory
27
27
  - Write a snippet, which carries no frontmatter and answers to a different authoring contract
28
- - Write into `.claude/standards/`, which is this repository's generated mirror and reaches no resolver
28
+ - Write into `.claude/standards/`, which no repository generates and no resolver reads
29
29
 
30
30
  ## Out of scope
31
31
 
@@ -9,7 +9,7 @@ What each finding from `aitk context audit` means. The command surface, its flag
9
9
 
10
10
  ## Required sections
11
11
 
12
- The required-section check reports what does not declare `## Overview` and `## Layout`, the two sections `.claude/standards/context.md` marks required. The list is held in code beside the numeric checkpoints rather than parsed out of the standard, so it fails on a defect rather than on a rewrite of the wording around it. A heading at any level counts, because a domain that split into a folder carries its overview in a sibling named `overview.md` where the section is the `#` title and an `##` beneath it would repeat the filename. Matching exactly is what keeps `## Layout catalog` from satisfying `Layout`.
12
+ The required-section check reports what does not declare `## Overview` and `## Layout`, the two sections `standards/context.md` marks required. The list is held in code beside the numeric checkpoints rather than parsed out of the standard, so it fails on a defect rather than on a rewrite of the wording around it. A heading at any level counts, because a domain that split into a folder carries its overview in a sibling named `overview.md` where the section is the `#` title and an `##` beneath it would repeat the filename. Matching exactly is what keeps `## Layout catalog` from satisfying `Layout`.
13
13
 
14
14
  Which unit answers depends on the folder. Entries of the folder named under `.claude/` are one domain each, so each answers for itself and a finding names the entry. Entries of a folder a domain split into describe that one domain between them, so any sibling answers and a finding names the folder. Holding a split folder to the rule per file would report every child beside its `overview.md`, and rolling the named folder up would let one conforming entry stand in for every other domain sitting next to it.
15
15
 
@@ -19,7 +19,7 @@ It reports rather than gates by default, the closer call because a missing secti
19
19
 
20
20
  ## Reference form
21
21
 
22
- The reference-form check reports an entry naming a sibling entry by bare filename where `.claude/standards/context.md` asks for the path it sits at. A bare name resolves against whichever folder its reader is already in, so a domain that splits into subfolders strands every inbound reference and the break surfaces nowhere. A path is checkable, which is what makes the form rule worth measuring at all: once a reference spells its path, the citation gate resolves it and a split that moves the file fails the push.
22
+ The reference-form check reports an entry naming a sibling entry by bare filename where `standards/context.md` asks for the path it sits at. A bare name resolves against whichever folder its reader is already in, so a domain that splits into subfolders strands every inbound reference and the break surfaces nowhere. A path is checkable, which is what makes the form rule worth measuring at all: once a reference spells its path, the citation gate resolves it and a split that moves the file fails the push.
23
23
 
24
24
  A finding is a backticked filename carrying no folder, matched against the entries beside the one that wrote it. The backticks are required, since a filename in running prose is not a reference a reader follows.
25
25
 
@@ -33,7 +33,7 @@ It reports and never gates, which is the split the citation check already draws.
33
33
 
34
34
  ## Length
35
35
 
36
- Length quotes its checkpoint from `.claude/standards/context.md`: roughly 150 rendered lines for an entry, applied to each of the 60 entries the audit measures, 51 of them under `.claude/context/` and 9 diagram files beside them. It counts rendered lines rather than source lines, wrapping each line at 80 columns and summing the heights.
36
+ Length quotes its checkpoint from `standards/context.md`: roughly 150 rendered lines for an entry, applied to each of the 60 entries the audit measures, 51 of them under `.claude/context/` and 9 diagram files beside them. It counts rendered lines rather than source lines, wrapping each line at 80 columns and summing the heights.
37
37
 
38
38
  A line is wrapped at the width it renders at, so a link counts as its anchor text and a backticked path counts whole, which parts the rendered count from a source count wherever an entry cites a path in backticks rather than as a link. 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.
39
39
 
@@ -45,7 +45,7 @@ Only the third is mechanical, and it is the provenance check already reported be
45
45
 
46
46
  It reports and never gates, which the standard settles rather than the usual judgment-against-fact line: there is no hard cap, so an entry that answers all three and is still long is a correct outcome. The JSON record carries the joined findings as `length`, each with a `causes` array in the standard's order, and the jurisdiction the join reads as `entries[].governed`. That flag is what parts a provenance list measured and empty from one never scanned, which the count alone cannot say. The join is published rather than left to a consumer, since deriving it means restating which question the provenance count answers.
47
47
 
48
- 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.
48
+ Depth and bullet weight are quoted from `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.
49
49
 
50
50
  ## Tables
51
51
 
@@ -63,7 +63,7 @@ Fenced blocks are excluded, which keeps a pinned version in an install command f
63
63
 
64
64
  ## Narration
65
65
 
66
- The narration check reports a bullet that states the design a sibling bullet replaced instead of rewriting it. `.claude/standards/context.md` asks for the rewrite because the subject is still live and two bullets on one subject leave a reader to work out which of them is current, and no other measure sees that shape.
66
+ The narration check reports a bullet that states the design a sibling bullet replaced instead of rewriting it. `standards/context.md` asks for the rewrite because the subject is still live and two bullets on one subject leave a reader to work out which of them is current, and no other measure sees that shape.
67
67
 
68
68
  It reads structure rather than words, which the corpus decided. Measured across the 39 entries this toolkit held the day it shipped, the terms carrying clean signal for a supersession are too rare to catch anything: `superseded` appears twice, `previously` three times, and `formerly`, `originally`, and `at first` never. The one term that would have caught the case a review caught by hand is `now`, which appears 57 times across 24 entries in correct present-tense prose. A list including it reports 57 lines to catch one, and a list excluding it reports nothing.
69
69
 
@@ -101,13 +101,13 @@ The coverage report classifies each decision as carrying a countable claim, a st
101
101
 
102
102
  Three limits are stated on every run rather than hidden. The countable signal reads digits alone, so a measured claim written in words reads as uncounted. Entries are counted by heading, and one heading holding several decisions counts once. Nothing is stored, so an entry rewritten tomorrow is classified afresh the next time the verb runs and no verdict goes stale.
103
103
 
104
- The report gates nothing. Deciding whether a sentence states a claim is a judgment no parser settles, so the output names candidates for a reader. This answers a different question from the verification anchors `.claude/standards/architecture.md` describes, which record that one cited number was re-read. That mechanism says whether a marked figure held, and this one says how much of the record could be checked at all.
104
+ The report gates nothing. Deciding whether a sentence states a claim is a judgment no parser settles, so the output names candidates for a reader. This answers a different question from the verification anchors `standards/architecture.md` describes, which record that one cited number was re-read. That mechanism says whether a marked figure held, and this one says how much of the record could be checked at all.
105
105
 
106
106
  ## Which folders each check reaches
107
107
 
108
108
  The provenance, required-section, and narration checks cover `.claude/context/` alone, the reference-form check covers the split folders inside it, and length and the table finding reach every audited folder.
109
109
 
110
- What narrows the three 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, and a diagram entry carries a heading per kind rather than a run of bullets deciding anything. 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.
110
+ What narrows the three is stated in `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, and a diagram entry carries a heading per kind rather than a run of bullets deciding anything. 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.
111
111
 
112
112
  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.
113
113
 
@@ -35,12 +35,18 @@ The set is closed rather than derived, so widening it is a deliberate change to
35
35
 
36
36
  ## How a match is decided
37
37
 
38
- A sentence has to carry an assertion verb (`loads`, `ships`, `carries`, `holds`, `counts`, `totals`, `documents`, `declares`, `installs`, `lists`, `contains`, `comprises`) or an article (`the`, `a`, `an`) immediately ahead of the number, which is immediately ahead of the catalog noun, with one optional qualifying word between the number and the noun (`sixty-one shipped skills`). The number reads either as digits or as a spelled-out cardinal through ninety-nine, since this corpus states a catalog size in words as often as in digits.
38
+ A sentence has to carry an assertion verb (`loads`, `ships`, `carries`, `holds`, `counts`, `totals`, `documents`, `declares`, `installs`, `lists`, `contains`, `comprises`, `authors`), the quantifier `all`, or an article (`the`, `a`, `an`) immediately ahead of the number, which is immediately ahead of the catalog noun, with one optional qualifying word between the number and the noun (`sixty-one shipped skills`). The number reads either as digits or as a spelled-out cardinal through ninety-nine, since this corpus states a catalog size in words as often as in digits.
39
+
40
+ Nothing stands between the trigger and the number, which is the rule an author writing a new count has to know. It separates `the toolkit authors 69 rules` and `took all 69 rules`, both read, from `a domain of 55 skills` and `denominator of sixty-one shipped skills`, neither of them read. A figure meant as a past state takes a date in the same sentence instead, which reads the sentence past whatever its shape.
41
+
42
+ ## How the trigger set grew
39
43
 
40
44
  The verb gate is not the first design tried. A bare number next to a catalog noun anywhere in the tracked corpus returned 290 findings against a repository whose actionable instance count was one. Reading that run showed why: `18 rules citing a standard`, `21 skill bodies`, and `eight internal skills` all pair a number with a catalog noun while naming a subset, an example, or a different catalog than the one matched, and that shape dominates ordinary prose. Every instance this sweep was written against reads the number as the direct object of a verb asserting the catalog's own total, and gating on that verb is what took the false-positive count from 290 to five on the same tree.
41
45
 
42
46
  The article half was added after a first review of the shipped design found a live miss: `.claude/context/development/gates.md` stated a stale audit total while the tree held 20, and the verb gate never reached it, since that sentence puts its verb after the noun rather than ahead of the number. Widening the trigger set to admit an article ahead of the number closed that gap.
43
47
 
48
+ The quantifier `all` and the verb `authors` joined on the same evidence, two more live misses failing only the trigger test. Allowing words to stand between the trigger and the number was the other candidate, measured and declined: at widths of one, two, and three it reached 77, 104, and 132 sentences against a baseline of 65, missed `took all 69 rules` at every width, and reached `the toolkit authors 69 rules` only at a width admitting the indirect-noun shape below. The two words reach both at 75 sentences and no false positive.
49
+
44
50
  ## The plausibility filter
45
51
 
46
52
  The verb gate alone still left four false positives standing: `carries two rules about a standard's own lifecycle`, `holds one rule or one fact` (twice, once per mirrored copy), and `documents two similar commands`. Each pairs an assertion verb with a catalog noun used for something the `rules` or `commands` catalog does not mean, and what tells those apart from the one live finding is magnitude: a catalog this sweep tracks drifts by a few members between the day a sentence was written and the day it is read, so a genuine staleness claim sits near the true count. `2` beside a true count of `59` is not a catalog that shrank, it is a different `rules` entirely.
@@ -51,7 +57,9 @@ The article gate is looser than the verb list and carries a real cost. Re-runnin
51
57
 
52
58
  ## What it does not measure
53
59
 
54
- A delta phrased as a transition (`from fourteen to fifteen`), a fraction (`thirteen of sixteen`), and a total reached through an indirect noun (`denominator of sixty-one shipped skills`) are all catalog-size claims this corpus carries, and none matches the assertion-verb shape this reads. Each is a known gap rather than an oversight, left for a wider pass once this design's own false-positive rate is measured over more than one run.
60
+ A delta phrased as a transition (`from fourteen to fifteen`), a fraction (`thirteen of sixteen`), and a total reached through an indirect noun (`denominator of sixty-one shipped skills`) are all catalog-size claims this corpus carries, and none matches the trigger shape this reads. Each stays a known gap. The false-positive rate that gated closing them has a measurement behind it now, and what it showed is that the widening these three need is the one that costs a false positive rather than the one that does not.
61
+
62
+ A second figure in a sentence whose first figure already matched is a fourth gap and a structural one, since one match is taken per catalog per sentence. `authors 69 rules under governance/rules/ and consumes 54 into .claude/rules/` is read for its 69 alone, which is correct here because the two figures name different populations, and a sentence stating one catalog twice would go unread the same way.
55
63
 
56
64
  A calendar date (`2026-08-21`) or a backticked commit reference in the same sentence reads the whole sentence past, since that is how this corpus already marks a figure as a historical record rather than a live claim. `.claude/ARCHITECTURE.md` and the context entries carry a figure this way deliberately, and every one of them stays correct forever.
57
65
 
@@ -76,7 +76,7 @@ standard exits 1 after listing the catalog on stderr.
76
76
  Two roots answer, in order: `standards/` at the working directory, then the
77
77
  corpus inside the package. A project that authors standards of its own uses the
78
78
  first, and this repository's own authoring root is the same path. `.claude/standards/`
79
- is not among them. It is this repository's generated mirror, and a copy an older
79
+ is not among them, and no repository writes one any more. A copy an older
80
80
  toolkit installed into a target resolves nothing.
81
81
 
82
82
  There is no citation closure to compute, since nothing is copied. A standard
@@ -78,4 +78,4 @@ Skills branch on the reason rather than on the exit code:
78
78
  aitk intake list toolkit-overview --unread --json | jq -r '.clusters[] | .cluster'
79
79
  ```
80
80
 
81
- For the folder layout, the item format, the answer contract, and retrieval, see `.claude/standards/intake.md`.
81
+ For the folder layout, the item format, the answer contract, and retrieval, see `standards/intake.md`.
@@ -41,7 +41,7 @@ Freezing the spellings gave up a property worth naming. They were derived by app
41
41
 
42
42
  A set shipped empty is reported rather than passed. It finds nothing and would exit clean, which reports a corpus nobody checked as a corpus carrying no violation, so the run names the empty set and exits `1`. The sets ship with the package, so a defect in the build is the only cause left.
43
43
 
44
- `aitk standards <name>` still resolves a standard under `.claude/standards/`, then the authoring root, then the package corpus, and prints it, so the human catalog reads without a project copy on disk.
44
+ `aitk standards <name>` still resolves a standard at the authoring root, then the package corpus, and prints it, so the human catalog reads without a project copy on disk.
45
45
 
46
46
  ## What each check reports
47
47
 
@@ -23,7 +23,7 @@ aitk records validate intake --json
23
23
 
24
24
  It reads and never writes, and the reason splits by kind. A session record is per-machine scratch with no history behind it, so a repair that guessed wrong could not be undone. A standard installs into every target and is cited by bare filename, so a rename the verb performed would reach further than the file it moved, which is why a finding naming one says so.
25
25
 
26
- `standards` reads the authoring root at `standards/` where it exists and the installed copy at `.claude/standards/` otherwise. The authoring root wins because the installed tree is generated from it in the toolkit, where a fix written to the copy is discarded by the next regen. A project that consumed the corpus holds only the second, so one precedence serves both. The walk stays flat, matching install and the catalog.
26
+ `standards` reads the authoring root at `standards/` where it exists and a copy at `.claude/standards/` otherwise. The authoring root wins because it is the only tree anyone authors in and the only one the resolver answers from, so a finding fixed anywhere else is fixed where nothing reads it. No repository generates the second candidate any more, which leaves it as a floor under a target holding a copy an older toolkit installed. The walk stays flat, matching the catalog.
27
27
 
28
28
  Nothing fires it automatically. The five record folders are gitignored, so the standards-audit hook exits early on them and any check reading changed files from git never lists one. The corpus is tracked and still unreached, since the markdown audit reads content across the files git lists and rules on no filename. The verb runs at the moment a session claims the record is finished, which is the same placement `aitk tasks validate` takes over the board.
29
29
 
@@ -70,7 +70,7 @@ Skills branch on the findings rather than on the exit code:
70
70
  aitk records validate plans --json | jq -r '.findings[] | "\(.kind): \(.subject)"'
71
71
  ```
72
72
 
73
- For the shapes each check enforces, see `.claude/standards/plan.md`, `.claude/standards/groundwork.md`, `.claude/standards/intake.md`, `.claude/standards/memory.md`, and `.claude/standards/standard.md`.
73
+ For the shapes each check enforces, see `standards/plan.md`, `standards/groundwork.md`, `standards/intake.md`, `standards/memory.md`, and `standards/standard.md`.
74
74
 
75
75
  ## Migrate
76
76
 
@@ -132,7 +132,7 @@ aitk records push --json
132
132
  aitk records pull
133
133
  ```
134
134
 
135
- The backed folders are `diagrams`, `groundwork`, `intake`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, all under `.claude/`. Seven of them are the Claude ignore group the claude manifest ships, minus three entries: `.claude/.tmp`, which is deletable without loss, `.claude/worktrees/`, whose contents belong to the project repository already, and `.claude/.records.git/`, which is the history the rest are pushed into. `diagrams` and `proposals` are the two that group does not carry at all, since a target still tracks its own copies of both, which is why the list is spelled out rather than derived. Each name is a top-level record folder and every archive sits inside the one it archives, so the list stays at one entry per surface however many archives appear. It is a constant rather than configuration, and it deliberately does not match the six record kinds `validate` hardcodes.
135
+ The backed folders are `diagrams`, `groundwork`, `intake`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, all under `.claude/`. Eight of them are the Claude ignore group the claude manifest ships, minus three entries: `.claude/.tmp`, which is deletable without loss, `.claude/worktrees/`, whose contents belong to the project repository already, and `.claude/.records.git/`, which is the history the rest are pushed into. `diagrams` is the one that group does not carry at all, since a target still tracks its own copies, which is why the list is spelled out rather than derived. Each name is a top-level record folder and every archive sits inside the one it archives, so the list stays at one entry per surface however many archives appear. It is a constant rather than configuration, and it deliberately does not match the six record kinds `validate` hardcodes.
136
136
 
137
137
  Records are gitignored by design, so the history lives in a second git directory at `.claude/.records.git` with `.claude/` as its work tree. Every path stays where it is, which is what a separate checkout could not do. The verbs stage the nine folders by explicit pathspec with `--force`, so nothing outside them can enter the index however the ignore rules read, and the project working tree and its index are never touched. Each pathspec is a bare folder name and git reads it against the current directory rather than against the work tree the same call names, so the invocation carries `-C` at the work tree beside the other two flags. That is what lets either verb run from a linked worktree under `.claude/worktrees/`, which sits inside the records work tree and would otherwise prefix every name with its own path.
138
138
 
@@ -146,7 +146,7 @@ git --git-dir=.claude/.records.git remote add origin <private-repo-url>
146
146
  printf '.claude/.records.git/\n' >> .gitignore
147
147
  ```
148
148
 
149
- The ignore line is part of the setup rather than part of the installed gitignore group, because the person running these commands is the one who creates the directory. Every path that group ships is written by a toolkit command without anyone asking, and this one is not, so a project that never sets up records would carry a rule for a directory it will never hold. Leaving it out and adding it here puts the rule where whoever creates the directory reads it.
149
+ The ignore line is repeated here rather than left to the install, because the person running these commands is the one who creates the directory and the rule is worth reading beside the command that needs it. The claude manifest ships `.claude/.records.git/` as the first entry of its group, so a project that ran `aitk claude sync` already carries it and this line is a no-op there. What the group buys is the project that never sets records up: it holds a rule for a directory it will never create, which costs nothing, and the backed-folder derivation above subtracts that entry from the group rather than from a list it does not sit in.
150
150
 
151
151
  Point it at a private repository, and at one that is not a remote of the project. Records carry the memory pen, the review reports, and the groundwork trails, so a public project publishes all of it to anyone who fetches all refs. `push` compares the configured origin against every remote of the project and refuses on a match. A read of that list which fails refuses as well, since an empty list clears the comparison for every origin and a gate that passes on its own failure is no gate.
152
152
 
@@ -191,4 +191,4 @@ Skills branch on the findings rather than on the exit code:
191
191
  aitk tasks validate --json | jq -r '.findings[] | "\(.kind): \(.subject)"'
192
192
  ```
193
193
 
194
- For the board format, the `Pull request:` line, and the archive rules, see `.claude/standards/tasks.md`.
194
+ For the board format, the `Pull request:` line, and the archive rules, see `standards/tasks.md`.
@@ -89,7 +89,7 @@ A constraint naming a track in flight carries the same problem past the handoff,
89
89
 
90
90
  `.claude/plans/`, `.claude/review/`, and `.claude/memory/` all resolve at the main worktree root, so artifacts created in any session are visible from any sibling worktree. A session inside a worktree reads them directly, since the file-editing tools refuse a main-root path but `Read` resolves normally. It writes a whole file through the shell and makes a change inside an existing file through an `aitk` verb, which resolves the main root in-process. See [Claude Code and git worktrees](../wiki/claude/claude-worktrees.md) for the full rule and the domain-level fan-out guidance.
91
91
 
92
- The plan's shape is fixed by `.claude/standards/plan.md`: the section list, the filename, the lifecycle, and the contract its questions keep. Every question carries a `- Suggested:` line and an empty `- Answer:` slot, and a blank answer accepts the suggestion at execution time. That default is what makes a plan decision-ready in one pass, and it is the opposite of the contract an intake folder keeps, where an empty slot means nobody reached the item.
92
+ The plan's shape is fixed by `standards/plan.md`: the section list, the filename, the lifecycle, and the contract its questions keep. Every question carries a `- Suggested:` line and an empty `- Answer:` slot, and a blank answer accepts the suggestion at execution time. That default is what makes a plan decision-ready in one pass, and it is the opposite of the contract an intake folder keeps, where an empty slot means nobody reached the item.
93
93
 
94
94
  An execution that picks other than the suggestion rewrites the `- Suggested:` line as `overridden at execution to <pick>,` followed by the measurement that moved it, and leaves the slot blank. That fixed phrase is how a reader of the archived plan tells an override from a suggestion the execution accepted, since an authored suggestion often carries a number of its own. The same deviation takes one line in the open task's `## Findings`, which is the register that survives the plan being archived. A deviation from a question somebody already answered goes back to whoever answered it instead, since a filled slot is a decision already made.
95
95
 
@@ -20,7 +20,7 @@ Claude Code reads both and writes the implementation. Works for CLI tools, inter
20
20
 
21
21
  ### Seed shape
22
22
 
23
- The toolkit seed in `tooling/claude/seeds/.claude/DESIGN.md` ships a token-table template with a starting set of roles, and `.claude/standards/design.md` carries the same tables under `## Template` with placeholder rows. The column headers are what the renderer parses, so they stay verbatim in either, while the rows and values are the project's own.
23
+ The toolkit seed in `tooling/claude/seeds/.claude/DESIGN.md` ships a token-table template with a starting set of roles, and `standards/design.md` carries the same tables under `## Template` with placeholder rows. The column headers are what the renderer parses, so they stay verbatim in either, while the rows and values are the project's own.
24
24
 
25
25
  The `aitk:claude-design-extract` skill drafts the file, sourcing tokens from a project's existing prose and CLI UI surfaces, or proposing them from `.claude/REQUIREMENTS.md` and a `## Personality` paragraph when no UI code exists yet. `aitk design render` writes an HTML plus CSS preview to `.claude/review/design/` for eyeballing the current system without leaving Claude Code. See `.claude/context/design.md`.
26
26
 
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  description: Enforce shape, scope, and success criterion for authoring convention files
3
3
  paths:
4
- - '.claude/standards/**/*.md'
5
4
  - 'standards/**/*.md'
6
5
  ---
7
6
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "3.37.1",
4
+ "version": "3.38.1",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+ set -o pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
7
+
8
+ GITIGNORE="$PROJECT_ROOT/.gitignore"
9
+ MANIFEST="$PROJECT_ROOT/tooling/claude/manifest.toml"
10
+ SECTION="# Claude"
11
+
12
+ # Divergences this repository has decided to keep, one `<entry>|<reason>` per
13
+ # line, matched after the trailing slash is stripped. Each names a path the
14
+ # enclosing repository ignores and the claude manifest deliberately does not
15
+ # ship, so a target keeps tracking it.
16
+ #
17
+ # This is the canonical list. `.gitignore` and the manifest carry a pointer
18
+ # beside their own entries rather than a second copy of the reason, and
19
+ # `.claude/context/tooling.md` carries the narrative behind both.
20
+ #
21
+ # A sanction that no longer describes a divergence fails the same as an
22
+ # unsanctioned one. An exception outliving its reason is what teaches the next
23
+ # reader to widen the list rather than to read it.
24
+ SANCTIONED=(
25
+ ".claude/diagrams|a target that tracks its diagrams keeps the context audit's default coverage of them, which ignoring the folder for every target would take away. This repository backs the folder through its records remote instead."
26
+ ".claude/README.md|the landing page a records pull writes back into a repository that set up a records remote. A target has not, so it never receives the file."
27
+ )
28
+
29
+ if [ ! -f "$GITIGNORE" ]; then
30
+ echo "No .gitignore at ${GITIGNORE#"$PROJECT_ROOT/"}, ignore parity unverifiable." >&2
31
+ exit 1
32
+ fi
33
+
34
+ if [ ! -f "$MANIFEST" ]; then
35
+ echo "No claude manifest at ${MANIFEST#"$PROJECT_ROOT/"}, ignore parity unverifiable." >&2
36
+ exit 1
37
+ fi
38
+
39
+ # A gitignore pattern and a manifest entry describe the same folder whether or
40
+ # not either spells the trailing slash, so presence is compared with it dropped.
41
+ # `.claude/.tmp` and `.claude/.tmp/` are the live instance of that pair.
42
+ normalize() {
43
+ printf '%s\n' "${1%/}"
44
+ }
45
+
46
+ # Every pattern the file carries, comments and blank lines dropped. The whole
47
+ # file rather than one header, because `.claude/teach/` sits under a header of
48
+ # its own and reading the `# Claude` header alone would report it missing from a
49
+ # list that carries it.
50
+ gitignore_patterns() {
51
+ awk '{ sub(/[[:space:]]+$/, "") } $0 ~ /^[[:space:]]*#/ || $0 == "" { next } { print }' "$GITIGNORE"
52
+ }
53
+
54
+ # Every entry of the named array in the manifest's `[gitignore]` table. The
55
+ # array is accumulated to its closing bracket before the quoted strings are read
56
+ # out, so a formatter wrapping it across lines does not silently empty this.
57
+ manifest_entries() {
58
+ awk -v key="\"$SECTION\"" '
59
+ /^\[/ { table = $0 }
60
+ table != "[gitignore]" { next }
61
+ index($0, key) == 1 { collecting = 1; buf = "" }
62
+ collecting { buf = buf $0 }
63
+ collecting && index($0, "]") > 0 {
64
+ collecting = 0
65
+ buf = substr(buf, index(buf, "["))
66
+ count = split(buf, parts, "\"")
67
+ for (i = 2; i <= count; i += 2) print parts[i]
68
+ }
69
+ ' "$MANIFEST"
70
+ }
71
+
72
+ contains() {
73
+ local needle=$1
74
+ shift
75
+ local candidate
76
+ for candidate in "$@"; do
77
+ [ "$candidate" = "$needle" ] || continue
78
+ return 0
79
+ done
80
+ return 1
81
+ }
82
+
83
+ ignored=()
84
+ while IFS= read -r pattern; do
85
+ [ -n "$pattern" ] || continue
86
+ ignored+=("$(normalize "$pattern")")
87
+ done < <(gitignore_patterns)
88
+
89
+ shipped=()
90
+ while IFS= read -r entry; do
91
+ [ -n "$entry" ] || continue
92
+ shipped+=("$(normalize "$entry")")
93
+ done < <(manifest_entries)
94
+
95
+ # An empty read on either side is a parse that failed rather than a repository
96
+ # ignoring nothing, and reporting parity off it would say the two lists agree
97
+ # having compared none of their entries.
98
+ if [ ${#ignored[@]} -eq 0 ]; then
99
+ echo "No patterns read from .gitignore, ignore parity unverifiable." >&2
100
+ exit 1
101
+ fi
102
+
103
+ if [ ${#shipped[@]} -eq 0 ]; then
104
+ echo "No entries read from the \"$SECTION\" array in ${MANIFEST#"$PROJECT_ROOT/"}, ignore parity unverifiable." >&2
105
+ exit 1
106
+ fi
107
+
108
+ failures=""
109
+ sanction_notes=""
110
+
111
+ # Entries the manifest ships that this repository does not ignore. A target is
112
+ # told to ignore a folder the toolkit itself tracks, which no decision sanctions,
113
+ # so this direction carries no exception list.
114
+ for entry in "${shipped[@]}"; do
115
+ contains "$entry" "${ignored[@]}" && continue
116
+ failures="$failures $entry is shipped by the manifest and absent from .gitignore"$'\n'
117
+ done
118
+
119
+ # Claude-scoped patterns this repository ignores that the manifest does not
120
+ # ship. Scoped to `.claude/` because the manifest is the claude stack and says
121
+ # nothing about `node_modules/` or `.env`.
122
+ for pattern in "${ignored[@]}"; do
123
+ case "$pattern" in
124
+ .claude/*) ;;
125
+ *) continue ;;
126
+ esac
127
+ contains "$pattern" "${shipped[@]}" && continue
128
+
129
+ matched=false
130
+ for sanction in "${SANCTIONED[@]}"; do
131
+ [ "${sanction%%|*}" = "$pattern" ] || continue
132
+ matched=true
133
+ sanction_notes="$sanction_notes $pattern stays out of the manifest: ${sanction#*|}"$'\n'
134
+ break
135
+ done
136
+
137
+ if [ "$matched" = false ]; then
138
+ failures="$failures $pattern is ignored here and absent from the manifest"$'\n'
139
+ fi
140
+ done
141
+
142
+ # A sanction naming a path that is no longer divergent, either because the
143
+ # manifest took it or because .gitignore dropped it.
144
+ for sanction in "${SANCTIONED[@]}"; do
145
+ entry="${sanction%%|*}"
146
+ if contains "$entry" "${ignored[@]}" && ! contains "$entry" "${shipped[@]}"; then
147
+ continue
148
+ fi
149
+ failures="$failures $entry is sanctioned as a divergence and is no longer one"$'\n'
150
+ done
151
+
152
+ if [ -n "$failures" ]; then
153
+ echo "The ignore set a target receives disagrees with this repository's own:" >&2
154
+ printf '%s' "$failures" >&2
155
+ echo "Add the entry to the \"$SECTION\" array in ${MANIFEST#"$PROJECT_ROOT/"} and to .gitignore, or record it in SANCTIONED in scripts/core/check-ignore-parity.sh with the reason it stays apart." >&2
156
+ exit 1
157
+ fi
158
+
159
+ if [ -n "$sanction_notes" ]; then
160
+ echo "Sanctioned divergences from the ignore set a target receives:"
161
+ printf '%s' "$sanction_notes"
162
+ fi
@@ -18,7 +18,6 @@ mirror_dir() {
18
18
  done < <(find "$src" -type f "${find_args[@]}" | sort)
19
19
  }
20
20
 
21
- mirror_dir "$PROJECT_ROOT/standards" "$PROJECT_ROOT/.claude/standards" -name "*.md" -not -path "*/bundled/*"
22
21
  mirror_dir "$PROJECT_ROOT/snippets" "$PROJECT_ROOT/.claude/snippets" -name "*.md"
23
22
 
24
23
  # `internal/` is the surface the plugin does not symlink. Mirrored on its own so
@@ -261,7 +261,7 @@ main() {
261
261
 
262
262
  log_step "Consumed copies"
263
263
  run_check "bash $PROJECT_ROOT/scripts/core/regen-claude-copies.sh" "Consumed-copy regen failed"
264
- assert_no_drift ".claude/standards .claude/snippets .claude/internal .claude/rules" "Consumed copies drifted. Run bun run check and commit .claude/standards, .claude/snippets, .claude/internal, and .claude/rules."
264
+ assert_no_drift ".claude/snippets .claude/internal .claude/rules" "Consumed copies drifted. Run bun run check and commit .claude/snippets, .claude/internal, and .claude/rules."
265
265
  log_info "Consumed copies clean"
266
266
 
267
267
  # Only the HTML is asserted. The PNG beside it is a chromium render whose bytes
@@ -278,6 +278,16 @@ main() {
278
278
  assert_no_drift "claude/skills/toolkit-cli/SKILL.md" "The overwrite contract drifted from what the stacks hold. Run bun run check and commit claude/skills/toolkit-cli/SKILL.md."
279
279
  log_info "Tooling paths clean"
280
280
 
281
+ # The claude manifest is the only route a target's ignore set travels, and it
282
+ # is hand-maintained beside this repository's own `.gitignore` with nothing
283
+ # comparing the two. A drift between them reaches every target on the next
284
+ # `aitk tooling sync` and surfaces to nobody, which is why this gates rather
285
+ # than reports. It is not an `assert_no_drift`: no generator produces either
286
+ # list, so there is nothing to regenerate and diff.
287
+ log_step "Ignore parity"
288
+ run_check "bash $PROJECT_ROOT/scripts/core/check-ignore-parity.sh" "The ignore set a target receives disagrees with this repository's own."
289
+ log_info "Ignore parity clean"
290
+
281
291
  log_step "Skill paths"
282
292
  run_check "bash $PROJECT_ROOT/scripts/core/check-skill-paths.sh" "Shipped skills reference a repo-local path."
283
293
  log_info "Skill paths clean"
@@ -570,7 +570,7 @@ export const AUDITS: readonly AuditSpec[] = [
570
570
  // `standards` is the one tracked kind here, so it takes none of the
571
571
  // per-machine default the other five inherit from their corpus. A target
572
572
  // reads standards through `aitk standards` rather than a copy in its own
573
- // tree, so carrying neither `standards/` nor `.claude/standards/` is the
573
+ // tree, so carrying no standards folder at all is the
574
574
  // ordinary state of every project but this repository.
575
575
  ...(kind === 'standards' && {
576
576
  absentReasons: ['no-folder'] satisfies RecordRefusal[],
@@ -5,11 +5,15 @@ import { INDEX_FILE, listIndexes } from '@/indexes/walk'
5
5
  /**
6
6
  * Folder names under `.claude/` audited by default.
7
7
  *
8
- * The index-plus-entry contract on its own over-collects. `.claude/standards/`
9
- * also carries a generated index beside entries with frontmatter, and auditing
10
- * it would measure the consumed copy of `standards/` against a rule written for
11
- * per-domain narrative. This list names the folders `standards/context.md`
12
- * governs, and `--folder` admits another without an edit here.
8
+ * A named list rather than the index-plus-entry contract read off disk, so a
9
+ * generated tree satisfying that contract is never measured against a rule
10
+ * written for per-domain narrative. This list names the folders
11
+ * `standards/context.md` governs, and `--folder` admits another without an edit
12
+ * here.
13
+ *
14
+ * It doubles as the citation check's scope, since `citationPattern` spells only
15
+ * these names. A `.claude/` folder left off the list is never resolved, so a
16
+ * path into one goes stale silently rather than failing a push.
13
17
  */
14
18
  export const DEFAULT_FOLDERS: readonly string[] = [
15
19
  'context',
@@ -78,6 +78,16 @@ function sentencesOf(line: string): string[] {
78
78
  * asserting the catalog's own total, `the full entry loads 59 skills` being
79
79
  * the live one, and gating the match on that verb is what a full re-read of
80
80
  * the 290 showed removing all of them while keeping that one.
81
+ *
82
+ * `authors` joined the list on a corpus measurement rather than on the shape
83
+ * argument above. `.claude/context/development/regeneration.md` states `the
84
+ * toolkit authors 69 rules under governance/rules/`, which is the assertion
85
+ * shape this list was built for with a verb the list did not carry, and the
86
+ * word costs one further sentence across the whole corpus and no finding.
87
+ * Extending the vocabulary is the narrow half of this fix and it is taken
88
+ * knowingly: a list of thirteen verbs still grows by whatever the next author
89
+ * writes, and the measurement below is what ruled out the general axis that
90
+ * would have replaced it.
81
91
  */
82
92
  const ASSERTION_VERBS = [
83
93
  'loads',
@@ -92,8 +102,26 @@ const ASSERTION_VERBS = [
92
102
  'lists',
93
103
  'contains',
94
104
  'comprises',
105
+ 'authors',
95
106
  ]
96
107
 
108
+ /**
109
+ * A quantifier asserting the whole of a catalog immediately ahead of the
110
+ * number, which is the one trigger class that states a total outright rather
111
+ * than by implication.
112
+ *
113
+ * `all 69 rules` names the catalog entire, where `the 69 rules` leaves a
114
+ * reader to decide whether a qualifier narrows it. That makes this class
115
+ * tighter than the articles below rather than looser, and the corpus agrees:
116
+ * adding it reached 9 further sentences and produced 2 further findings, both
117
+ * of them real staleness this repository then repaired.
118
+ *
119
+ * `every` and `both` were measured beside `all` and reached nothing, so
120
+ * neither ships. A quantifier earns a place here by naming a sentence in the
121
+ * corpus, not by belonging to the same part of speech.
122
+ */
123
+ const QUANTIFIERS = ['all']
124
+
97
125
  /**
98
126
  * An article immediately ahead of the number, admitting the shape a bare verb
99
127
  * gate cannot reach: `the twelve audits read gitignored folders` asserts the
@@ -120,8 +148,9 @@ const ASSERTION_VERBS = [
120
148
  const ARTICLES = ['the', 'a', 'an']
121
149
 
122
150
  /**
123
- * Matches a catalog's stated size: an assertion verb or an article, the
124
- * number, an optional single qualifying word, then the noun in either number.
151
+ * Matches a catalog's stated size: an assertion verb, a quantifier, or an
152
+ * article, the number, an optional single qualifying word, then the noun in
153
+ * either number.
125
154
  *
126
155
  * The optional word between the number and the noun is what reaches a form
127
156
  * like `installs 11 shipped snippets` without also reaching past an
@@ -130,16 +159,34 @@ const ARTICLES = ['the', 'a', 'an']
130
159
  * double as its match narrows the corpus this reads without hand-listing
131
160
  * every irregular plural, since none of these six is irregular.
132
161
  *
133
- * What this drops along with the false positives: `denominator of sixty-one
134
- * shipped skills` and `exposed all 59` both state a real catalog total and
135
- * neither puts a verb or an article directly ahead of the number, so both
136
- * read past. The gap between the trigger and the number is a second axis
137
- * this could widen once this design's own false-positive rate is measured
138
- * over more than the two runs behind it so far.
162
+ * The gap between the trigger and the number was the second axis this comment
163
+ * used to name as open to widening once the false-positive rate had been
164
+ * measured over more than two runs. It was measured over the whole corpus at
165
+ * 518 files and 31,836 sentences and it is not the axis to take. Widths of
166
+ * one, two, and three optional words reached 77, 104, and 132 sentences
167
+ * against a baseline of 65, and the two live misses that motivated the
168
+ * measurement came back missed at every width but one: no width reaches `took
169
+ * all 69 rules`, whose nearest verb or article is the `The` opening the
170
+ * sentence, with five words standing between it and the number, and the width
171
+ * that does reach `the toolkit authors 69 rules` is the same
172
+ * width that admits `a domain of 55 skills`, which is the indirect-noun shape
173
+ * this module already records as out of scope. Widening the gap buys one of
174
+ * two misses at 60 percent more reach and a false positive of a class already
175
+ * named.
176
+ *
177
+ * The trigger vocabulary carries both misses instead, at 75 sentences reached
178
+ * against 65 and no false positive: `all` reaches the first and `authors` the
179
+ * second. Two of the four findings that shape reports were new, and both were
180
+ * real. Measured at `ffe7e7c6` on 2026-08-28.
181
+ *
182
+ * What this still drops: `denominator of sixty-one shipped skills` states a
183
+ * real catalog total through an indirect noun and reads past, which the gap
184
+ * measurement above is the argument for leaving alone rather than an
185
+ * oversight.
139
186
  */
140
187
  function buildMatcher(catalog: Catalog): RegExp {
141
188
  const [singular, plural] = catalog.nouns
142
- const triggers = [...ASSERTION_VERBS, ...ARTICLES].join('|')
189
+ const triggers = [...ASSERTION_VERBS, ...QUANTIFIERS, ...ARTICLES].join('|')
143
190
  return new RegExp(
144
191
  `\\b(?:${triggers})\\s+(${NUMBER_PATTERN})(?:\\s+[a-z]+)?\\s+(?:${singular}|${plural})\\b`,
145
192
  'gi',
@@ -187,10 +234,19 @@ function isPlausibleClaim(stated: number, actual: number): boolean {
187
234
  * What it does not measure: a delta phrased as a transition (`from fourteen
188
235
  * to fifteen`), a fraction (`thirteen of sixteen`), and a total reached
189
236
  * through an indirect noun (`denominator of sixty-one shipped skills`) are
190
- * all catalog-size claims this corpus carries, and none matches the
191
- * assertion-verb shape this reads. Each is a known gap rather than an
192
- * oversight, left for a wider pass once this one's false-positive rate is
193
- * measured.
237
+ * all catalog-size claims this corpus carries, and none matches the trigger
238
+ * shape this reads. Each stays a known gap. The rate that used to gate
239
+ * closing them is measured now, and what it showed is that the widening they
240
+ * would need is the one that costs a false positive rather than the one that
241
+ * does not, which `buildMatcher` records with its numbers.
242
+ *
243
+ * A second figure in a sentence whose first figure already matched is a
244
+ * fourth gap and a structural one: `regex.exec` takes one match per catalog
245
+ * per sentence, so `authors 69 rules under governance/rules/ and consumes 54
246
+ * into .claude/rules/` is read for its 69 alone. Reaching the 54 would be
247
+ * wrong here rather than better, since it counts the consumed mirror rather
248
+ * than the source catalog this sweep tracks, but a sentence stating one
249
+ * catalog twice would go unread the same way.
194
250
  */
195
251
  export async function scanCounts(root: string): Promise<CountsReport> {
196
252
  const scope = await resolveMarkdown(root, [])
@@ -12,10 +12,12 @@ import { gitEnv } from '@/git-env'
12
12
  * list is spelled out rather than read off that group so adding an ignore entry
13
13
  * cannot silently enlarge the payload.
14
14
  *
15
- * `diagrams` and `proposals` are the two names the manifest group does not
16
- * carry, so a target tracks both where this repository ignores them. That is
17
- * the second reason to spell the list out: the manifest bounds it rather than
18
- * producing it, and no count taken off that group matches this one.
15
+ * `diagrams` is the one name the manifest group does not carry, so a target
16
+ * tracks it where this repository ignores it. That is the second reason to
17
+ * spell the list out: the manifest bounds it rather than producing it, and no
18
+ * count taken off that group matches this one. `scripts/core/check-ignore-parity.sh`
19
+ * compares the two lists and holds the reason `diagrams` stays apart, so the
20
+ * one difference is enforced rather than assumed.
19
21
  *
20
22
  * The manifest group is the one this reads rather than the enclosing
21
23
  * repository's own `.gitignore`, which spreads the same entries across two
@@ -33,7 +33,8 @@ export interface ResolvedStandard {
33
33
 
34
34
  /**
35
35
  * The roots a standard resolves against. No toolkit standard installs into a
36
- * project any more, so `.claude/standards/` is gone from the list and a target
36
+ * project any more, so `.claude/standards/` is gone from the list, no repository
37
+ * generates one, and a target
37
38
  * carries neither entry below: the package corpus is the only root that
38
39
  * answers there, which is what leaves no precedence to reason about.
39
40
  *
@@ -13,7 +13,7 @@ const CLAUDE_DIR = '.claude'
13
13
 
14
14
  /**
15
15
  * A `CLAUDE.md` past this many lines has more in it than the always-load tier
16
- * is for. The figure is the checkpoint `.claude/standards/context.md` sets for
16
+ * is for. The figure is the checkpoint `standards/context.md` sets for
17
17
  * a context entry, raised because `CLAUDE.md` legitimately carries more than
18
18
  * one domain. It gates a proposal rather than a failure, so being approximate
19
19
  * costs a line an operator can ignore.
@@ -14,7 +14,7 @@ const ORDERING_FILE = 'priority.md'
14
14
  const BACKLOG_FILE = 'backlog.md'
15
15
 
16
16
  /**
17
- * The readiness headings `.claude/standards/tasks.md` fixes. The names are the
17
+ * The readiness headings `standards/tasks.md` fixes. The names are the
18
18
  * contract rather than a suggestion, so a board grouping under names of its own
19
19
  * reads as carrying no group at all and the run refuses instead of reporting a
20
20
  * clean board it never parsed.
@@ -42,11 +42,11 @@ An intake failing these is non-conforming even when it satisfies every shape rul
42
42
 
43
43
  ## Files
44
44
 
45
- | File | Holds | Required |
46
- | -------------------- | ------------------------------------------------------------ | -------- |
47
- | `00-overview.md` | Index: format block, cluster table, verdicts, open questions | Always |
48
- | `NN-<domain>.md` | One cluster of items, filed by the domain their fixes touch | Always |
49
- | `99-next-session.md` | What a compaction destroys that no cluster file carries | Optional |
45
+ | File | Holds | Required |
46
+ | -------------------- | ----------------------------------------------------------- | -------- |
47
+ | `00-overview.md` | Index: cluster table, verdicts, open questions | Always |
48
+ | `NN-<domain>.md` | One cluster of items, filed by the domain their fixes touch | Always |
49
+ | `99-next-session.md` | What a compaction destroys that no cluster file carries | Optional |
50
50
 
51
51
  `00` is the only reserved number. Everything else is read order, and the domain rides in the filename so a reader knows what `07-tooling.md` holds without opening it.
52
52
 
@@ -70,7 +70,6 @@ Date the folder once rather than every file. Twelve dated files leave eleven sta
70
70
 
71
71
  The index. It points at items and answers nothing itself.
72
72
 
73
- - The item format block, copied so a returning session picks the shape up from the folder
74
73
  - The answer contract stated out loud, since it inverts the plan file's
75
74
  - A cluster table of file, what it holds, item count, and open count
76
75
  - The verdict counts across the folder
@@ -168,10 +167,6 @@ date: <YYYY-MM-DD>
168
167
 
169
168
  <One line on what the dump covers and the commit it was measured against.>
170
169
 
171
- ## Item format
172
-
173
- <the item format block, copied so a returning session picks the shape up here>
174
-
175
170
  ## The answer contract
176
171
 
177
172
  `You:` ships empty and empty means unread. Accepting a verdict is typed as
@@ -9,7 +9,7 @@ Applies to each authored standard in the folder. Skip for `index.md`, which is g
9
9
 
10
10
  ## Overview
11
11
 
12
- A standard is a target-facing authoring convention for one document type, or for one attribute carried across every document. It installs into a project under `.claude/standards/` and is consumed by skills and developers alike. This file governs itself, so every rule below applies to it.
12
+ A standard is a target-facing authoring convention for one document type, or for one attribute carried across every document. Nothing installs it into a project, so a reader resolves it with `aitk standards <name>` or reads it at `standards/<name>.md`, and skills and developers consume it alike. This file governs itself, so every rule below applies to it.
13
13
 
14
14
  ## Scope
15
15
 
@@ -7,5 +7,8 @@ extends = ""
7
7
  runtime = ""
8
8
  scaffold = ""
9
9
 
10
+ # Checked against this repository's own `.gitignore` by
11
+ # scripts/core/check-ignore-parity.sh, which also holds the two `.claude/` paths
12
+ # this array deliberately omits and the reason each stays out.
10
13
  [gitignore]
11
- "# Claude" = [".claude/.records.git/", ".claude/.tmp/", ".claude/groundwork/", ".claude/intake/", ".claude/memory/", ".claude/plans/", ".claude/review/", ".claude/worktrees/", ".claude/tasks/", ".claude/teach/"]
14
+ "# Claude" = [".claude/.records.git/", ".claude/.tmp/", ".claude/groundwork/", ".claude/intake/", ".claude/memory/", ".claude/plans/", ".claude/proposals/", ".claude/review/", ".claude/worktrees/", ".claude/tasks/", ".claude/teach/"]
@@ -29,7 +29,7 @@ A project installed before the board became a folder still holds `.claude/TASKS.
29
29
 
30
30
  Convert by hand, once per project:
31
31
 
32
- 1. Split each `###` block into `.claude/tasks/vXX.Y-<slug>.md`, adding the `title` and `description` frontmatter that `.claude/standards/tasks.md` requires. Compare each new file against the block it came from rather than checking that every block landed somewhere.
32
+ 1. Split each `###` block into `.claude/tasks/vXX.Y-<slug>.md`, adding the `title` and `description` frontmatter the tasks standard requires, which `aitk standards tasks` prints. Compare each new file against the block it came from rather than checking that every block landed somewhere.
33
33
  2. Run `aitk indexes regen --no-stage --root . .claude/tasks/<any-task>.md` to build the catalog.
34
34
  3. Delete `.claude/TASKS.md`, and swap its `.gitignore` entry for `.claude/tasks/`.
35
35