@erclx/aitk 0.72.0 → 0.73.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.
@@ -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.72.0",
4
+ "version": "0.73.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -21,6 +21,8 @@ The last failure is writing without re-checking. A domain sync brought a stricte
21
21
  - Map the stated intent to one lifecycle phase, then either run the simplest command that satisfies it or hand off
22
22
  - Resolve every stack, rule, snippet, and standard name from a catalog at runtime
23
23
  - Run the CLI non-interactively and report the command run, what changed, and the full path of anything written
24
+ - Read the reverse walk beside the forward sections, so a folder the toolkit stopped shipping and a migration with a live case both reach the user
25
+ - Hand off a named migration to the skill the report names, the way an unmigrated domain reaches `migration-standards`
24
26
  - Offer every audit whose surface the target carries, and offer none whose surface it lacks
25
27
  - Re-check the target after any operation that wrote, and report what those writes changed against the state read before acting
26
28
 
@@ -29,6 +31,7 @@ The last failure is writing without re-checking. A domain sync brought a stricte
29
31
  - Edit a managed file by hand instead of running the CLI that owns it
30
32
  - Reimplement a flow another skill owns
31
33
  - Hardcode a catalog name
34
+ - Move, delete, or name a command against a folder in `unclaimed`. The toolkit stopped claiming the path and only the user knows whether the project owns what sits there
32
35
  - Run an audit the user did not pick, which turns a front door into a full sweep
33
36
  - Refuse to finish an operation over an audit finding, since every audit reports judgments beside facts and a router that stops on one is a router a target works around
34
37
  - Auto-trigger. It is a door the user opens, and a router that fires on its own routes requests nobody made.
@@ -38,6 +41,7 @@ The last failure is writing without re-checking. A domain sync brought a stricte
38
41
  - An intent matching a deep flow hands off rather than running a shallow version of it, since a partial scaffold is harder to recover from than none
39
42
  - An ignore-only fix on an unmeasured tooling report stops, names which cause applies, and names the command answering that cause. Unmeasured has several causes and they do not share a remedy, so one suggested command is wrong for at least one of them. No cause asks the user to supply a stack name, and none reads the zero counts under an unmeasured report as a clean target.
40
43
  - A re-check with no earlier report to compare against says the write ran without a baseline, rather than describing the target from scratch
44
+ - A report missing a field this skill reads names the CLI version as the cause and reports that section as unread. Neither an absent key nor a hand-rolled substitute for it may be reported as a finding, since the first reads as a clean target and the second reaches a verdict only the field's attribution can make
41
45
 
42
46
  ## Out of scope
43
47
 
@@ -22,23 +22,28 @@ Load a domain doc with `aitk docs <topic>` only when the intent touches that dom
22
22
 
23
23
  Run `aitk sync --check . --json` before routing. It reports what a target is behind on across every surface, so the intent comes from the project state rather than from the user having to know it already. Skip only when the user named a single operation to run.
24
24
 
25
- Read six fields off the report and carry each to `## Route`:
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
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
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
- - `historyUnavailable` on a domain or on `seeds`: attribution failed, so treat every difference as unverified and say so rather than reporting a file as untouched
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
32
+ - `historyUnavailable` on a domain, on `seeds`, or on `reverse`: nothing could be dated, so treat every difference as unverified and say so rather than reporting a file as untouched. It is set on `reverse` when the toolkit itself ships without history, which is the registry-install case, so the walk found nothing rather than finding a clean target
32
33
  - `tooling`: read `measured` first. Every count under it is zero when it is false, which is an absence of measurement rather than a measured zero. Past that, `chain` names the stacks the install resolved, nearest first, and `counts.gitignore` counts the managed ignore entries the target is missing.
33
34
 
34
35
  State what the report found in one line per finding before acting on any of it.
35
36
 
37
+ A `reverse` key the report does not carry at all is a CLI predating the field rather than a target with nothing unclaimed. Say the walk did not run and name the CLI version as the cause. Reading the absent key as an empty answer reports a clean target to a project nobody has looked at, and answering it from a filesystem walk of your own is worse, since that reaches a verdict the attribution the field carries is the only thing entitled to make.
38
+
36
39
  ## Route
37
40
 
38
41
  Map the stated intent, or what `## Diagnose` found, to one lifecycle phase, then act:
39
42
 
40
43
  - A domain in `unmigrated`: hand off to `migration-standards`
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
41
45
  - Anything in `superseded`: tell the user which files and what replaced them. Do not move or delete them
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
42
47
 
43
48
  - First-time scaffold of a fresh project: hand off to `setup-init`
44
49
  - Governance rules for the project stack: hand off to `setup-gov`
@@ -210,6 +210,12 @@ code change. Scoping to those roots is what keeps the walk useful: walking the
210
210
  whole tree reports every project folder as unclaimed, which is true and says
211
211
  nothing.
212
212
 
213
+ The managed gate above applies here too, and it is the one place it surprises.
214
+ A directory holding a dropped folder and nothing else reports an empty `reverse`
215
+ rather than the folder, because it carries none of the three markers. Read an
216
+ empty section on an unmanaged target as a walk that never ran rather than as a
217
+ clean result.
218
+
213
219
  Each entry carries `rel`, a file count, and an `attribution` of `dropped`,
214
220
  `project`, or `unattributed`. A dropped folder and one the project wrote are the
215
221
  same bytes at the same path, so the verdict is traced from history rather than
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "0.72.0",
4
+ "version": "0.73.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -99,6 +99,67 @@ stage_toolkit_markdown() {
99
99
  done < <(find "$src" -maxdepth 1 -type f -name "*.md" ! -name "index.md" | sort | head -n "$count")
100
100
  }
101
101
 
102
+ # The newest top-level path history records a deletion under that the toolkit no
103
+ # longer ships. Read from history rather than hardcoded, so an arm stages a root
104
+ # the walk will actually recognize instead of a name that has since come back.
105
+ # `prompts` is preferred because it is the case measured in a real target, and
106
+ # any other dropped root exercises the same walk.
107
+ pick_dropped_root() {
108
+ local preferred="prompts"
109
+ local first=""
110
+ local candidate
111
+
112
+ while IFS= read -r candidate; do
113
+ [ -e "$PROJECT_ROOT/$candidate" ] && continue
114
+ [ "$candidate" = "$preferred" ] && {
115
+ echo "$preferred"
116
+ return 0
117
+ }
118
+ [ -n "$first" ] || first="$candidate"
119
+ done < <(git -C "$PROJECT_ROOT" log --all --diff-filter=D --name-only --format= |
120
+ awk -F/ 'NF > 1 { print $1 }' | sort -u)
121
+
122
+ echo "$first"
123
+ }
124
+
125
+ # Restores one file's exact published bytes from the commit before it was
126
+ # deleted. Content is what the attribution matches on, so a file written by hand
127
+ # would report unattributed and an arm would assert the wrong verdict.
128
+ #
129
+ # Both reads take the whole listing through a process substitution rather than a
130
+ # pipeline ending in an early exit. `set -o pipefail` is on, and a `grep -m 1`
131
+ # that matches the first line closes the pipe while git is still writing, so the
132
+ # substitution returns git's SIGPIPE status and the arm fails on a listing it
133
+ # actually read.
134
+ restore_dropped_file() {
135
+ local root="$1"
136
+ local rel="" commit="" line
137
+
138
+ while IFS= read -r line; do
139
+ case "$line" in
140
+ "$root"/*)
141
+ rel="$line"
142
+ break
143
+ ;;
144
+ esac
145
+ done < <(git -C "$PROJECT_ROOT" log --all --diff-filter=D --name-only \
146
+ --format= -- "$root/")
147
+
148
+ [ -n "$rel" ] || return 1
149
+
150
+ while IFS= read -r line; do
151
+ commit="$line"
152
+ break
153
+ done < <(git -C "$PROJECT_ROOT" log --all --diff-filter=D --format=%H -- "$rel")
154
+
155
+ [ -n "$commit" ] || return 1
156
+
157
+ mkdir -p "$(dirname "$rel")"
158
+ git -C "$PROJECT_ROOT" show "$commit^:$rel" >"$rel" || return 1
159
+
160
+ echo "$rel"
161
+ }
162
+
102
163
  # Stages one step of a scenario arm into the sandbox working directory.
103
164
  # Scenarios call this once per step so their own git operations stay between
104
165
  # the steps, where they are visible.