@erclx/aitk 3.12.0 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/claude-groundwork/REQUIREMENT.md +1 -0
- package/claude/skills/claude-groundwork/SKILL.md +12 -12
- package/claude/skills/claude-intake/REQUIREMENT.md +1 -0
- package/claude/skills/claude-intake/SKILL.md +7 -7
- package/docs/agents/install-and-sync.md +55 -28
- package/docs/agents/intake.md +3 -1
- package/docs/ai-workflow.md +2 -2
- package/docs/target-projects.md +7 -5
- package/governance/rules/claude/556-groundwork.md +5 -1
- package/governance/rules/claude/557-intake.md +5 -1
- package/governance/rules/lang/120-bash.md +18 -0
- package/governance/rules/lib/305-e2e-reliability.md +34 -0
- package/governance/rules/lib/306-test-scope.md +25 -0
- package/governance/rules/lib/350-security-web.md +1 -0
- package/governance/rules/ui/450-link-behavior.md +19 -0
- package/governance/stacks/astro.toml +1 -1
- package/governance/stacks/base.toml +1 -1
- package/governance/stacks/react.toml +1 -1
- package/package.json +1 -1
- package/src/commands/gov.ts +10 -1
- package/src/commands/sync.ts +18 -5
- package/src/demo/compile.ts +1 -1
- package/src/gov/adapter.ts +29 -0
- package/src/gov/install.ts +22 -1
- package/src/gov/stacks.ts +33 -1
- package/src/intake/folder.ts +49 -9
- package/src/sync/check.ts +53 -15
- package/src/sync/engine.ts +25 -0
- package/src/sync/stamp.ts +73 -24
- package/src/tooling/stamp.ts +1 -1
- package/standards/groundwork.md +10 -3
- package/standards/index.md +2 -2
- package/standards/intake.md +10 -3
- package/tooling/web/manifest.toml +1 -0
- package/tooling/web/reference.md +1 -0
|
@@ -14,6 +14,7 @@ Two failure modes cost more than the rest. A track that fans out to subagents re
|
|
|
14
14
|
## Must
|
|
15
15
|
|
|
16
16
|
- Detect open, resume, and close from the folder itself, matching the topic against tracks already there before deriving a slug
|
|
17
|
+
- Name a newly opened folder with a two-digit ordinal ahead of the slug, taken from the highest one already present across groundwork and intake
|
|
17
18
|
- Apply the qualifying test in open mode alone, so a track already measured is not refused by the test that admitted it
|
|
18
19
|
- Measure the current state now rather than carrying a figure from a previous session
|
|
19
20
|
- Carry a lean and the finding that would overturn it on every open question, or admit that a measurement is missing
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: claude-groundwork
|
|
3
|
-
description: Opens and runs a numbered groundwork folder under `.claude/groundwork/<slug>/` for a topic that has to be measured before it can be planned. Detects open, resume, and close from the folder itself. Use when asked to "research X", "dig into X", "work out what we should do about X", "measure this before we commit", or "open a groundwork folder". Do NOT use to write a feature plan or to implement. That is `claude-feature`.
|
|
3
|
+
description: Opens and runs a numbered groundwork folder under `.claude/groundwork/<nn>-<slug>/` for a topic that has to be measured before it can be planned. Detects open, resume, and close from the folder itself. Use when asked to "research X", "dig into X", "work out what we should do about X", "measure this before we commit", or "open a groundwork folder". Do NOT use to write a feature plan or to implement. That is `claude-feature`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Claude groundwork
|
|
@@ -19,10 +19,10 @@ Read `${CLAUDE_SKILL_DIR}/../../standards/groundwork.md` before writing any file
|
|
|
19
19
|
|
|
20
20
|
## Write scope
|
|
21
21
|
|
|
22
|
-
- Write only inside `.claude/groundwork/<slug>/`. A feature plan, source changes, a standard, a rule, and a reference doc all live outside that folder, so this one rule forbids every one of them.
|
|
22
|
+
- Write only inside `.claude/groundwork/<nn>-<slug>/`. A feature plan, source changes, a standard, a rule, and a reference doc all live outside that folder, so this one rule forbids every one of them.
|
|
23
23
|
- One exception, at close only: write one task file recording what the track concluded.
|
|
24
24
|
- A second exception, for what a spike reads: write an input under `.claude/.tmp/groundwork-fixtures/<slug>/`. Keep it out of `.claude/groundwork/` so mode detection never matches a fixture as a track. A fixture a headless run is pointed at goes outside the repository instead, per the rule in `## Running a spike`.
|
|
25
|
-
- What a spike produces stays inside the track rather than joining the two exceptions above: write evidence a spike file cites under `.claude/groundwork/<slug>/evidence/`, which the first rule already permits. Mode detection matches entries at the top level of the tracks directory, so the sibling the fixtures rule guards against cannot be a folder nested inside a track, and the scratch path holds only what can be deleted without loss, which a recording a finding rests on is not.
|
|
25
|
+
- What a spike produces stays inside the track rather than joining the two exceptions above: write evidence a spike file cites under `.claude/groundwork/<nn>-<slug>/evidence/`, which the first rule already permits. Mode detection matches entries at the top level of the tracks directory, so the sibling the fixtures rule guards against cannot be a folder nested inside a track, and the scratch path holds only what can be deleted without loss, which a recording a finding rests on is not.
|
|
26
26
|
- Reading is not restricted. External research is in scope, so read documentation, comparable projects, and papers whenever a live question needs them.
|
|
27
27
|
- Every claim about a source outside the project carries a link to it. A source found and not read is listed as a lead and is never cited.
|
|
28
28
|
- Treat the folder as gitignored and unbacked. It dies with the machine, so `07-next-session.md` repeats what it needs instead of pointing at its siblings.
|
|
@@ -41,11 +41,11 @@ Record method, result, measured cost, and caveats in `08-spikes.md`. Put whateve
|
|
|
41
41
|
|
|
42
42
|
## Step 1: detect the mode
|
|
43
43
|
|
|
44
|
-
List `.claude/groundwork/` from the project root and match the topic against the
|
|
44
|
+
List `.claude/groundwork/` from the project root and match the topic against the slug half of each `<nn>-<slug>` folder already there before deriving anything. A resume pass rarely phrases the topic the way the folder was named, so a fresh slug derived from the wording would miss a live track and restart it.
|
|
45
45
|
|
|
46
46
|
Never match against `.claude/` itself. That directory holds every other workflow surface, so a topic matched there lands on a folder that was never a track.
|
|
47
47
|
|
|
48
|
-
With no match, derive a kebab-case slug named for the subject rather than the activity. Prefer `ts-migration` over `migration-research`. Then route on `.claude/groundwork/<slug>/`:
|
|
48
|
+
With no match, derive a kebab-case slug named for the subject rather than the activity. Prefer `ts-migration` over `migration-research`. Also list `.claude/intake/` and take `<nn>` as the highest ordinal present across both listings, incremented, per `${CLAUDE_SKILL_DIR}/../../standards/groundwork.md`. Then route on `.claude/groundwork/<nn>-<slug>/`:
|
|
49
49
|
|
|
50
50
|
- Folder absent: open
|
|
51
51
|
- Folder present without `06-decision.md`: resume
|
|
@@ -72,7 +72,7 @@ The standard sets the open question format and requires it inside a topic file a
|
|
|
72
72
|
|
|
73
73
|
## Open mode
|
|
74
74
|
|
|
75
|
-
1. Create `.claude/groundwork/<slug
|
|
75
|
+
1. Create `.claude/groundwork/<nn>-<slug>/`, with `<nn>` and `<slug>` as derived in Step 1.
|
|
76
76
|
2. Write `README.md` first. Writing it first forces the question of what the track is for.
|
|
77
77
|
3. Write `01-current-state.md` by measuring now. Never carry a figure from a previous session or from recall without re-measuring it. Measure only what an open question in the folder needs. A number with no question attached is how groundwork turns into the work.
|
|
78
78
|
4. Write `00-scope.md` when the track is large enough to run away. Skip it on a small track.
|
|
@@ -103,12 +103,12 @@ Emit the full relative path from the project root for every file written or upda
|
|
|
103
103
|
Open and resume:
|
|
104
104
|
|
|
105
105
|
```plaintext
|
|
106
|
-
📂 Opened .claude/groundwork/<slug>/
|
|
106
|
+
📂 Opened .claude/groundwork/<nn>-<slug>/
|
|
107
107
|
|
|
108
108
|
**Written:**
|
|
109
109
|
|
|
110
|
-
- `.claude/groundwork/<slug>/README.md`
|
|
111
|
-
- `.claude/groundwork/<slug>/01-current-state.md`
|
|
110
|
+
- `.claude/groundwork/<nn>-<slug>/README.md`
|
|
111
|
+
- `.claude/groundwork/<nn>-<slug>/01-current-state.md`
|
|
112
112
|
|
|
113
113
|
**Open questions:**
|
|
114
114
|
|
|
@@ -122,12 +122,12 @@ Use `📂 Resumed` in place of `📂 Opened` on a resume pass.
|
|
|
122
122
|
Close:
|
|
123
123
|
|
|
124
124
|
```plaintext
|
|
125
|
-
✅ Closed .claude/groundwork/<slug>/
|
|
125
|
+
✅ Closed .claude/groundwork/<nn>-<slug>/
|
|
126
126
|
|
|
127
127
|
**Written:**
|
|
128
128
|
|
|
129
|
-
- `.claude/groundwork/<slug>/06-decision.md`
|
|
130
|
-
- `.claude/groundwork/<slug>/07-next-session.md`
|
|
129
|
+
- `.claude/groundwork/<nn>-<slug>/06-decision.md`
|
|
130
|
+
- `.claude/groundwork/<nn>-<slug>/07-next-session.md`
|
|
131
131
|
|
|
132
132
|
**Uncited external claims:** <count, or none>
|
|
133
133
|
|
|
@@ -22,6 +22,7 @@ A session with no numbering convention re-decides the folder shape per dump, so
|
|
|
22
22
|
- Close every item with a verdict, and pair every open question with a suggested pick
|
|
23
23
|
- Treat an empty operator slot as unread rather than as agreement
|
|
24
24
|
- Reserve the index number and carry the domain in every other filename, leaving the rest of the numbering as read order
|
|
25
|
+
- Name a newly opened folder with a two-digit ordinal ahead of the slug, taken from the highest one already present across intake and groundwork
|
|
25
26
|
- Keep answers on items alone, with the index pointing at them
|
|
26
27
|
- Name the heading and the act beside every path the pass wrote
|
|
27
28
|
- Confine writes to the intake folder
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: claude-intake
|
|
3
|
-
description: Files a raw brain dump into a numbered intake folder under `.claude/intake/<slug>/`, one item per finding carrying a measured problem, a proposed fix, and a verdict. Use when asked to "file this dump", "triage my notes", "work through this list", "sort out this brain dump", or "run an intake pass". Do NOT use for one question that has to be measured before anyone can plan it. That is `claude-groundwork`.
|
|
3
|
+
description: Files a raw brain dump into a numbered intake folder under `.claude/intake/<nn>-<slug>/`, one item per finding carrying a measured problem, a proposed fix, and a verdict. Use when asked to "file this dump", "triage my notes", "work through this list", "sort out this brain dump", or "run an intake pass". Do NOT use for one question that has to be measured before anyone can plan it. That is `claude-groundwork`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Claude intake
|
|
@@ -29,7 +29,7 @@ Using the wrong one fails in two shapes. Intake on a question that needs measuri
|
|
|
29
29
|
|
|
30
30
|
## Write scope
|
|
31
31
|
|
|
32
|
-
- Write only inside `.claude/intake/<slug>/`. A plan file, a task file, a source change, a standard, and a rule all live outside that folder, so this one rule forbids every one of them.
|
|
32
|
+
- Write only inside `.claude/intake/<nn>-<slug>/`. A plan file, a task file, a source change, a standard, and a rule all live outside that folder, so this one rule forbids every one of them.
|
|
33
33
|
- There is no exception. Promoting an item onto the board runs through `claude-tasks` after the operator has answered, which is a separate invocation.
|
|
34
34
|
- Reading is unrestricted inside the project. Measuring is the work.
|
|
35
35
|
- Treat the folder as gitignored and unbacked. No check reaches its contents, so every rule stated here holds only while a session reads it.
|
|
@@ -38,11 +38,11 @@ Nothing outside this body carries the write-scope floor, and no path-scoped rule
|
|
|
38
38
|
|
|
39
39
|
## Step 1: detect open or resume
|
|
40
40
|
|
|
41
|
-
List `.claude/intake/` from the project root and match the topic against the
|
|
41
|
+
List `.claude/intake/` from the project root and match the topic against the slug half of each `<nn>-<slug>` folder already there before deriving a slug. A second pass over the same subject rarely phrases the topic the way the folder was named, so a fresh slug would open a duplicate beside a live folder.
|
|
42
42
|
|
|
43
43
|
Never match against `.claude/` itself. That directory holds every other workflow surface, so a topic matched there lands on a folder that was never an intake.
|
|
44
44
|
|
|
45
|
-
With no match, derive a kebab-case slug named for the subject rather than the activity. Prefer `toolkit-overview` over `august-triage`. An absent folder opens, and a present one resumes by appending items and revising verdicts the tree has moved under.
|
|
45
|
+
With no match, derive a kebab-case slug named for the subject rather than the activity. Prefer `toolkit-overview` over `august-triage`. Also list `.claude/groundwork/` and take `<nn>` as the highest ordinal present across both listings, incremented, per `${CLAUDE_SKILL_DIR}/../../standards/intake.md`. An absent folder opens, and a present one resumes by appending items and revising verdicts the tree has moved under.
|
|
46
46
|
|
|
47
47
|
## Step 2: orient
|
|
48
48
|
|
|
@@ -90,12 +90,12 @@ Emit the full relative path from the project root for every file written, and na
|
|
|
90
90
|
A file the pass only read gets no line, which is what keeps the block short.
|
|
91
91
|
|
|
92
92
|
```plaintext
|
|
93
|
-
📂 Opened .claude/intake/<slug>/
|
|
93
|
+
📂 Opened .claude/intake/<nn>-<slug>/
|
|
94
94
|
|
|
95
95
|
**Filed:**
|
|
96
96
|
|
|
97
|
-
- `.claude/intake/<slug>/05-coverage.md` gains items 6 to 8 under a new `## What the merge gate covers`
|
|
98
|
-
- `.claude/intake/<slug>/00-overview.md` cluster rows and verdict counts updated
|
|
97
|
+
- `.claude/intake/<nn>-<slug>/05-coverage.md` gains items 6 to 8 under a new `## What the merge gate covers`
|
|
98
|
+
- `.claude/intake/<nn>-<slug>/00-overview.md` cluster rows and verdict counts updated
|
|
99
99
|
|
|
100
100
|
**Routing:** <N> plan-ready, <N> groundwork candidates, <N> already settled
|
|
101
101
|
|
|
@@ -16,6 +16,15 @@ is left alone regardless of its name, since that subfolder is project-authored
|
|
|
16
16
|
by location. It also removes a stale `.claude/GOV.md`
|
|
17
17
|
from the retired build. Use `aitk gov install` to add rules.
|
|
18
18
|
|
|
19
|
+
When the target's install recorded a stack, `aitk gov sync` also reports a
|
|
20
|
+
rule that stack lists and `.claude/rules/` does not hold, as a `missing` entry
|
|
21
|
+
carrying no change. This is what makes a target whose recorded sync point
|
|
22
|
+
postdates a rule joining its stack still see that rule: the report reads the
|
|
23
|
+
target's current entitlement against its current tree rather than diffing
|
|
24
|
+
from an anchor a later sync could advance past the rule's own commit. A
|
|
25
|
+
target whose install predates the recorded chain falls back to the same
|
|
26
|
+
band-inference `newRules` uses in `aitk sync --check`.
|
|
27
|
+
|
|
19
28
|
There is no `aitk standards sync` and no `aitk standards install`. The corpus
|
|
20
29
|
installs into no project, so the domain has nothing in a target to reconcile.
|
|
21
30
|
`aitk standards <name>` prints one, resolving `standards/` at the working root
|
|
@@ -94,16 +103,23 @@ It also refuses a target whose working tree is dirty, so commit or stash first.
|
|
|
94
103
|
and is safe to run at any time. Each file is classified as `stale` when it still
|
|
95
104
|
matches what the toolkit installed, `customized` when the project edited it,
|
|
96
105
|
`stranded` when it sits at a path the toolkit no longer installs to, `orphaned`
|
|
97
|
-
when the project authored it, or `drifted` when no stamp covers it.
|
|
106
|
+
when the project authored it, or `drifted` when no stamp covers it. Governance
|
|
107
|
+
also reports `missing`, for a rule the target's recorded stack lists that its
|
|
108
|
+
tree does not hold at all.
|
|
98
109
|
|
|
99
110
|
Use `--json` for the machine-readable report and `--exit-code` to fail a CI job. Orphaned
|
|
100
|
-
files are excluded from that exit code
|
|
101
|
-
converges
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
111
|
+
and missing files are both excluded from that exit code: a project-authored
|
|
112
|
+
rule never converges, and a sync that added a missing one silently changes
|
|
113
|
+
what the project is governed by, which stays a separate command an operator
|
|
114
|
+
chooses to run. Attribution reads `.claude/aitk/config.json`, which every
|
|
115
|
+
install and sync writes.
|
|
116
|
+
|
|
117
|
+
A target installed before stamping shipped has no such file, and neither does
|
|
118
|
+
that fallback do anything to migrate it. A target stamped before the file
|
|
119
|
+
moved into `.claude/aitk/` still carries it at the retired
|
|
120
|
+
`.claude/aitk.json`, and `aitk sync --check` reads that path when the current
|
|
121
|
+
one is absent, reporting it rather than moving it. Only a target carrying
|
|
122
|
+
neither path falls back to the toolkit's own git history.
|
|
107
123
|
Installed content matching any
|
|
108
124
|
version that history ever published proves the file is untouched, so it reports
|
|
109
125
|
`stale` naming the commit it came from, and content matching no published
|
|
@@ -207,10 +223,10 @@ file and proposes relocating the lot.
|
|
|
207
223
|
|
|
208
224
|
#### Rules the target never received
|
|
209
225
|
|
|
210
|
-
`newRules` names a rule the
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
226
|
+
`newRules` names a rule the target could receive and its tree does not hold. A
|
|
227
|
+
sync refreshes the files a target already holds and adds none, so without this
|
|
228
|
+
section a project's rule set freezes at its install date while every file it
|
|
229
|
+
does hold reports as current. That is the report's most confident wrong
|
|
214
230
|
answer, since a clean result reads as a target holding everything the toolkit
|
|
215
231
|
publishes.
|
|
216
232
|
|
|
@@ -225,11 +241,22 @@ installs, nothing counts toward `--exit-code`, and a target can read the list an
|
|
|
225
241
|
act on none of it. The value stops at an operator reading it, which is the same
|
|
226
242
|
contract the skills list already sets.
|
|
227
243
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
244
|
+
Since `aitk gov install` records the stack it resolved, a target carrying that
|
|
245
|
+
record answers this by comparing its current entitlement against its current
|
|
246
|
+
tree, with no anchor and no git diff involved. That is what lets the section
|
|
247
|
+
name a rule that shipped before the target's last sync: the anchor a sync
|
|
248
|
+
advances plays no part in the read, where an anchor-bound diff can never see a
|
|
249
|
+
rule on the far side of a window a later sync moved past it. The per-file
|
|
250
|
+
`missing` state `aitk gov sync` reports comes from the same comparison, so a
|
|
251
|
+
rule the chain lists reaches both surfaces the same way.
|
|
252
|
+
|
|
253
|
+
A target stamped before governance recorded a chain falls back to the older
|
|
254
|
+
band-inference read below. The measurement there anchors on governance's own
|
|
255
|
+
stamp rather than on the oldest anchor across domains, since rules are
|
|
256
|
+
domain-scoped and a shared anchor would let a snippets sync move the revision
|
|
257
|
+
rules are measured from. A target carrying no chain and no governance anchor
|
|
258
|
+
reports nothing at all: it has no date to measure against, and diffing from the
|
|
259
|
+
start of history would read the whole catalog as new.
|
|
233
260
|
|
|
234
261
|
An anchor this toolkit cannot resolve reports nothing by the same route, and that
|
|
235
262
|
one is not visible. A stamp naming a revision the running clone has never seen,
|
|
@@ -238,22 +265,22 @@ fails the read and yields an empty list rather than a stated absence. It looks
|
|
|
238
265
|
identical to a target holding every rule the toolkit publishes. `newSkills`
|
|
239
266
|
behaves the same way, and neither carries the `historyUnavailable` flag the
|
|
240
267
|
per-domain scan uses to separate the two. Treat an empty section on a toolkit
|
|
241
|
-
that is not a full clone as unmeasured rather than clean.
|
|
268
|
+
that is not a full clone as unmeasured rather than clean. This gap does not
|
|
269
|
+
reach the chain-based read above, since it consults no anchor at all.
|
|
242
270
|
|
|
243
|
-
|
|
244
|
-
stack takes the `core` and `claude` folders whole and
|
|
245
|
-
it with individually named rules, so an unfiltered
|
|
246
|
-
consumer about rules it can never receive and train the
|
|
247
|
-
section.
|
|
271
|
+
In the fallback, entitlement is filtered, because a stack does not receive
|
|
272
|
+
every rule. The base stack takes the `core` and `claude` folders whole and
|
|
273
|
+
every other stack extends it with individually named rules, so an unfiltered
|
|
274
|
+
list would tell a base consumer about rules it can never receive and train the
|
|
275
|
+
reader to skip the section.
|
|
248
276
|
|
|
249
277
|
The filter accepts a band on either of two grounds. A folder the base stack takes
|
|
250
278
|
whole is entitled to every target, read from the stack file so a folder added to
|
|
251
279
|
base later needs no code change. Every other band is read off the folders the
|
|
252
|
-
target already carries,
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
under-reporting would cost the section its point.
|
|
280
|
+
target already carries, which is the fallback's only evidence of what a target
|
|
281
|
+
was entitled to before it recorded a chain. One band can be reached by more than
|
|
282
|
+
one stack, so the test over-reports inside a folder the target holds, which
|
|
283
|
+
costs a line where under-reporting would cost the section its point.
|
|
257
284
|
|
|
258
285
|
A rule the target already holds is dropped by name. That is what keeps a rule the
|
|
259
286
|
toolkit moved between band folders out of the list, since a rename reaches this
|
package/docs/agents/intake.md
CHANGED
|
@@ -9,6 +9,8 @@ description: Reading intake folder counts and items, the three read states an it
|
|
|
9
9
|
|
|
10
10
|
`aitk intake list` reports the intake folders under `.claude/intake/`, or the items one folder holds. It reads and never writes, because an answer belongs to the operator and a verb that filled one would decide what the folder exists to ask.
|
|
11
11
|
|
|
12
|
+
A folder carries a two-digit ordinal ahead of its slug, as in `21-toolkit-overview`, so a listing sorts by when each opened. A bare slug still resolves: passing `toolkit-overview` matches the one folder whose name is an ordinal ahead of it, and the folder's real name is what every command reports back.
|
|
13
|
+
|
|
12
14
|
```bash
|
|
13
15
|
aitk intake list
|
|
14
16
|
aitk intake list toolkit-overview --unread --json
|
|
@@ -58,7 +60,7 @@ One call writes one cluster. A call per selection is the alternative, and severa
|
|
|
58
60
|
|
|
59
61
|
An item already carrying an answer refuses rather than being overwritten, and one filled item refuses the whole batch, so a partly applied write never lands. Drop the named item and send the rest.
|
|
60
62
|
|
|
61
|
-
Exit codes: `0` every named item now carries its answer, `1` refused. The `reason` field carries `no-intake`, `no-folder`, `no-cluster`, `no-item`, `answered`, or `bad-input`.
|
|
63
|
+
Exit codes: `0` every named item now carries its answer, `1` refused. The `reason` field carries `no-intake`, `no-folder`, `ambiguous-slug`, `no-cluster`, `no-item`, `answered`, or `bad-input`. A bare slug matching more than one ordinal-prefixed folder refuses as `ambiguous-slug` rather than `no-folder`, naming every match in `detail`.
|
|
62
64
|
|
|
63
65
|
An exit code says nothing about a call made from a session, since a shell profile may wrap the binary in a function taking its status from a later command. Read the record's `reason` rather than the exit when a skill consumes this, which matters most here because the verb writes.
|
|
64
66
|
|
package/docs/ai-workflow.md
CHANGED
|
@@ -44,8 +44,8 @@ One session works for most features. Prefer splitting across two sessions only w
|
|
|
44
44
|
|
|
45
45
|
Work in Claude Code directly. It reads `CLAUDE.md` automatically and has full file access, no pasting needed.
|
|
46
46
|
|
|
47
|
-
- When the input is a pile of findings rather than one feature, invoke `aitk:claude-intake` first. It files the dump into `.claude/intake/<slug>/`, one item per finding carrying a problem measured against the tree, a proposed fix, and a verdict, then names which items are plan-ready, which need measuring, and which are already settled.
|
|
48
|
-
- When the current state is unmeasured and more than one approach is live, invoke `aitk:claude-groundwork` first. It opens a track folder under `.claude/groundwork/<slug>/` and ends in a decision, which may be to do nothing. Skip it when the approach is already settled.
|
|
47
|
+
- When the input is a pile of findings rather than one feature, invoke `aitk:claude-intake` first. It files the dump into `.claude/intake/<nn>-<slug>/`, one item per finding carrying a problem measured against the tree, a proposed fix, and a verdict, then names which items are plan-ready, which need measuring, and which are already settled.
|
|
48
|
+
- When the current state is unmeasured and more than one approach is live, invoke `aitk:claude-groundwork` first. It opens a track folder under `.claude/groundwork/<nn>-<slug>/` and ends in a decision, which may be to do nothing. Skip it when the approach is already settled.
|
|
49
49
|
- Invoke `aitk:claude-feature` to scan for code-level conflicts and ambiguities, confirm approach before proceeding
|
|
50
50
|
- Implement the feature, then Claude Code runs the commands defined in `CLAUDE.md`, fixes failures, and iterates until all pass
|
|
51
51
|
- For UI changes, invoke `aitk:claude-ui-test` to generate and run Playwright e2e tests
|
package/docs/target-projects.md
CHANGED
|
@@ -124,7 +124,7 @@ The report opens by naming the binary running it. The installed version reads ag
|
|
|
124
124
|
|
|
125
125
|
A `stale` file still matches what the toolkit installed, so the update is mechanical. A `customized` file carries local edits, so taking the upstream version is a decision and `aitk:claude-seed-sync` is the tool for it. A `stranded` file sits where an older toolkit installed it and the toolkit has since moved, which is what `aitk:migration-standards` handles.
|
|
126
126
|
|
|
127
|
-
That attribution comes from `.claude/aitk/config.json`, a stamp every install and sync writes. Snippets and governance record a hash per installed file.
|
|
127
|
+
That attribution comes from `.claude/aitk/config.json`, a stamp every install and sync writes. A target stamped before that path shipped is read from the retired `.claude/aitk.json` instead, reported rather than migrated. Snippets and governance record a hash per installed file. Governance also records the stack `aitk gov install` was given, and tooling records the stack chain it resolved instead of any file hash, since its install runs no per-file walk to attribute.
|
|
128
128
|
|
|
129
129
|
Each domain holds its own toolkit commit, so syncing governance today does not move the revision snippets measures against, and each domain reports the upstream commits touching its own source path. Running any sync stamps that domain, and the report names the ones still unstamped.
|
|
130
130
|
|
|
@@ -136,13 +136,15 @@ That third one matters most on an older project. Before it existed, a target hol
|
|
|
136
136
|
|
|
137
137
|
#### Rules you never received
|
|
138
138
|
|
|
139
|
-
A sync refreshes the files you already hold and adds none, so your rule set is frozen at the date you installed governance while every file in it reports as current. `newRules` names
|
|
139
|
+
A sync refreshes the files you already hold and adds none, so your rule set is frozen at the date you installed governance while every file in it reports as current. `newRules` names a rule you could receive that your tree does not hold, which is the one section that reads your absence rather than your contents. `aitk gov sync` reports the same rules per file, marked `missing`, so you see them either way you check.
|
|
140
140
|
|
|
141
141
|
Take a clean section as reporting rather than as delivering. Nothing here installs, and it counts toward nothing, so pick the rules up with `aitk gov install <stack>` or take one with `--add <rule>`. That separation is deliberate: adopting a rule changes what your project is governed by, which is a choice a sync has no business making for you.
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
Since `aitk gov install` records the stack you gave it, the list is read by comparing that stack's current rules against what you hold right now, with no date involved at all. That is what lets it name a rule the toolkit shipped before you last synced: nothing here depends on when you installed. A target stamped before this recording shipped falls back to the older read below.
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
The fallback filters to what your stack can receive, read off the rule folders you already carry plus the folders the base stack takes whole. A rule under `lang/` or `ui/` belongs to some stacks and not others, so an unfiltered list would name rules you can never install. A target with neither a recorded stack nor a governance anchor reports nothing at all, since there is no date to measure against either.
|
|
146
|
+
|
|
147
|
+
In the fallback, an empty section is not proof either way when the toolkit running the check is not a full git clone. The read needs history to reach your anchor, and an install from the registry ships none, so the section goes quiet rather than saying it could not measure. Run the check from a clone before reading a clean result as a complete one. The stack-based read above is untouched by this, since it consults no history.
|
|
146
148
|
|
|
147
149
|
This also closes the case where a rule arrives citing a sibling you do not have. A sync refreshing a rule can land a version pointing at a file it never installs, and the section names the missing sibling rather than the broken citation, which is enough to act on.
|
|
148
150
|
|
|
@@ -177,7 +179,7 @@ Standards take no part in that run. Nothing installed them, so there is no copy
|
|
|
177
179
|
### Targeted
|
|
178
180
|
|
|
179
181
|
- Claude seed docs such as `CLAUDE.md` and `.claude/REQUIREMENTS.md`: invoke `aitk:claude-seed-sync`. The skill splits each file into a preamble (between the H1 and the first H2) plus one part per `##` section, then diffs part by part and proposes per-part edits. User customizations are preserved.
|
|
180
|
-
- Governance rules already installed: `aitk gov sync <path>` diffs and applies, and never adds new rules
|
|
182
|
+
- Governance rules already installed: `aitk gov sync <path>` diffs and applies, and never adds new rules. A rule your recorded stack lists reports as `missing` instead.
|
|
181
183
|
- Tooling configs and seeds: `aitk tooling <stack> <path>` overwrites golden configs and merges seeds
|
|
182
184
|
- Reference docs for a stack: `aitk tooling ref <stack> <path>`
|
|
183
185
|
- Index regeneration after markdown edits: `aitk indexes regen`
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .claude/groundwork/ edits to the groundwork standard for numbering, frontmatter, and required file contents
|
|
2
|
+
description: Route .claude/groundwork/ edits to the groundwork standard for ordinal naming, numbering, frontmatter, and required file contents
|
|
3
3
|
paths:
|
|
4
4
|
- '.claude/groundwork/**'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Groundwork standards
|
|
8
8
|
|
|
9
|
+
## Folder name
|
|
10
|
+
|
|
11
|
+
- Name a new track `.claude/groundwork/<nn>-<slug>/`, a two-digit zero-padded ordinal followed by a kebab-case slug. Take the ordinal from the highest one already present across both `.claude/groundwork/` and `.claude/intake/`, incremented, or `01` when neither folder holds an entry. Never renumber an existing folder.
|
|
12
|
+
|
|
9
13
|
## Measuring and closing
|
|
10
14
|
|
|
11
15
|
- Never carry a figure from an earlier session without re-measuring it. Mark an inference as an inference.
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .claude/intake/ edits to the intake standard for the item format, the answer contract, and
|
|
2
|
+
description: Route .claude/intake/ edits to the intake standard for the item format, the answer contract, and ordinal naming
|
|
3
3
|
paths:
|
|
4
4
|
- '.claude/intake/**'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Intake standards
|
|
8
8
|
|
|
9
|
+
## Folder name
|
|
10
|
+
|
|
11
|
+
- Name a new dump `.claude/intake/<nn>-<slug>/`, a two-digit zero-padded ordinal followed by a kebab-case slug. Take the ordinal from the highest one already present across both `.claude/intake/` and `.claude/groundwork/`, incremented, or `01` when neither folder holds an entry. Never renumber an existing folder.
|
|
12
|
+
|
|
9
13
|
## The answer contract
|
|
10
14
|
|
|
11
15
|
- Never fill a `You:` slot and never infer a disposition from an empty one. Empty means unread, never agreement.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Route bash script authoring to the interactive or non-interactive skill, and name the lint gate
|
|
3
|
+
paths:
|
|
4
|
+
- '**/*.sh'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Bash standards
|
|
8
|
+
|
|
9
|
+
## Skill routing
|
|
10
|
+
|
|
11
|
+
- Use `bash-script` for an interactive or human-facing script: prompts, a visual timeline UI, framed terminal output.
|
|
12
|
+
- Use `cli-script` for a non-interactive script: automation, CI, cron, a pipeline helper, or anything run by an agent rather than watched by a person.
|
|
13
|
+
- Load the matched skill's own reference templates rather than hand-rolling interactivity or logging patterns outside them.
|
|
14
|
+
|
|
15
|
+
## Lint gate
|
|
16
|
+
|
|
17
|
+
- Format with `shfmt --write --indent 2` and lint with `shellcheck --severity=warning` before committing a script.
|
|
18
|
+
- Fix a shellcheck finding at the source. Suppress one with a directive comment only for a genuine false positive, and state why beside the suppression.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce settled waits and falsifiable guards in end-to-end tests
|
|
3
|
+
paths:
|
|
4
|
+
- 'e2e/*.ts'
|
|
5
|
+
- 'e2e/**/*.ts'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# End-to-end reliability standards
|
|
9
|
+
|
|
10
|
+
## Waiting
|
|
11
|
+
|
|
12
|
+
- Settle on the condition a step waits for. Use `expect.poll` or a web-first assertion ahead of any read.
|
|
13
|
+
- Reserve a fixed duration for an assertion that nothing happened across a window.
|
|
14
|
+
- Bound every settle with an explicit timeout.
|
|
15
|
+
- Do not raise a timeout to clear a failure that reproduces under load. Replace the wait with a settle.
|
|
16
|
+
- Do not read a value once after a pause. Poll it.
|
|
17
|
+
|
|
18
|
+
## Falsifiable guards
|
|
19
|
+
|
|
20
|
+
- Assert the set under test is non-empty before asserting a property over its members.
|
|
21
|
+
- Raise from an instrument that was refused rather than returning a value a passing assertion accepts.
|
|
22
|
+
- Run a new guard against the defect it was written for, and see it fail, before trusting it.
|
|
23
|
+
- Do not weaken an assertion to clear a failure. Narrow the wait instead.
|
|
24
|
+
|
|
25
|
+
## Reproducing a failure that only appears in CI
|
|
26
|
+
|
|
27
|
+
- Reproduce under `Emulation.setCPUThrottlingRate` rather than by rerunning the gate.
|
|
28
|
+
- Read the state the assertion does not: which markers were set, which listeners fired, how far a transition ran.
|
|
29
|
+
- Vary the condition under suspicion deliberately. Do not compare counts across runs that differed in something uncontrolled.
|
|
30
|
+
- Read the check conclusion as its own act. A green diff review reports nothing about the gate.
|
|
31
|
+
|
|
32
|
+
## Authority
|
|
33
|
+
|
|
34
|
+
- Follow `.claude/rules/lib/300-testing-ts.md` for framework choice, file placement, and test naming.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce which specs and which engines an end-to-end run covers at each point in the loop
|
|
3
|
+
paths:
|
|
4
|
+
- 'e2e/*.ts'
|
|
5
|
+
- 'e2e/**/*.ts'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Test scope standards
|
|
9
|
+
|
|
10
|
+
## Selecting a run
|
|
11
|
+
|
|
12
|
+
- Narrow an end-to-end run by spec path or by test name. Never narrow it by engine.
|
|
13
|
+
- Run one named test while iterating on a behavior: `bun run test:e2e -- -g '<name>'`.
|
|
14
|
+
- Run one surface while iterating on that surface: `bun run test:e2e -- e2e/<area>.spec.ts`.
|
|
15
|
+
- Run `bun run test:e2e:changed` to select specs from the import graph.
|
|
16
|
+
- Run the whole suite before pushing.
|
|
17
|
+
- Pass `--project` in a local run only to reproduce a failure that engine has already reported. The CI matrix passes it on every job, one engine per leg, which is the gate rather than a narrowed run.
|
|
18
|
+
- Do not add a script that pins a default run to one engine.
|
|
19
|
+
|
|
20
|
+
## Instruments
|
|
21
|
+
|
|
22
|
+
- Answer a question about the running page with a script against the dev server rather than with the suite.
|
|
23
|
+
- Do not enable `fullyParallel` in `playwright.config.ts`.
|
|
24
|
+
- Follow `.claude/rules/ui/440-surface-capture.md` for capture scope.
|
|
25
|
+
- Follow `.claude/rules/lib/305-e2e-reliability.md` for waits and guards.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce the same-tab default for an outbound link and for a mail link
|
|
3
|
+
paths:
|
|
4
|
+
- '**/*.tsx'
|
|
5
|
+
- '**/*.jsx'
|
|
6
|
+
- '**/*.astro'
|
|
7
|
+
- '**/*.html'
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Link behavior standards
|
|
11
|
+
|
|
12
|
+
## Default target
|
|
13
|
+
|
|
14
|
+
- Open a link in the same tab. Reserve `target="_blank"` for a destination that would discard in-progress work if it replaced the current page, such as a document a user is midway through elsewhere.
|
|
15
|
+
- Do not add `target="_blank"` as a default for every external link.
|
|
16
|
+
|
|
17
|
+
## mailto: links
|
|
18
|
+
|
|
19
|
+
- Open a `mailto:` link in the same tab. It hands off to the mail client rather than replacing page content, so a new tab leaves an empty tab behind.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
extends = "node"
|
|
2
|
-
rules = ["210-astro", "350-security-web", "400-ui", "410-a11y", "430-ux-completeness", "440-surface-capture"]
|
|
2
|
+
rules = ["210-astro", "350-security-web", "400-ui", "410-a11y", "430-ux-completeness", "440-surface-capture", "450-link-behavior"]
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
extends = "node"
|
|
2
|
-
rules = ["200-react", "230-nextjs", "250-tailwind", "300-testing-ts", "310-zod", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture"]
|
|
2
|
+
rules = ["200-react", "230-nextjs", "250-tailwind", "300-testing-ts", "305-e2e-reliability", "306-test-scope", "310-zod", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture", "450-link-behavior"]
|
package/package.json
CHANGED
package/src/commands/gov.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
type TestOrderReport,
|
|
36
36
|
} from '@/gov/test-order'
|
|
37
37
|
import { recordStamp, runDomainSync } from '@/sync/engine'
|
|
38
|
+
import { writeChainStamp } from '@/sync/stamp'
|
|
38
39
|
import { resolveTarget } from '@/target'
|
|
39
40
|
import {
|
|
40
41
|
intro,
|
|
@@ -757,7 +758,15 @@ async function runInstall(
|
|
|
757
758
|
|
|
758
759
|
logStep('Installing rules')
|
|
759
760
|
for (const rel of await installRules(found, resolved)) logAdd(rel)
|
|
760
|
-
|
|
761
|
+
|
|
762
|
+
const now = new Date()
|
|
763
|
+
await recordStamp(createGovAdapter(PROJECT_ROOT), resolved, now)
|
|
764
|
+
await writeChainStamp(
|
|
765
|
+
resolved,
|
|
766
|
+
{ domain: 'governance', toolkitRoot: PROJECT_ROOT },
|
|
767
|
+
[selected],
|
|
768
|
+
now,
|
|
769
|
+
)
|
|
761
770
|
|
|
762
771
|
const { GREEN, NC } = palette(process.stderr)
|
|
763
772
|
outro()
|
package/src/commands/sync.ts
CHANGED
|
@@ -99,6 +99,12 @@ function renderCheck(report: CheckReport): void {
|
|
|
99
99
|
|
|
100
100
|
renderSkew(report)
|
|
101
101
|
|
|
102
|
+
if (report.stampAtLegacyPath) {
|
|
103
|
+
logWarn(
|
|
104
|
+
'Stamp found at the retired .claude/aitk.json. Move it to .claude/aitk/config.json.',
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
102
108
|
if (!report.managed) {
|
|
103
109
|
logStep('Not a toolkit project')
|
|
104
110
|
logWarn('No .claude/ directory and no CLAUDE.md at the target.')
|
|
@@ -124,13 +130,20 @@ function renderCheck(report: CheckReport): void {
|
|
|
124
130
|
|
|
125
131
|
for (const entry of domain.entries) {
|
|
126
132
|
if (entry.state === 'matching' || entry.state === 'orphaned') continue
|
|
127
|
-
logWarn(`${entry.rel} (${entry.state})`)
|
|
133
|
+
logWarn(entry.notice ?? `${entry.rel} (${entry.state})`)
|
|
128
134
|
}
|
|
129
135
|
|
|
130
|
-
const { stale, customized, drifted, stranded, orphaned } =
|
|
136
|
+
const { stale, customized, drifted, stranded, orphaned, missing } =
|
|
137
|
+
domain.counts
|
|
131
138
|
if (stale + customized + drifted + stranded === 0) {
|
|
132
139
|
logInfo(orphaned === 0 ? 'up to date' : `up to date (${orphaned} local)`)
|
|
133
140
|
}
|
|
141
|
+
// A local file is not a deficiency and a missing rule is, so this never
|
|
142
|
+
// shares the up-to-date line's parenthetical. It prints on its own,
|
|
143
|
+
// regardless of whether anything above needs a sync.
|
|
144
|
+
if (missing > 0) {
|
|
145
|
+
logWarn(`${missing} listed by the stack, not installed`)
|
|
146
|
+
}
|
|
134
147
|
|
|
135
148
|
for (const commit of domain.upstream) {
|
|
136
149
|
logInfo(`${commit.sha} ${commit.subject}`)
|
|
@@ -168,10 +181,10 @@ function renderCheck(report: CheckReport): void {
|
|
|
168
181
|
if (report.newRules.length > 0) {
|
|
169
182
|
logStep('New rules, never installed')
|
|
170
183
|
for (const name of report.newRules) logWarn(name)
|
|
171
|
-
//
|
|
172
|
-
//
|
|
184
|
+
// `install` re-resolves the whole stack rather than adding one rule, so
|
|
185
|
+
// the remedy names both routes rather than assuming the reader wants all.
|
|
173
186
|
logInfo(
|
|
174
|
-
'Run `aitk gov install <stack
|
|
187
|
+
'Run `aitk gov install <stack>` to take the whole stack again, or `--add <rule>` to take one.',
|
|
175
188
|
)
|
|
176
189
|
}
|
|
177
190
|
|