@erclx/aitk 0.74.0 → 0.76.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/migration-superseded/REQUIREMENT.md +45 -0
- package/claude/skills/migration-superseded/SKILL.md +117 -0
- package/claude/skills/toolkit-operator/SKILL.md +2 -2
- package/docs/agents/install-and-sync.md +7 -0
- package/docs/agents/tasks.md +1 -1
- package/package.json +1 -1
- package/standards/tasks.md +6 -4
|
@@ -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.
|
|
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`:
|
|
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
|
package/docs/agents/tasks.md
CHANGED
|
@@ -90,7 +90,7 @@ aitk tasks validate
|
|
|
90
90
|
aitk tasks validate --json
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
Four checks run. Plan and Collisions
|
|
93
|
+
Four checks run. Plan and Collisions reach one half each of the `## Run now` test the board standard states, and the second reaches only the collision reason that half names, since the other blocker kinds put no fact on disk to check against. Mapping and Grouping test the folder contract and hold for every group:
|
|
94
94
|
|
|
95
95
|
| Check | What it reports |
|
|
96
96
|
| ---------- | --------------------------------------------------------------------------------- |
|
package/package.json
CHANGED
package/standards/tasks.md
CHANGED
|
@@ -48,13 +48,15 @@ Group tasks by readiness rather than by status, one row per task, under the colu
|
|
|
48
48
|
|
|
49
49
|
Readiness is three groups under fixed headings, `## Run now`, `## Up next`, and `## Needs a plan`, in that order. The names are the contract rather than a suggestion, because a board grouped by readiness under names of its own satisfies every other rule here and still reads as empty to anything counting rows under a heading. Add no fourth group. A task belongs to exactly one, and the tests are read in order.
|
|
50
50
|
|
|
51
|
-
- `## Run now`: a written plan covers every open outcome, and the
|
|
52
|
-
- `## Up next`: a written plan exists, and the task
|
|
51
|
+
- `## Run now`: a written plan covers every open outcome, and the task carries no reason it cannot start. A collision against the files something already running touches is one such reason, and the `Touches` column is what states it. A worker is handed a task from this group alone.
|
|
52
|
+
- `## Up next`: a written plan exists, and the task carries a stated reason it cannot start. The `Waiting on` column names that reason.
|
|
53
53
|
- `## Needs a plan`: everything else. The task has no plan, or the plan it carries no longer describes the work.
|
|
54
54
|
|
|
55
|
-
Each group fixes its own columns, which follow from the test above it rather than from preference. Neither half of the `## Run now` test is checkable without the file set and the plan sitting beside the task. The
|
|
55
|
+
Each group fixes its own columns, which follow from the test above it rather than from preference. Neither half of the `## Run now` test is checkable without the file set and the plan sitting beside the task. The `Waiting on` column under `## Up next` carries that reason in one of three forms. `## Needs a plan` states no file set at all, because a task with no plan has no bounded one to state. A group with no rows keeps its heading and its header row.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Under `## Up next` a collision names the file held by the task already running, a sibling task names that task, and an external condition names both the condition and what would satisfy it. Naming what would satisfy it is what separates a blocked row from one nobody has examined, so a cell stating a condition with no way out of it fails the test. The header text is the contract the way the group names are, because anything reading the cell resolves the column by header rather than by position.
|
|
58
|
+
|
|
59
|
+
`aitk tasks validate` reads those columns and reports where a row's claim and the tree disagree: a plan pointer resolving to no file, a row and a task file that do not map one to one, a task in two groups, and two `## Run now` rows touching a path in common. It does not read the `Waiting on` cell, so that test holds on reading alone. Run it when the readiness claim is made rather than on a schedule, since the board is gitignored per-machine scratch and no shared moment exists to hang it on. It reports and never writes, so a session fixes the row it names.
|
|
58
60
|
|
|
59
61
|
```markdown
|
|
60
62
|
---
|