@erclx/aitk 0.73.0 → 0.75.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/README.md CHANGED
@@ -62,7 +62,7 @@ Two limits worth knowing before you install. Claude Code is the only agent runti
62
62
  Each domain has a canonical source in this repo and a thin install or sync CLI on your side. The links run to internal narrative, written for someone maintaining the toolkit rather than installing it, so skip them on a first pass.
63
63
 
64
64
  - [Claude Code plugin](.claude/context/claude-plugin/index.md): skills for planning, review, docs sync, and the git ship chain
65
- - [Governance rules](.claude/context/governance.md): Cursor rules and stacks, installable per project
65
+ - [Governance rules](.claude/context/governance/index.md): Claude rules and stacks, installable per project
66
66
  - [Standards](.claude/context/standards/index.md): shared authoring conventions, synced to projects
67
67
  - [Snippets](.claude/context/snippets.md): reusable prompts for Claude and Gemini chat
68
68
  - [Tooling stacks](.claude/context/tooling.md): golden configs, seeds, and references per framework
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aitk",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "0.73.0",
4
+ "version": "0.75.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: migration-superseded
3
+ description: Why a retired file's split ships as a proposal read from the destination standard, and why detection stops rather than falling back to a listing
4
+ ---
5
+
6
+ # Migration superseded requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a target holding a retired `.claude/` file is told what replaced it and nothing else. The drift report names both halves and no command touches either, so the user hand-splits one file into a folder answering to a standard they have not read. The destination files land with frontmatter fields missing and filenames that sort out of order, which is the step the toolkit exists to remove.
11
+
12
+ A second failure comes from the ordering nobody gets right unaided. A retired file committed before its ignore entry was written is tracked and ignored at once, and removing the entry first leaves the file tracked with nothing naming it. No report catches that state, so the mistake is discovered by whoever next wonders why a gitignored path keeps appearing in diffs.
13
+
14
+ Two more follow from where a session looks when the report is thin. An uppercase stem under `.claude/` is not the test for a superseded file, so a session substituting a folder listing reaches `ARCHITECTURE.md`, `REQUIREMENTS.md`, `ROADMAP.md`, and `DESIGN.md`, each a single file the layout intends to stay one, and proposes shredding four documents. A session falling back to the plugin's own copy of the destination standard proposes a shape the project never adopted, against content only the user can place.
15
+
16
+ The last is the two-speed release skew arriving as a confident wrong answer. `superseded` reached a release in `0.46.0`, and an older CLI exits zero with a well-formed report that never mentions the field. A session reading that absent key as an empty array reports a clean layout to exactly the population holding the retired files.
17
+
18
+ ## Must
19
+
20
+ - Take detection from the report's `superseded` array alone, and stop when the key is absent rather than treating it as empty
21
+ - Resolve the governing standard by matching the replacement folder against the `appliesTo` the standards catalog declares, so a seed folder added later resolves without an edit to the body
22
+ - Read the destination shape from the project's own installed standard, and propose nothing for a folder whose standard is absent or unshipped
23
+ - Separate a folder no standard governs from one whose governing standard could not be read, since the catalog derives `appliesTo` from prose and answers an unparsed scope statement with an empty array
24
+ - Name the untrack command ahead of the ignore entry whenever a retired file is both tracked and ignored, and say when the check could not run
25
+ - Report what the destination standard leaves open, such as a phase label the retired file never recorded, rather than supplying a value for it
26
+ - Report the measured empty case as a pass, since the guard is what proves the section ran
27
+
28
+ ## Must not
29
+
30
+ - Write, move, or delete the retired file or its replacement, or create the destination folder
31
+ - Run `git rm --cached` or edit the file carrying the ignore entry
32
+ - Substitute a folder listing for the `superseded` array
33
+ - Read the plugin's own copy of the destination standard when the target lacks that file
34
+
35
+ ## Guards
36
+
37
+ - A directory with no `.claude/` stops, since nothing there was superseded and the report's sections would not be measured
38
+
39
+ ## Out of scope
40
+
41
+ - Relocating root `standards/` and `snippets/` into `.claude/`: `migration-standards`
42
+ - Classifying `CLAUDE.md` sections into the three-tier model: `migration-claude-md`
43
+ - Relocating `docs/` files by audience: `migration-context`
44
+ - Reconciling a seed file against its source section by section, which diffs two files rather than splitting one into a folder: `claude-seed-sync`
45
+ - Applying the split and running the untrack, which the user does after reviewing
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: migration-superseded
3
+ description: Proposes how to split a retired `.claude/` file into the folder that replaced it, reading the destination shape from the standard that folder answers to. Use when asked to "split TASKS.md", "my .claude/DIAGRAMS.md was replaced by a folder", "migrate the superseded file", or after a drift report names a superseded artifact. Do NOT write to the retired file or to its replacement, and do NOT run the untrack command. Proposal only.
4
+ ---
5
+
6
+ # Migration superseded
7
+
8
+ ## Guards
9
+
10
+ - If `.claude/` does not exist at `pwd`, stop: `❌ No .claude/ directory. Nothing here was superseded.`
11
+
12
+ The guard is also what makes the report's `superseded` section measured. Every section is gated on the target being toolkit-managed, and a `.claude/` directory satisfies that gate, so the empty case below is a measured empty rather than a section that never ran.
13
+
14
+ ## Step 1: read the report
15
+
16
+ Run `aitk sync --check . --json` from the project root. Its `superseded` array is the detection. Each entry carries `rel`, the retired file, and `replacedBy`, the folder that took its job.
17
+
18
+ An empty array is the pass: `✅ No superseded artifact. Every .claude/ file the seeds replaced is already a folder.`
19
+
20
+ ### When the report is unavailable
21
+
22
+ Stop on any of three conditions, naming which one fired:
23
+
24
+ - `aitk` is not on `PATH`: `❌ aitk is not on PATH. The superseded section is the only detection this skill has.`
25
+ - The command exits non-zero: `❌ aitk sync --check failed. Fix the report before proposing a split.`
26
+ - The report parses and carries no `superseded` key at all: `❌ This aitk predates the superseded field, which reached a release in 0.46.0. Upgrade, then re-run.`
27
+
28
+ Test for the key rather than for emptiness. A current CLI reporting `"superseded": []` has looked and found nothing, which is the pass above, and reading an absent key as an empty answer reports a clean layout to the projects this skill exists for.
29
+
30
+ No listing substitutes for the field. The pairing runs against the seed folder names the toolkit ships, and an uppercase stem is not the test, so a listing of `.claude/*.md` also reaches `ARCHITECTURE.md`, `REQUIREMENTS.md`, `ROADMAP.md`, and `DESIGN.md`. Each of those is a single file the layout intends to stay one. A fallback that proposed splitting them would shred four documents to avoid reporting a stale CLI, which is why this skill stops where its siblings degrade.
31
+
32
+ ## Step 2: resolve the destination standard
33
+
34
+ ### Match the folder against the catalog
35
+
36
+ Run `aitk standards list --json` and match each entry's `replacedBy` against the `appliesTo` array the catalog declares. Resolve from the catalog rather than from the folder stem, so a seed folder the toolkit adds later resolves without an edit here.
37
+
38
+ Compare on the folder rather than on the exact string. An `appliesTo` value matches when it equals `replacedBy` or begins with `replacedBy` followed by a slash. The report spells the folder `.claude/tasks` and the catalog spells it `.claude/tasks/`, and `memory` declares a filename pattern beneath its folder rather than the folder itself, so string equality matches nothing the catalog actually carries and sends every entry to a decline below.
39
+
40
+ ### Search both standards roots
41
+
42
+ A match resolves to that standard's `name`. Then look for that standard in the target, reading `.claude/standards/<name>.md` first and `standards/<name>.md` when the first is absent. The catalog says which standard governs the folder and only the target says whether the project holds it.
43
+
44
+ Search both roots in that order, which is the order the toolkit's own standards resolution already uses. A project that has not relocated its standards keeps them at the root, and testing the installed path alone hands exactly that target a refusal plus a command that installs a second copy beside the one it already holds.
45
+
46
+ A standard found at the root rather than under `.claude/` is read the same way and produces a full proposal. Say where it was read from and name `migration-standards` as the skill that relocates it, since the split is answerable either way and the layout is a note beside the proposal rather than a reason to refuse.
47
+
48
+ ### The states that end an entry
49
+
50
+ Four states end an entry with no proposal. Name whichever one fired rather than collapsing them, because two of them are unmeasured and two are answers:
51
+
52
+ - The catalog carries no `appliesTo` key: `⚠️ This aitk emits no appliesTo, so which standard governs <replacedBy> is unread.`
53
+ - No value covers `replacedBy` and some entry carries an empty `appliesTo`: an empty array is a scope statement that did not parse, so a no-match verdict is unread rather than negative. Name the standards that did not parse.
54
+ - No value covers `replacedBy` and every array is populated: `⚠️ The toolkit ships no standard for <replacedBy>. Nothing states the destination shape.` Installing cannot fix it, so name no command.
55
+ - The standard resolved and neither root holds it: name it, then name `aitk standards install --only <name> .` as the command that lands it.
56
+
57
+ Do not read the toolkit's own copy through `${CLAUDE_SKILL_DIR}` when the target lacks the file. Sibling skills cite that fallback for a standard they follow themselves, and this one is different: the standard here is the project's agreement about the shape of its own folder. A proposal drawn from the plugin copy hands back a shape the project never adopted, against content only the user can place.
58
+
59
+ ## Step 3: read the shape and map the file onto it
60
+
61
+ Read the resolved standard and take four things from it: the filename convention, the required frontmatter, the required sections, and anything the folder holds that is not an item, such as a generated `index.md` no proposal may claim.
62
+
63
+ Then read the retired file and split it at the boundary the standard implies, one destination file per item. Propose a filename per item from the convention, and name the frontmatter and sections each destination file owes.
64
+
65
+ Report what the standard leaves open rather than inventing a value for it. A convention carrying a field the retired file never recorded, such as a phase label or a verified SHA, is the user's to supply, and a proposal that fills one in reads as a decision the split already made.
66
+
67
+ ## Step 4: check the ignore ordering
68
+
69
+ Run both against each `rel`, in parallel:
70
+
71
+ - `git ls-files --error-unmatch <rel> 2>/dev/null`: whether the file is tracked
72
+ - `git check-ignore -v --no-index <rel> 2>/dev/null`: whether it is ignored, and which file and line names it
73
+
74
+ `--no-index` is what makes the second read answer the question. `git check-ignore` consults the index by default and reports a tracked path as not ignored, so the flagless form returns nothing in exactly the tracked-while-ignored state this step exists to find, and the run reads a clean result off the one case that is not clean.
75
+
76
+ A file that is both tracked and ignored carries an ignore entry added after the commit. Take it out of the index first, then remove the entry `check-ignore` named:
77
+
78
+ ```bash
79
+ git rm --cached <rel>
80
+ ```
81
+
82
+ Reversing the two leaves the file tracked with nothing naming it, and no report catches that state. Name both steps in that order and run neither. `git rm --cached` writes the index, which is a change to the project's own history even though no content moves.
83
+
84
+ When `pwd` is not a git work tree, say the tracked check did not run rather than reporting the ordering as clear.
85
+
86
+ ## Step 5: output
87
+
88
+ Print one block per superseded entry, then the shared reminder. Omit empty groups.
89
+
90
+ ```markdown
91
+ ## Split
92
+
93
+ `.claude/TASKS.md` → `.claude/tasks/`, shaped by `.claude/standards/tasks.md`
94
+
95
+ ## Proposed files
96
+
97
+ - `.claude/tasks/vXX.Y-<slug>.md` ← the "<heading>" section
98
+ - `.claude/tasks/vXX.Y-<slug>.md` ← the "<heading>" section
99
+
100
+ Each carries `title` and `description` frontmatter, an `## Outcomes` heading, and a `## Findings` heading.
101
+
102
+ ## You supply
103
+
104
+ - The phase label on each filename. The retired file records no version.
105
+
106
+ ## Untrack before editing the ignore entry
107
+
108
+ git rm --cached .claude/TASKS.md
109
+
110
+ Then remove the `.claude/TASKS.md` line from `.gitignore:12`.
111
+
112
+ ## Reminder
113
+
114
+ Nothing above was written or run. The retired file holds content the project authored, so the split is yours to apply and the destination folder shape is what this proposal supplies.
115
+ ```
116
+
117
+ Do not create the destination folder. Do not write, move, or delete the retired file. Do not run `git rm --cached`. The user applies the split after reviewing it.
@@ -25,7 +25,7 @@ Run `aitk sync --check . --json` before routing. It reports what a target is beh
25
25
  Read seven fields off the report and carry each to `## Route`:
26
26
 
27
27
  - `unmigrated`: a domain sitting at the root layout with nothing under `.claude/`. The most urgent finding, because that domain reports no drift of its own while being entirely behind
28
- - `superseded`: a file a newer seed folder replaced. Report it and stop. No command moves it, since the content is the project's own
28
+ - `superseded`: a file a newer seed folder replaced. No command moves it, since the content is the project's own, so this routes to the skill that proposes the split rather than ending at the report
29
29
  - `seeds`: entries are `matching`, `stale`, `drifted`, or `missing`. Anything but `matching` needs the seed handoff
30
30
  - `domains[].entries`: per-file `stale`, `customized`, `stranded`, and `orphaned` as before
31
31
  - `reverse`: what the target holds that no live catalog claims. `unclaimed` lists folders at roots the toolkit stopped shipping, each with an `attribution` of `dropped`, `unattributed`, or `project`. Act on the first two and leave `project` alone, which history proved the project owns. `migrations` names a proposal-only skill with a live case here and the `reason` it was measured from
@@ -42,7 +42,7 @@ Map the stated intent, or what `## Diagnose` found, to one lifecycle phase, then
42
42
 
43
43
  - A domain in `unmigrated`: hand off to `migration-standards`
44
44
  - An entry in `migrations`: hand off to the skill its `skill` field names, spelled as the report spells it. Both of them propose without writing, so the handoff is where this stops
45
- - Anything in `superseded`: tell the user which files and what replaced them. Do not move or delete them
45
+ - Anything in `superseded`: name which files and what replaced them, then hand off to `migration-superseded`. Both skills propose, so neither moves nor deletes the file
46
46
  - A folder in `unclaimed`: name it and the attribution it carries. No command moves it and the content may be the project's own, so the decision is the user's
47
47
 
48
48
  - First-time scaffold of a fresh project: hand off to `setup-init`
@@ -183,6 +183,13 @@ seed tree rather than from a fixed set of filenames, so a folder added later is
183
183
  covered without a code change. Only an exact stem matches, which leaves a
184
184
  suffixed variant such as `TASKS-ARCHIVE.md` unreported.
185
185
 
186
+ Route it to `migration-superseded`. That skill resolves the standard governing
187
+ `replacedBy` from the `appliesTo` the standards catalog declares, reads the
188
+ destination shape from the target's own copy under `.claude/standards/` or at the
189
+ root, and proposes the split without writing. Where a folder has no governing
190
+ standard, or neither root holds the one it has, the entry earns a named refusal
191
+ rather than a shape the project never adopted.
192
+
186
193
  `unmigrated` names a domain sitting at the root layout an older toolkit installed
187
194
  to, with nothing at the path the current one reads. It carries `rootPath`,
188
195
  `installPath`, and a file count. Without it a project holding `standards/` at its
@@ -55,14 +55,16 @@ Every weight and depth measure counts the text a reader is shown. A link reduces
55
55
 
56
56
  A code span is walked around rather than through, so a path quoting link or angle-bracket syntax keeps the width the page gives it. Masking inside one takes back the decision to count it, and the placeholders this toolkit writes are where that shows.
57
57
 
58
- The paragraph check 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: 88 paragraphs in this corpus sit inside four sentences and past the weight checkpoint, and the heaviest of those runs 1121 characters. The standard therefore states a weight beside the sentence cap, and the verb reads it as its own checkpoint.
58
+ The paragraph check 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: 36 paragraphs in this corpus sit inside four sentences and past the weight checkpoint, and the heaviest of those runs 1121 characters. The standard therefore states a weight beside the sentence cap, and the verb reads it as its own checkpoint.
59
59
 
60
- The paragraph weight sits at 600 and the bullet weight at 400. The two shapes measure one population, sharing a median near 170 characters with no gap behind either candidate, so the paragraph number was borrowed from the bullet rule when both checks shipped. They are separate checkpoints in the standard and separate patterns in the parser, and the sample below moved one and left the other untouched.
60
+ The paragraph weight sits at 700 and the bullet weight at 400. Both shipped at 400, because the paragraph number was borrowed from the bullet rule when the two checks landed together, and each has since been read against a sample of its own. They are separate checkpoints in the standard and separate patterns in the parser, so a read that moves one leaves the other where it is.
61
61
 
62
62
  #### The sample behind the paragraph number
63
63
 
64
64
  The checkpoint shipped at 400 as a borrowed number and was decided against a read of the prose it reports. Thirty-six findings were sampled, six from each of six weight bands, drawn at even spacing through each band ordered by path and line, and each was classed as prose a reader wants split or prose the checkpoint should not have reported.
65
65
 
66
+ Every band below was measured before the scan stopped counting link syntax as prose, so a paragraph sitting in one of these bands is heavier than a paragraph reported at the same number today. The re-sample in the section below re-reads the same range against the corrected measure and reaches the opposite verdict on it, which is the measure moving rather than the reader.
67
+
66
68
  | Band | Wants the split | Reads as written |
67
69
  | --------- | --------------- | ---------------- |
68
70
  | 400 - 425 | 1 | 5 |
@@ -78,6 +80,10 @@ Nothing inside the 500 to 600 band separated the two classes by length, which is
78
80
 
79
81
  The sample is thirty-six paragraphs against a reported population in the hundreds, and one reader classed all of them. Treat a band's rate as the order of magnitude it is rather than as a measured precision, and re-sample before moving the number again.
80
82
 
83
+ #### The re-sample that moved the number to 700
84
+
85
+ That re-sample ran once the scan stopped counting link syntax as prose. Findings at 604, 633, and 677 characters each read as an ordinary four-sentence paragraph on one topic, density arrived around 760 and was plain by 860, and the move cut the weight half of the report roughly in half while leaving the sentence cap untouched.
86
+
81
87
  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.
82
88
 
83
89
  ## Exit codes
@@ -193,5 +193,5 @@ Sync also refuses a target whose working tree is dirty, so commit or stash befor
193
193
 
194
194
  - [agents](agents/index.md): CLI flags, exit codes, and JSON output shapes
195
195
  - [AI workflow](ai-workflow.md): feature-development loop inside a toolkit-managed project
196
- - [tooling](../.claude/context/tooling.md), [governance](../.claude/context/governance.md), [claude plugin](../.claude/context/claude-plugin/index.md), [indexes](../.claude/context/indexes.md), [snippets](../.claude/context/snippets.md), [standards](../.claude/context/standards/index.md): per-domain mechanics
196
+ - [tooling](../.claude/context/tooling.md), [governance](../.claude/context/governance/index.md), [claude plugin](../.claude/context/claude-plugin/index.md), [indexes](../.claude/context/indexes.md), [snippets](../.claude/context/snippets.md), [standards](../.claude/context/standards/index.md): per-domain mechanics
197
197
  - [sandbox](../.claude/context/sandbox/index.md): scenario catalog for verifying domain flows
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "0.73.0",
4
+ "version": "0.75.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -45,7 +45,7 @@ export const DEFAULT_CHECKPOINTS = {
45
45
  run: 40,
46
46
  peerBullet: 130,
47
47
  bullet: 400,
48
- paragraph: 600,
48
+ paragraph: 700,
49
49
  sentences: 4,
50
50
  renderWidth: 80,
51
51
  } as const
@@ -34,7 +34,7 @@ Does not govern:
34
34
 
35
35
  - Use prose by default. Reserve bullets for discrete, unrelated items.
36
36
  - Keep paragraphs to four sentences or fewer. Split longer blocks at the next logical boundary.
37
- - Past roughly 600 characters in one paragraph, folding in the lines that wrap it, split at the next logical boundary as well. A paragraph written as two long sentences satisfies the sentence cap above and still asks the reader to hold too much at once. This number sits above the bullet checkpoint because a paragraph is read straight through and a bullet is scanned.
37
+ - Past roughly 700 characters in one paragraph, folding in the lines that wrap it, split at the next logical boundary as well. A paragraph written as two long sentences satisfies the sentence cap above and still asks the reader to hold too much at once. This number sits well above the bullet checkpoint because a paragraph is read straight through and a bullet is scanned.
38
38
  - Keep bullets tight. Past roughly 400 characters in one top-level bullet, counting the lines that continue it and excluding any bullet nested under it, the overflow belongs in prose. The number is a checkpoint rather than a cap, and a bullet reading well past it means the number is wrong rather than the rule.
39
39
  - Collapse a stack of bullets narrating one subsystem into a single `###` subsection carrying one narrative. Splitting a heavy bullet into three light ones satisfies the checkpoint above and leaves the reader no better off, and subdividing a block does not lighten the bullets inside it, so the two rules answer different defects.
40
40
  - Use dashes (`-`) not asterisks (`*`) for bulleted lists