@erclx/aitk 0.93.0 → 0.95.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.
Files changed (30) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/claude-docs/SKILL.md +1 -1
  3. package/claude/skills/claude-orchestrate/SKILL.md +1 -1
  4. package/claude/skills/claude-orchestrate/references/orchestrator-handoff.md +11 -39
  5. package/claude/skills/claude-orchestrate/references/orchestrator-resume.md +1 -1
  6. package/claude/skills/claude-ux-audit/REQUIREMENT.md +1 -0
  7. package/claude/skills/claude-ux-audit/SKILL.md +2 -1
  8. package/claude/skills/claude-ux-measure/REQUIREMENT.md +45 -0
  9. package/claude/skills/claude-ux-measure/SKILL.md +110 -0
  10. package/claude/skills/session-resume/REQUIREMENT.md +14 -3
  11. package/claude/skills/session-resume/SKILL.md +18 -5
  12. package/claude/skills/setup-gov/REQUIREMENT.md +3 -0
  13. package/claude/skills/setup-gov/SKILL.md +5 -0
  14. package/claude/skills/setup-indexes/REQUIREMENT.md +3 -0
  15. package/claude/skills/setup-indexes/SKILL.md +1 -0
  16. package/claude/skills/setup-init/REQUIREMENT.md +8 -0
  17. package/claude/skills/setup-init/SKILL.md +16 -4
  18. package/claude/skills/toolkit-operator/REQUIREMENT.md +1 -0
  19. package/claude/skills/toolkit-operator/SKILL.md +5 -2
  20. package/docs/agents/tasks.md +1 -1
  21. package/docs/ai-workflow.md +1 -0
  22. package/docs/target-projects.md +5 -1
  23. package/docs/visual-design-workflow.md +1 -0
  24. package/package.json +1 -1
  25. package/src/sandbox/expect.ts +29 -5
  26. package/src/tasks/archive.ts +13 -5
  27. package/src/tasks/validate.ts +2 -3
  28. package/standards/index.md +1 -0
  29. package/standards/session.md +106 -0
  30. package/standards/tasks.md +7 -2
@@ -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.93.0",
4
+ "version": "0.95.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -217,7 +217,7 @@ Write the retarget as a markdown link, `Plan: [feature-<slug>](../plans-archive/
217
217
 
218
218
  If `.claude/review/review-<slug>.md` exists, delete it. `claude-review` writes with this convention. Do not sweep any other `review-*.md` file.
219
219
 
220
- Do not sweep `ui-checklist-*.md` (pending human verification) or `ux-audit-*.md` (standalone deliverable).
220
+ Do not sweep `ui-checklist-*.md` (pending human verification), `ux-audit-*.md`, or `ux-measure-*.md` (standalone deliverables).
221
221
 
222
222
  Output one line per file swept:
223
223
 
@@ -38,7 +38,7 @@ The roadmap is optional and this skill does not require it. It carries why a seq
38
38
 
39
39
  A compaction is a moment this skill cannot detect, so the human asks for each side of it and this skill reads the matching runbook when they do.
40
40
 
41
- On a request to write the handoff or save the session, read `${CLAUDE_SKILL_DIR}/references/orchestrator-handoff.md` and follow it. It captures what the session learned, then writes `.claude/tasks/session.md` with the state of play, the decisions taken under delegated authority, the mistakes worth not repeating, and the standing cautions. That capture is the only one this session runs, since the refill sweep reports it as owed rather than paying it. Write nothing to the handoff that the board, a task file, or a groundwork folder already carries.
41
+ On a request to write the handoff or save the session, read `${CLAUDE_SKILL_DIR}/references/orchestrator-handoff.md` and follow it. It captures what the session learned, then writes a session map per `.claude/standards/session.md`, or `${CLAUDE_SKILL_DIR}/../../standards/session.md` when the project does not have it, carrying the state of play, the mistakes worth not repeating, and the standing cautions, with the decisions taken under delegated authority added as this role's extension. That capture is the only one this session runs, since the refill sweep reports it as owed rather than paying it. Write nothing to the handoff that the board, a task file, or a groundwork folder already carries.
42
42
 
43
43
  On a request to resume after a compaction, read `${CLAUDE_SKILL_DIR}/references/orchestrator-resume.md`, which reads that file back with the board and the groundwork behind the live work.
44
44
 
@@ -1,58 +1,30 @@
1
1
  ---
2
2
  title: Orchestrator handoff runbook
3
- description: Memory capture at the close of a session, what to write to .claude/tasks/session.md before a compaction, what to leave to the board, and the resume invocation the file carries out
3
+ description: The two sections an orchestrating session adds over the shared session map, how capture runs from a session that never commits, and the resume invocation the file carries out
4
4
  ---
5
5
 
6
- Capture what the session learned, then write the pre-compact handoff as orchestrator. Do both before a compaction, because a compaction keeps conclusions and drops the reasoning that produced them, and no other file in the repository carries that reasoning.
6
+ Write the pre-compaction handoff as orchestrator. Follow `.claude/standards/session.md`, or `${CLAUDE_SKILL_DIR}/../../standards/session.md` when the project does not have it, for the filename, the three core sections, the write procedure, and the citation rule. Everything below is the extension this role adds over that core, and none of it belongs to a session holding no delegated authority.
7
7
 
8
- 1. Invoke `aitk:claude-memory-capture` and tell it this session does not commit. Capture reads the session and this handoff summarizes it, so capturing first lets the handoff cite what was written instead of restating the same lesson in prose.
9
- 2. Run `aitk claude skills drift <commit this session started from>` and re-read any body it names before writing anything below. Nothing on the machine records that commit, so recover it from how long the session has been running with `git log -1 --format=%H --before='<duration> ago'`, rounding the duration up rather than down. A ref older than the oldest load over-reports and confirming a name costs one read of the body, so the generous end is the safe one and a guess at the exact commit is not worth making. A skill body enters a session once and re-invoking the skill replays the held copy rather than the file, so the drift is worst at exactly this moment and a name here is a body this session has been following out of date. Record what it named under `## Standing cautions`. The verb answers where the working directory carries `claude/skills/` with history behind it, which is the toolkit repository itself, and refuses by naming the absent tree anywhere else. A project consuming the plugin from a marketplace cache is that second case, so read the refusal as the boundary rather than as a fault.
10
- 3. Resolve the main worktree root with `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd`. Write `.claude/tasks/session.md` under it.
11
- 4. Write only what a compaction destroys and no other file already carries. The board holds the ordering and what each task waits on, a task file holds its own findings, and a groundwork folder holds its track.
12
- 5. Use this shape, resolving `${CLAUDE_SKILL_DIR}/references/orchestrator-resume.md` and `${CLAUDE_SKILL_DIR}/references/orchestrator-poll.md` to absolute paths as you write it and pasting each in place of `<RESUME_RUNBOOK>` and `<POLL_RUNBOOK>`:
8
+ 1. Run the capture step the core opens with, and tell `aitk:claude-memory-capture` that this session does not commit.
9
+ 2. Add `## Decisions taken under delegated authority` directly after `## State`, holding each decision and why it went that way, so nobody re-proposes it. It sits there rather than after the core because a decision is read against the state it was taken in.
10
+ 3. Close the file with the block below, resolving `${CLAUDE_SKILL_DIR}/references/orchestrator-resume.md` and `${CLAUDE_SKILL_DIR}/references/orchestrator-poll.md` to absolute paths as you write it and pasting each in place of `<RESUME_RUNBOOK>` and `<POLL_RUNBOOK>`:
13
11
 
14
12
  ```markdown
15
- ---
16
- title: Session map
17
- description: <what the board cannot show, and the date it was written>
18
- ---
19
-
20
- # Session map
21
-
22
- <one line marking the file throwaway and naming the board as the real source>
23
-
24
- ## State
25
-
26
- <what is clean, what is running, what is open, and any untracked file that needs committing>
27
-
28
- ## Decisions taken under delegated authority
29
-
30
- <each decision and why it went that way, so nobody re-proposes it>
31
-
32
- ## Mistakes worth not repeating
33
-
34
- <what went wrong and the rule it yields>
35
-
36
- ## Standing cautions
37
-
38
- <commands that lie, tools that measure the wrong tree, and anything unbacked>
39
-
40
13
  Resume by loading the orchestrator skill and asking it to resume after a compaction. This repository spells that `/aitk:claude-orchestrate` followed by the request. Following <RESUME_RUNBOOK> reaches the same place with no skill loaded at all.
41
14
 
42
- That resume reads the board and stops. It restarts nothing, so the review poll is a second thing owed here, and <POLL_RUNBOOK> holds the prompt and the condition. Do not reach for `session-resume`, which reads tracked work and knows nothing about this board or the workers on it.
15
+ That resume reads the board and stops. It restarts nothing, so the review poll is a second thing owed here, and <POLL_RUNBOOK> holds the prompt and the condition. Do not reach for `session-resume`, which reads tracked work and reports the newest map without restarting this loop.
43
16
  ```
44
17
 
45
- 6. Cite a commit, a task, or a file and line for every claim, so the next session can tell a read from a recall.
46
- 7. Overwrite the previous handoff rather than appending to it. A stale entry read as current is worse than no handoff.
47
-
48
- The substitution belongs in step 5 because step 7 ends the write. A reader who treats the list as finished there ships the literal placeholders, and the variable expands while this runbook renders rather than in the turn that reads the handoff back, so a path left unresolved reaches a session holding no skill as a string matching nothing. `orchestrator-poll.md` resolves its script at the same point and for the same reason.
18
+ The delegated-authority section is the orchestrator's alone because a worker holds no delegation to have exercised, and a section a session cannot fill teaches its reader to skip the file. The closing block is the orchestrator's for the same reason from the other direction: it restarts a review poll no other role runs.
49
19
 
50
- Add a section only for content that fits none of the four and would otherwise be lost. Do not restate the board, and do not summarize the work that shipped, because git already carries it. The closing block is the one exception, and the paragraph below states why.
20
+ The substitution belongs in step 3 because that step ends the write. A reader who treats the list as finished elsewhere ships the literal placeholders, and the variable expands while this runbook renders rather than in the turn that reads the handoff back, so a path left unresolved reaches a session holding no skill as a string matching nothing. `orchestrator-poll.md` resolves its script at the same point and for the same reason.
51
21
 
52
- That block sits in this runbook and again in the file this runbook writes, which is the fix rather than a copy for a later pass to collapse. A session has to already be holding this runbook to read it, and a compaction that took the skill body took the routing to it too, so the session that most needs the resume is the one that can no longer find it. `session.md` survives that, so it carries the invocation itself. Each of the two reaches a reader the other cannot.
22
+ That block sits in this runbook and again in the file this runbook writes, which is the fix rather than a copy for a later pass to collapse. A session has to already be holding this runbook to read it, and a compaction that took the skill body took the routing to it too, so the session that most needs the resume is the one that can no longer find it. The map survives that, so it carries the invocation itself. Each of the two reaches a reader the other cannot.
53
23
 
54
24
  The requirement is a resume request to the orchestrator skill with that skill loaded first. The command the block carries is this repository's spelling rather than the only one, since the skill ships to every target holding the plugin and each runs whatever client it runs. The poll restart is named beside it because the resume performs none.
55
25
 
56
26
  Step 1 exists because both other callers of capture are ship-chain skills and this session never ships. Without a call here, the session that receives every operator correction is the one session that records none. A compaction arriving with no warning takes the capture with it, and firing it once per batch of merges leaves the same window open across a long planning stretch, since a sweep runs only on a merge. The refill sweep reports the debt between handoffs so the operator knows one is owed.
57
27
 
58
28
  Capture is told this session does not commit, so it skips routing and writes memory files alone. A routed fact lands in a context entry, which is a tracked file, and the orchestrator's boundaries forbid writing one from this session. That split is correct rather than a limitation, since a domain fact belongs to the task that owns the surface and goes in that task's Findings, while what this session produces is feedback about how to work, which is the class the memory folder keeps.
29
+
30
+ The core's drift step answers where the working directory carries `claude/skills/` with history behind it, which is the toolkit repository itself, and refuses by naming the absent tree anywhere else. A project consuming the plugin from a marketplace cache is that second case, so read the refusal as the boundary rather than as a fault.
@@ -6,7 +6,7 @@ description: What to read back after a compaction, and how to treat a groundwork
6
6
  Resume as orchestrator after a compaction. Read the board and the groundwork behind the live work before answering anything, because a compaction summary keeps conclusions and drops the reasoning that produced them.
7
7
 
8
8
  1. Resolve the main worktree root with `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd`. Every path below resolves against it.
9
- 2. Read `.claude/tasks/priority.md` for the ordering and what each task waits on. Read `.claude/tasks/session.md` when it exists, and treat it as this session's scratch rather than a source.
9
+ 2. Read `.claude/tasks/priority.md` for the ordering and what each task waits on. Read the newest `.claude/tasks/session-*.md` when one exists, per `.claude/standards/session.md`, and treat it as the previous session's scratch rather than a source.
10
10
  3. Read the task file for every entry under `## Run now`, plus any entry a live pull request names.
11
11
  4. Read the groundwork folder each of those tasks cites. Read `README.md` for the file map, then `06-decision.md` when the track is closed, or `07-next-session.md` and the numbered topic files when it is open. A task's Findings section is a lossy summary of its track, so planning against the summary re-derives what the folder already settled.
12
12
  5. Run `gh pr list --state open` and `git log --oneline -5` to see what shipped since the folder was written.
@@ -37,3 +37,4 @@ The pass slides into fixing what it finds, and then the audit and the change lan
37
37
  - Feature planning, which `claude-feature` owns
38
38
  - Verifying one specific change, which `claude-ui-test` owns
39
39
  - Defining the intent it audits against, which `claude-design-extract` and the wireframes own
40
+ - Measuring what a running interface costs to paint, block, or shift, which `claude-ux-measure` owns. Contrast stays here rather than going with it, being computable from two color values this skill already reads off the token table.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: claude-ux-audit
3
- description: Audits the current UI for incomplete, inconsistent, or confusing patterns. Reads DESIGN.md and .claude/wireframes/ for intent, scans UI files, and outputs observations grouped by surface. Use when asked "audit the UX", "audit the UI", "UX audit", or "find UI roughness". Do NOT use for new feature planning or code changes.
3
+ description: Audits the current UI for incomplete, inconsistent, or confusing patterns. Reads DESIGN.md and .claude/wireframes/ for intent, scans UI files, and outputs observations grouped by surface. Use when asked "audit the UX", "audit the UI", "UX audit", or "find UI roughness". Do NOT use for new feature planning or code changes, and do NOT use to measure what a running interface costs to paint, which is `claude-ux-measure`.
4
4
  ---
5
5
 
6
6
  # Claude UX audit
@@ -8,6 +8,7 @@ description: Audits the current UI for incomplete, inconsistent, or confusing pa
8
8
  ## Guards
9
9
 
10
10
  - If no UI files exist in the project (no JSX, TSX, Vue, Svelte, or HTML under `src/`), stop: `❌ No UI surfaces found to audit.`
11
+ - If the request asks what the interface costs to paint, block, or shift at runtime, run nothing and name `claude-ux-measure`. This skill reads source and reaches no number a browser produces. Contrast is the exception and stays here, since it is computable from the two color values already in the token table.
11
12
 
12
13
  ## Step 1: read context
13
14
 
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: claude-ux-measure
3
+ description: Why a rendering cost question is answered with a number against a published threshold, and why the runner is detected rather than prescribed
4
+ ---
5
+
6
+ # Claude UX measure requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a session asked what a page costs answers from source. It reads component code, names a suspicious loop, and reports a judgment, so nobody learns the number and the next session repeats the guess. Nothing in the corpus starts a browser, so the question has no surface at all and lands on whichever skill matched the word "UI".
11
+
12
+ A session that does start one picks a runner on the spot. The reading then comes from a browser nobody chose, cannot be compared against the next run, and disappears with the chat. A number reported with no boundary beside it is the same dead end as the judgment it replaced, because a reader holding `1.9s` and no threshold concludes nothing from it.
13
+
14
+ ## Must
15
+
16
+ - Detect the harness the project already carries before naming any runner
17
+ - Report a number per metric beside the threshold it is measured against and the published source of that threshold
18
+ - Cover paint, processor, and layout cost, and name what the run left unmeasured
19
+ - Report what the measurement needs and stop when no harness is detected, since that is a project without a runner rather than a failure here
20
+ - Take several readings and report the median, since one load carries startup noise wider than the gap between two thresholds
21
+ - Write the reading to the branch-derived path at the main worktree root, overwriting
22
+ - Reach the URL before measuring against it, starting the project's own command only when nothing already answers there
23
+ - Leave the interface stopped and the probe deleted
24
+
25
+ ## Must not
26
+
27
+ - Install a runner, add a dependency, or write a config to make detection succeed
28
+ - Invent a threshold, or move a published one to fit a reading
29
+ - Report an observation about the source in place of a measurement
30
+ - Suggest a fix for what it measured, which is a change with its own review
31
+ - Measure anything past the three metrics, since every adjacent ask doubles the run
32
+
33
+ ## Guards
34
+
35
+ - No command that serves an interface and no URL from the user: stop, since there is nothing to reach. Test for a servable interface rather than for source under a named folder, which refuses a project laying its tree out differently while its server sits ready
36
+ - No harness detected: name the runners this can drive and stop
37
+ - No URL derivable from the project's own commands: ask rather than guessing a port
38
+
39
+ ## Out of scope
40
+
41
+ - Judging the interface against stated intent, which `claude-ux-audit` owns
42
+ - Contrast, which is computable from two color values in the token table `claude-ux-audit` already reads. A contrast failure from a color computed at runtime stays invisible to that reader, and the cost is accepted rather than overlooked.
43
+ - Network waterfall, bundle size, and accessibility, which no version of this measures
44
+ - Writing behavioral tests against the interface, which `claude-ui-test` owns
45
+ - Fixing what the reading found
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: claude-ux-measure
3
+ description: Measures paint, processor, and layout cost against a running interface and reports numbers against published thresholds. Detects the project's existing browser harness rather than requiring one. Use when asked "how fast is this page", "measure the UI", "what does this cost to render", "check Core Web Vitals", or "profile the interface". Do NOT use to judge UI quality by reading source, which is `claude-ux-audit`.
4
+ ---
5
+
6
+ # Claude UX measure
7
+
8
+ Report numbers. A sentence about what the source looks like is what this exists to replace, so every finding is a reading beside the threshold it is measured against.
9
+
10
+ ## Guards
11
+
12
+ - If the project names no command that serves an interface and the user names no URL, stop: `❌ Nothing to serve. Name a running URL or a command that starts one.` Test for a servable interface rather than for source under a particular folder, since this measures what a browser receives and never reads the tree that produced it.
13
+ - If the request is about intent, consistency, missing states, or contrast, run nothing and name `claude-ux-audit`. That skill reads source and this one runs the interface.
14
+
15
+ ## Step 1: reach the interface
16
+
17
+ Read these in parallel from the project root, skipping any that do not exist:
18
+
19
+ - `package.json`: the `scripts` block naming a dev, preview, or start command
20
+ - `.claude/context/development/`: the documented run commands and the port each serves
21
+ - `CLAUDE.md`: project type and conventions
22
+
23
+ Prefer a production-shaped build (`preview`, `start`) over the dev server. A dev server ships unminified modules and reports a cost no user pays.
24
+
25
+ Ask for the URL when nothing names one. Do not guess a port.
26
+
27
+ Then reach it. Request the URL first and measure whatever already answers, since a server the user started is the one they mean. Build and start the chosen command only when nothing answers, wait for it to accept a request before going on, and stop it once Step 4 has the readings. A run that measures before the server is listening reports a connection failure as a cost.
28
+
29
+ ## Step 2: detect the harness
30
+
31
+ Test each in order and take the first that resolves. Name the one found before running it.
32
+
33
+ 1. A connected browser MCP server exposing navigation and a performance trace
34
+ 2. `@playwright/test` or `playwright` in `package.json`, or a `playwright.config.*` at the project root
35
+ 3. `lighthouse` in `package.json`, or a script whose command names it
36
+
37
+ Never install a runner, add a dependency, or write a config to make one of these resolve. The project chooses its harness and this skill reads that choice.
38
+
39
+ When none resolves, report what the measurement needs and stop:
40
+
41
+ ```plaintext
42
+ ❌ No browser harness detected. This measurement drives one of:
43
+ - a browser MCP server with a performance trace
44
+ - Playwright (@playwright/test)
45
+ - Lighthouse (lighthouse)
46
+ Install one, or name a running URL and which runner to drive it with.
47
+ ```
48
+
49
+ ## Step 3: measure
50
+
51
+ Take three readings against the same URL on a cold profile and report the median. One load carries startup noise wider than the gap between two thresholds, so a single number cannot be placed against them.
52
+
53
+ Measure these three and nothing else:
54
+
55
+ - **Paint**: Largest Contentful Paint, the moment the largest element in the viewport finishes rendering
56
+ - **Processor**: Total Blocking Time, main-thread time past 50ms per long task between first paint and interactive
57
+ - **Layout**: Cumulative Layout Shift, the summed score of unexpected shifts over the page lifetime
58
+
59
+ Through Playwright or an MCP server, read them from a `PerformanceObserver` registered before navigation against the `largest-contentful-paint`, `longtask`, and `layout-shift` entry types. Lighthouse reports all three under its JSON audits, so parse rather than re-derive them.
60
+
61
+ Write any probe the harness needs into the project's own test folder, run it, then delete it. Leave no file behind.
62
+
63
+ ### Thresholds
64
+
65
+ | Metric | Good | Needs work | Poor |
66
+ | ------ | --------- | ---------- | --------- |
67
+ | LCP | `≤ 2.5s` | `≤ 4.0s` | `> 4.0s` |
68
+ | TBT | `≤ 200ms` | `≤ 600ms` | `> 600ms` |
69
+ | CLS | `≤ 0.1` | `≤ 0.25` | `> 0.25` |
70
+
71
+ These are Google's published Core Web Vitals boundaries, with the Lighthouse lab boundary for Total Blocking Time. Cite that source in the report. Never move a number to fit a reading, and never invent a fourth metric's boundary.
72
+
73
+ ## Step 4: report and persist
74
+
75
+ ### Report format
76
+
77
+ ```markdown
78
+ 3 metrics measured against <url> over 3 runs. Harness: <name>.
79
+
80
+ | Metric | Median | Threshold | Verdict |
81
+ | ------ | ------ | --------- | ---------------------------- |
82
+ | LCP | <n> | `≤ 2.5s` | <good \| needs work \| poor> |
83
+ | TBT | <n> | `≤ 200ms` | <verdict> |
84
+ | CLS | <n> | `≤ 0.1` | <verdict> |
85
+
86
+ Thresholds: Google Core Web Vitals, Lighthouse lab boundary for TBT.
87
+
88
+ Not measured: network waterfall, bundle size, accessibility, contrast.
89
+ ```
90
+
91
+ Report the reading and stop there. A remedy for a poor verdict is a change with its own review, so name no fix and edit no source.
92
+
93
+ ### Persist
94
+
95
+ Derive `<slug>` per `.claude/standards/slug.md`, or `${CLAUDE_SKILL_DIR}/../../standards/slug.md` when the project does not have it. Fall back to `latest` on an empty result.
96
+
97
+ Write the full reading directly to `.claude/review/ux-measure-<slug>.md` at the main worktree root, not the current worktree. See Worktrees in `CLAUDE.md`. Create the directory if it does not exist. Always overwrite.
98
+
99
+ From a linked worktree the file-editing tools refuse that path, so the reading goes out through `Bash`. Send the `mkdir -p` and the heredoc as two plain commands rather than joining them with `&&`, which is refused as compound.
100
+
101
+ The `.claude/review/` directory is gitignored. Do not stage or commit the file.
102
+
103
+ ### Chat output
104
+
105
+ Output the summary line and the file path. Do not repeat the table in chat.
106
+
107
+ ```plaintext
108
+ 3 metrics measured against <url>. <n> past threshold.
109
+ 📝 Wrote .claude/review/ux-measure-<slug>.md
110
+ ```
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: session-resume
3
- description: Why resuming reads an index before task files, resolves scratch at the main root, and never mutates what it reports
3
+ description: Why resuming reads the handoff before the board, reads an index before task files, resolves scratch at the main root, and never mutates what it reports
4
4
  ---
5
5
 
6
6
  # Session resume requirement
@@ -9,14 +9,20 @@ description: Why resuming reads an index before task files, resolves scratch at
9
9
 
10
10
  Without this skill, a session picking up old work reconstructs state from the git log, which records what shipped and not what is queued, so finished work reads as pending and the actual next item goes unmentioned. The alternative failure is worse for a different reason. A session that reads every task file and every plan to be thorough spends the context the work needs before the work starts, and the summary arrives in a session with no room left to act on it.
11
11
 
12
+ A compaction keeps conclusions and drops the reasoning that produced them, and the previous session can write that reasoning to a handoff before it goes. A resume that reads the board, the plans, and the memory index alone walks past that file, so the one artifact written to survive the compaction reaches nobody and the next session re-derives what it holds.
13
+
14
+ The task index is a folder catalog rather than a task filter, so it carries a row for the ordering file and one for every handoff on the board. A session reporting each row as a backlog item queues work nobody filed, and the count grows with every session that ever wrote a handoff.
15
+
12
16
  From a linked worktree the shared scratch folders resolve against the worktree rather than the main root, where they are empty. That reports no tracked work on a repository carrying a full backlog, and the report is indistinguishable from the true empty case.
13
17
 
14
18
  A resume is a read, and a session that treats it as a cleanup pass offers to archive finished entries or refresh a memory it decided was stale. Both change tracked state on the strength of a summary the user has not confirmed yet.
15
19
 
16
20
  ## Must
17
21
 
18
- - Resolve the plans, memory, and tasks folders at the main worktree root
22
+ - Read the newest handoff before the board, and report what it carries attributed to its writer
23
+ - Resolve the handoff, plans, memory, and tasks folders at the main worktree root
19
24
  - Read the task index before any individual task file, and open only the task files the summary needs
25
+ - Drop the board siblings from the index before reporting it as the backlog, since the catalog filters nothing and carries a row per handoff
20
26
  - Preserve the index's order in the report, since the order is the priority
21
27
  - Surface only the memory entries that inform the top item
22
28
  - Close with one recommendation naming the first item and whether a plan backs it
@@ -24,15 +30,20 @@ A resume is a read, and a session that treats it as a cleanup pass offers to arc
24
30
  ## Must not
25
31
 
26
32
  - Read the whole tasks folder to build a summary
33
+ - Report the absence of a handoff, which is the common case and would train a reader to skip the line on the run where one exists
34
+ - Restate a handoff's counts, sizes, or costs as current. Each was true when written.
27
35
  - Offer to remove, archive, or reorder an entry. Resume reports and does not mutate.
28
36
  - Update memory, which changes when a recorded fact becomes wrong rather than on a resume
29
37
 
30
38
  ## Guards
31
39
 
32
- - All three surfaces absent or empty reports no tracked work and stops, rather than inventing a next step from the repository
40
+ - All four surfaces absent or empty reports no tracked work and stops, rather than inventing a next step from the repository
41
+ - A handoff found beside an empty board recommends what the handoff leaves open, since the guard passes on the handoff alone and the recommendation has no backlog item to name
33
42
 
34
43
  ## Out of scope
35
44
 
45
+ - Writing a handoff, which happens at the close of a session rather than at its start. This skill names the standard that governs one and follows it no further.
46
+ - The sections a role adds over the core handoff, which belong to that role's own surface
36
47
  - Archiving a shipped task out of the folder: `claude-tasks`
37
48
  - Archiving a plan and marking an outcome, which `claude-docs` does when the work ships
38
49
  - Implementing the item it recommends, which is the next request rather than part of this one
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: session-resume
3
- description: Resumes a previous session by reading tracked work and relevant context. Use when starting a new session, or when asked to "pick up where we left off", "what was I working on", or "resume".
3
+ description: Resumes a previous session by reading the handoff it left behind, tracked work, and relevant context. Use when starting a new session, or when asked to "pick up where we left off", "what was I working on", or "resume".
4
4
  ---
5
5
 
6
6
  # Session resume
@@ -11,19 +11,24 @@ Resolve `.claude/plans/`, `.claude/memory/`, and `.claude/tasks/` at the main wo
11
11
 
12
12
  Read these in parallel, skipping any that do not exist:
13
13
 
14
- - `.claude/tasks/index.md`: the backlog, one line per task. Read this before any individual task file.
14
+ - the newest `.claude/tasks/session-*.md`: the handoff a previous session wrote before a compaction, per `.claude/standards/session.md`, or `${CLAUDE_SKILL_DIR}/../../standards/session.md` when the project does not have it. It leads the report rather than the reads.
15
+ - `.claude/tasks/index.md`: the folder catalog. Read this before any individual task file, and take the backlog from it by dropping the `index`, `priority`, and `session-` rows, which are siblings rather than tasks.
15
16
  - `.claude/plans/*.md`: execution detail for in-progress tasks
16
17
  - `.claude/memory/index.md` and any memory files relevant to the top backlog item
17
18
 
18
19
  Then read only the task files the summary needs, typically the top one. Do not read the whole folder.
19
20
 
20
- If all three surfaces are absent or empty, stop: `✅ No tracked work found. Start a new task.`
21
+ Most projects carry no handoff. Say nothing about its absence, since a line reporting it every run trains a reader to skip the line on the run where a handoff exists.
22
+
23
+ If all four surfaces are absent or empty, stop: `✅ No tracked work found. Start a new task.`
21
24
 
22
25
  ## Step 2: summarize
23
26
 
24
- Output three sections:
27
+ Output these sections, omitting the first when no handoff was found:
28
+
29
+ **Carried over:** the reasoning the handoff holds, as its writer stated it. Attribute it to the map rather than restating it as fact, and re-measure any count, size, or cost before acting on one.
25
30
 
26
- **Up next:** one line per entry in `.claude/tasks/index.md`, preserving order.
31
+ **Up next:** one line per task row in `.claude/tasks/index.md`, preserving order. A board carrying handoffs has a row per session that wrote one, so a report listing every row queues work nobody filed.
27
32
 
28
33
  **Active plans:** one line per file in `.claude/plans/`, linking each to its task file in `.claude/tasks/`. Say "None" if empty.
29
34
 
@@ -33,6 +38,14 @@ Output three sections:
33
38
 
34
39
  End with one line: `Start with: <first Up next item>` and note whether it has a linked plan.
35
40
 
41
+ When the board is empty and a handoff was found, name what the handoff leaves open instead: `Start with: <the open thread the handoff names>`. A recommendation slot filled with nothing reads as a failed run, and a handoff on an empty board is the shape a session leaves when it was reasoning rather than shipping.
42
+
36
43
  Do not offer to remove entries. A completed task is archived out of `.claude/tasks/` when work ships. The git log is the authoritative record of shipped work. Plan files are archived per the plan lifecycle rule in `CLAUDE.md`.
37
44
 
38
45
  Memory is updated only when a recorded fact becomes wrong, never on resume. A domain fact reaches a session through `.claude/context/`, which `claude-memory-capture` routes to and the three-tier model loads on demand, so the memory folder read here is the residue no context entry owns.
46
+
47
+ ## Writing the next one
48
+
49
+ This skill reads a handoff and never writes one. Reading and writing are two jobs, and the write happens at the close of a session rather than at its start.
50
+
51
+ Name the standard when the session asks how to leave a handoff behind, and let the session follow it directly. Any session may write one, whatever role it holds, so nothing here routes the request to another skill. A role carrying sections of its own adds them over the core per that role's own runbook.
@@ -11,12 +11,15 @@ Without this skill, a session picks a stack name from memory and installs rules
11
11
 
12
12
  The two costly failures are quieter. An unmatched technology gets the nearest rule instead of a stop, so the project carries governance authored for something adjacent and nothing marks it as a guess. A rule written directly into the target lands where no sync will ever reach it, so it drifts from the toolkit copy that owns it.
13
13
 
14
+ The last failure sits at a boundary rather than inside a run. `setup-init` turns away a project whose language it carries no stack for and sends it here, and a body stating only what this skill does not cover leaves that route running one way, so a person arriving on it reads a skill that never says it is the destination.
15
+
14
16
  ## Must
15
17
 
16
18
  - Read the catalog at run time and resolve both the stack and every extra against it
17
19
  - Name the evidence file behind each detected technology in the preview, so a wrong match is visible before the install rather than after
18
20
  - Dedupe extras against the rules the picked stack already resolves
19
21
  - Run the install non-interactively, since the CLI picker blocks where no terminal is attached
22
+ - State the inbound route from `setup-init`, so a project sent here for the language-neutral rule layer can tell it landed at the destination the chain named
20
23
 
21
24
  ## Must not
22
25
 
@@ -7,6 +7,11 @@ description: Detects a project's stack from its files and installs matching tool
7
7
 
8
8
  Automates `aitk gov install` by inferring the stack and extras from the current project, then shelling out to the CLI with the resolved arguments.
9
9
 
10
+ ## Scope
11
+
12
+ - Governance rules and nothing else. `setup-init` is the one-shot chain that installs governance beside tooling, standards, snippets, and the seeds.
13
+ - This is where `setup-init` sends a project whose language the toolkit carries no stack for. Rules install without the `base` development dependencies the tooling layer would drop on it. Resolve and stop exactly as below, since a language with no stack is the unmatched case `## Gap handling` already owns and arriving by that route changes none of it.
14
+
10
15
  ## Read the catalog
11
16
 
12
17
  Run this first to load the current stacks and rules. Never hardcode names. The catalog is the source of truth.
@@ -11,6 +11,8 @@ Without this skill, a session bootstrapping the index system writes `index.md` i
11
11
 
12
12
  The scan is where the other failures start. A walk that does not prune reaches `node_modules`, gitignored output, and the snippets folder, which is invoked by reference rather than browsed and needs neither an index nor per-file frontmatter. A folder that already carries an `index.md` gets a second one written over the first.
13
13
 
14
+ One failure predates the scan. `setup-init` declines an install that wants the Claude layer without the tooling chain and names this skill as where the index system gets bootstrapped, and a body that never states the inbound route leaves the person arriving on it unable to tell whether they landed at the destination or somewhere adjacent to it.
15
+
14
16
  Drafted frontmatter is a proposal, and a session that writes it before the user sees it turns a review into a cleanup. The convention block has the mirror failure. A session that paraphrases it into `CLAUDE.md` produces a copy that reads correctly and no longer matches its source, so the two drift with nothing reporting it. And a project with no `CLAUDE.md` gets one scaffolded to hold the block, which installs a file the project declined.
15
17
 
16
18
  ## Must
@@ -21,6 +23,7 @@ Drafted frontmatter is a proposal, and a session that writes it before the user
21
23
  - Validate with a dry run and stop on the first reported error, before writing for real
22
24
  - Paste the convention block verbatim from its single source
23
25
  - Emit the closeout exactly once, whatever the seed step concluded
26
+ - State the inbound route from `setup-init`, so an install wanting the Claude layer without the tooling chain can tell it landed at the destination the chain named
24
27
 
25
28
  ## Must not
26
29
 
@@ -13,6 +13,7 @@ Read `.claude/context/indexes.md` from the toolkit if context on the system is n
13
13
 
14
14
  - Bootstrap un-indexed folders only. Skip folders that already contain an `index.md`.
15
15
  - All-or-nothing per chosen folder. Every `*.md` sibling in a chosen folder gets `title` and `description` injected, or none does. Partial migration creates folders that hard-error on regen.
16
+ - This is where `setup-init` sends an install that wants the Claude layer without the tooling chain, once `aitk claude init` has seeded the docs. The work is the same either way, since the scan below prunes `.claude` and reads the project's own documentation folders whichever route reached it.
16
17
 
17
18
  ## Scan
18
19
 
@@ -11,12 +11,18 @@ Without this skill, a session installs a stack whose name it recalled rather tha
11
11
 
12
12
  Two failures are the expensive ones because both return success. A monorepo run drops husky into every subtree, git honors one `core.hooksPath`, and the extra hook directories go quiet with nothing reporting it. A caller reads the chain as onboarding complete when index bootstrap and plugin provisioning never ran, since no step states whether they belong to the chain.
13
13
 
14
+ Two more start at the resolve step. A project whose language the toolkit carries no stack for resolves to `base`, and the preview reports the resolved name without marking it as a fallback, so `base` development dependencies, scripts, and hooks land on a project that will not use them and nobody had the moment to decline.
15
+
16
+ The other is the refusal that ends rather than routes. An existing project, an install wanting the Claude layer without the tooling chain, and a language with no stack are the three states the chain does not serve, and each is declined by a declaration naming no destination, so the person who arrived with one reads a stop and goes no further.
17
+
14
18
  ## Must
15
19
 
16
20
  - Resolve every domain argument against a catalog read at run time, so a stack, rule, snippet, or tooling name the catalog does not carry cannot reach the command line
17
21
  - Preview the resolved chain before the first command runs, naming the evidence file behind each detected technology
18
22
  - Pass `--skip base` on every subtree past the first in a monorepo, so the shared hook layer lands once
19
23
  - State which onboarding steps the chain does not run, so a caller knows what is left rather than inferring completeness from a clean report
24
+ - Mark a stack reached by fallback as a fallback in the preview, and name what `base` carries, so the write can be declined at the one point it is still declinable
25
+ - Name a destination for every state the chain does not serve, in this body rather than only in a reference doc the person would have to already know to open, and say for each whether the chain stops or runs on a default
20
26
 
21
27
  ## Must not
22
28
 
@@ -35,3 +41,5 @@ Two failures are the expensive ones because both return success. A monorepo run
35
41
  - Provisioning Claude Code plugins: `setup-plugins`. Those install once per machine rather than into a project, so no project-scoped chain can carry them.
36
42
  - Running the verification scripts, which `setup-verify` owns and this chain invokes as its last step
37
43
  - Re-installing into a project that already has the files, which the per-domain `aitk <domain> install` and `aitk sync` commands own
44
+ - Deciding which of those per-domain commands an existing project needs: `toolkit-operator`. That skill reads the target before naming one, which this chain never does, so it is the destination rather than a guess made here.
45
+ - Seeding the `.claude/` folder without the tooling chain, which `aitk claude init` does on its own and `setup-indexes` finishes
@@ -12,6 +12,16 @@ Orchestrates the onboarding chain. Detects project type, resolves per-domain arg
12
12
  - This skill and `aitk init` run once on a fresh scaffold, never on an existing project. They do not guard against clobbering existing configs. When tempted to add guards, mode switches, or an existing-project branch, stop. Extend the per-domain `aitk <domain> install` or `aitk sync` paths instead.
13
13
  - The chain does not bootstrap the `index.md` system and does not provision Claude Code plugins. `setup-indexes` owns the first. `setup-plugins` owns the second, which installs once per machine rather than into a project, so no project-scoped chain can carry it. Name both in the report so a clean result does not read as onboarding complete.
14
14
 
15
+ ## Declined states
16
+
17
+ Three states reach this skill that the chain does not serve. Name the destination for each, so the refusal routes rather than ends. The first two stop the chain outright and the third runs it on a default the person may not want.
18
+
19
+ - **An existing project.** Stop and hand off to `toolkit-operator`. It reads what the target already carries before it names a command, which this chain never does, so any per-domain install picked here is a guess against configs nobody read. The Scope bullet above names the same commands as the authoring alternative, and this is the destination a person takes.
20
+ - **An install wanting the `.claude/` folder alone.** Stop. Run `aitk claude init` for the seed docs, then invoke `setup-indexes` to bootstrap the `index.md` system over the project's own documentation folders. Neither needs the tooling sync this chain runs.
21
+ - **A language the toolkit carries no stack for.** The chain still runs, on `base`, with the fallback marked in the preview. A project that wants none of what `base` carries declines there and takes `setup-gov` for the governance layer, which is language-neutral. Say so at the preview rather than resolving it here, since the fallback is a working default and only the person can say whether it fits.
22
+
23
+ Do not add a stack, a mode switch, or an existing-project branch to satisfy one of these. Each destination already exists and routing to it costs a line.
24
+
15
25
  ## Read catalogs
16
26
 
17
27
  Run in parallel. Never hardcode stack, rule, snippet, or standards names.
@@ -36,8 +46,8 @@ Read these from the project root in parallel, skipping any that do not exist:
36
46
 
37
47
  ## Resolve arguments
38
48
 
39
- - **Stack:** pick the closest governance stack by matching detected runtime or framework against stack names in the catalog. If nothing matches, fall back to `base`.
40
- - **Tooling stack:** pick the closest tooling stack from `aitk tooling list --json` (e.g. `vite-react`, `astro`). Distinct from the governance stack. Fall back to `base` if no framework match.
49
+ - **Stack:** pick the closest governance stack by matching detected runtime or framework against stack names in the catalog. If nothing matches, fall back to `base` and carry the fallback into the preview.
50
+ - **Tooling stack:** pick the closest tooling stack from `aitk tooling list --json` (e.g. `vite-react`, `astro`). Distinct from the governance stack. Fall back to `base` if no framework match, and carry that fallback into the preview too.
41
51
  - **Extras:** identify technologies not already covered by the picked stack. For each, find a rule whose `description` or `paths` points at that technology and pass it via `--add`. Do not add a rule the stack already pulls in.
42
52
  - **Snippets:** default to `all`. Narrow only if the user asked for a specific category.
43
53
  - **Skip (`--skip`):** `standards` and `wiki` are core and install by default. Add `--skip standards` or `--skip wiki` only when the user explicitly wants them left out.
@@ -56,14 +66,16 @@ Rules, snippets, and stacks are authored in the toolkit repo, never in the targe
56
66
  Before executing, output:
57
67
 
58
68
  - **Detected:** each technology with its evidence file
59
- - **Stack:** picked governance stack + resolved rule count
60
- - **Tooling stack:** picked tooling stack
69
+ - **Stack:** picked governance stack + resolved rule count. Mark it `fallback` when no detected runtime or framework matched a catalog name.
70
+ - **Tooling stack:** picked tooling stack. Mark it `fallback` on the same test, and name what `base` lands: configs, seeds, and gitignore entries in every case, plus the JavaScript development dependencies, scripts, and hook activation wherever a `package.json` exists to carry them. A project outside that ecosystem runs none of the second group and keeps the first.
61
71
  - **Extras:** each `--add` rule with a one-line reason
62
72
  - **Snippets:** resolved category
63
73
  - **Skip:** any `--skip` entries with reason
64
74
  - **Target:** resolved target path
65
75
  - **Commands:** the full chain that will run
66
76
 
77
+ A resolved name and a fallback read alike once written, so mark the fallback here rather than in the report. The preview is the last point before the first write, and the report runs after the files have landed.
78
+
67
79
  ## Execute
68
80
 
69
81
  Run the chain in order, starting immediately after the preview. Each step's permission dialog is the confirmation gate. Do not pause for additional confirmation. Run from the target project's current directory.
@@ -31,6 +31,7 @@ The last failure is a section no route reaches. `## Route` maps an intent or a d
31
31
  - Route a measurement intent to the audit offers from the route table itself, rather than leaving the section reachable only by reading past the table
32
32
  - Rank a lifecycle row against the audit offers where the route table itself states it, so a session acting on the lifecycle row reads the ranking without opening the section being ranked
33
33
  - Offer every audit whose surface the target carries, and offer none whose surface it lacks
34
+ - Hand off an audit that is a skill rather than a command, since the execute contract governs a CLI run and says nothing about a skill's own guards
34
35
  - Re-check the target after any operation that wrote, and report what those writes changed against the state read before acting
35
36
 
36
37
  ## Must not
@@ -69,7 +69,7 @@ On a measured report, take `<stack>` from the first name in `tooling.chain`, whi
69
69
 
70
70
  ### Audits
71
71
 
72
- Four audits measure a surface without changing it. Offer the ones whose surface the target carries, list them together, and let the user pick. Run none of them unasked, and never treat a finding as a reason to abandon the operation the user asked for, since each reports judgments beside facts.
72
+ Five audits measure a surface without changing it. Offer the ones whose surface the target carries, list them together, and let the user pick. Run none of them unasked, and never treat a finding as a reason to abandon the operation the user asked for, since each reports judgments beside facts.
73
73
 
74
74
  A lifecycle row and these offers fire together on a project carrying a context folder and no installed domain, which is the ranking the preamble states. Scaffold work on a target with nothing installed is real work, so an audit offered instead of it answers a question nobody asked, while an audit dropped in favor of it loses the one the user did.
75
75
 
@@ -77,10 +77,13 @@ A lifecycle row and these offers fire together on a project carrying a context f
77
77
  - A record folder present under `.claude/`, one of `plans`, `groundwork`, `intake`, or `memory`: offer `aitk records validate <kind>` for each one found
78
78
  - Markdown that git lists: offer `aitk markdown audit`
79
79
  - TypeScript or shell source present: offer `aitk comments scan`
80
+ - A `package.json` script that serves an interface, one of `dev`, `preview`, `serve`, or `start`: offer `claude-ux-measure`
80
81
 
81
82
  An audit offered against a surface the target lacks reports an empty run as a finding, which is the same defect as never offering it at all. Check the surface before naming the command.
82
83
 
83
- The markdown row is the one every target satisfies, since a project with no markdown is not one this reaches. Its condition is stated so the four rows read alike, and the row needs no gate beyond it.
84
+ The markdown row is the one every target satisfies, since a project with no markdown is not one this reaches. Its condition is stated so the five rows read alike, and the row needs no gate beyond it.
85
+
86
+ The last row is a skill handoff rather than a command, so `## Execute` does not govern it. Hand it off and stop, the way a lifecycle row hands off. Its condition reads a serving command rather than a source folder, since that skill measures what a browser receives and never opens the tree behind it. It carries a second condition this skill cannot read, which is a browser harness the project already installed, and the skill itself reports what it needs when it finds none. Testing for that here would put the detection in two places and let this one answer stale.
84
87
 
85
88
  ## Execute
86
89
 
@@ -103,7 +103,7 @@ The collision check is the one a person cannot run by eye. Paths come from the b
103
103
 
104
104
  Exit codes: `0` every check passed, `1` refused, `2` at least one finding. The `reason` field carries which gate refused: `no-board`, `no-ordering`, or `no-groups`. A board grouping under headings of its own trips `no-groups` rather than being read against columns it never declared.
105
105
 
106
- Columns are read from each table's own header rather than by position, so a project whose board differs from this one is reported for what it lacks. The `index`, `priority`, and `session` siblings are skipped, since none of the three is a task.
106
+ Columns are read from each table's own header rather than by position, so a project whose board differs from this one is reported for what it lacks. The `index` and `priority` siblings are skipped, along with every pre-compaction handoff, which takes one file per session under a `session-` prefix. None of them is a task, and a handoff counted as one would be reported as a task carrying no row on every session that wrote one.
107
107
 
108
108
  Skills branch on the findings rather than on the exit code:
109
109
 
@@ -181,6 +181,7 @@ Before the first feature session on a UI-heavy project, pick a design tier. The
181
181
  | `aitk:claude-docs` | When decisions diverged from plan, update `.claude/` docs |
182
182
  | `aitk:claude-ui-test` | After UI changes, generate and run e2e tests + visual checklist |
183
183
  | `aitk:claude-ux-audit` | Audit existing UI surfaces for missing states, edge cases, inconsistencies |
184
+ | `aitk:claude-ux-measure` | Measure paint, processor, and layout cost against a running interface, on the harness the project already carries |
184
185
  | `aitk:claude-autoship` | After plan approval, chain implement → verify → review → draft PR |
185
186
  | `aitk:systematic-debugging` | When a test fails or bug surfaces, enforce root-cause investigation before fixes |
186
187
  | `aitk:git-ship` | Post-feature: sync docs, commit, rename branch, open PR |
@@ -49,6 +49,8 @@ The chain is:
49
49
  - The agent follows the reference to generate eslint, vitest, playwright configs and the stack's setup script, and extends `.claude/context/ci.md` and `.claude/context/development.md` per the reference's extend sections <!-- audit-ignore-citations -->
50
50
  - `setup-verify` runs the installed `package.json` scripts (lint, typecheck, check, test, build) and reports pass or fail
51
51
 
52
+ The chain serves a fresh scaffold and names a destination for the three states it does not. An existing project goes to `aitk:toolkit-operator`, which reads what the project already carries before naming a per-domain command. An install wanting the Claude layer without the tooling chain runs `aitk claude init` for the seed docs and then `aitk:setup-indexes` for the index system. A language the toolkit ships no stack for is the one of the three the chain still runs for, on `base`, with the fallback marked in the preview so it can be declined there.
53
+
52
54
  Keep the `## Scripts` table in `.claude/context/development.md` current as scripts are added. Base tooling seeds that entry with the commands it installs, and each stack reference extends the table. `project-commands` reads it to start the app or run a check on request, so a command missing from the table cannot be run that way. A project whose entry outgrew one file and split into `.claude/context/development/` keeps the table in `overview.md`, which is where the skill looks next. <!-- audit-ignore-citations -->
53
55
 
54
56
  ### From scaffold to first feature
@@ -76,6 +78,8 @@ Escalate only for real web apps. The `setup-init` skill reads `package.json` and
76
78
 
77
79
  Markdown-heavy projects, CLI tools, docs sites, research notebooks, and scripting repos stay on `base`. Escalation is a ceiling move, not a default.
78
80
 
81
+ A project the toolkit ships no stack for lands on `base` the same way, and the skill marks that resolution as a fallback in its preview rather than reporting it as a match. Configs, seeds, and gitignore entries land either way, and the JavaScript development dependencies, scripts, and hook activation land only where a `package.json` exists to carry them. A project outside that ecosystem declines at the preview and takes `aitk:setup-gov` for the governance layer alone, which is language-neutral.
82
+
79
83
  Run `aitk tooling list --json` and `aitk gov list --json` to see the current catalogs. Never hardcode stack names.
80
84
 
81
85
  ### Core domains and skips
@@ -174,7 +178,7 @@ cd <your-project>
174
178
  claude
175
179
  ```
176
180
 
177
- In the session, invoke `aitk:setup-init`. The skill detects no framework, resolves tooling to `base`, governance to `base`, snippets to `all`, and auto-enables `standards` if `docs/` exists. It previews the chain, then runs `aitk init`.
181
+ In the session, invoke `aitk:setup-init`. The skill detects no framework, resolves tooling to `base`, governance to `base`, snippets to `all`, and auto-enables `standards` if `docs/` exists. The preview marks both stacks as fallbacks, since neither came from a match, then the chain runs `aitk init`.
178
182
 
179
183
  Ongoing: run `aitk sync --check .` to see what has drifted, then invoke `aitk:claude-seed-sync` for seed drift or `aitk sync .` for a catch-all refresh.
180
184
 
@@ -34,6 +34,7 @@ The `aitk:claude-design-extract` skill drafts the file, sourcing tokens from a p
34
34
  - `aitk:claude-design-extract` to draft `.claude/DESIGN.md`, from existing project signals or from requirements alone on day one
35
35
  - `aitk:claude-ui-test` for e2e test generation after UI changes
36
36
  - `aitk:claude-ux-audit` for UX gap detection on existing surfaces
37
+ - `aitk:claude-ux-measure` for what a running surface costs to paint, read against published thresholds
37
38
  - Anthropic's `frontend-design` plugin optional for light visual steering
38
39
 
39
40
  ### When to pick
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "0.93.0",
4
+ "version": "0.95.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -220,15 +220,39 @@ function checkPaths(
220
220
  )
221
221
  }
222
222
 
223
+ /**
224
+ * An entry carrying `*` is matched as a glob, which is what lets an arm forbid
225
+ * a file whose name a run derives rather than fixes. Pinning one spelling of a
226
+ * derived name passes vacuously against every other spelling, which reads as
227
+ * coverage the arm does not have. Returns the offending path so a failure names
228
+ * the file the run wrote rather than the pattern that caught it.
229
+ */
230
+ function writtenUnder(pattern: string, sandboxDir: string): string | undefined {
231
+ if (!pattern.includes('*')) {
232
+ return existsSync(join(sandboxDir, pattern)) ? pattern : undefined
233
+ }
234
+
235
+ for (const match of new Bun.Glob(pattern).scanSync({
236
+ cwd: sandboxDir,
237
+ dot: true,
238
+ })) {
239
+ return match
240
+ }
241
+
242
+ return undefined
243
+ }
244
+
223
245
  function checkAbsent(
224
246
  expectation: Expectation,
225
247
  sandboxDir: string,
226
248
  ): AssertionResult[] {
227
- return expectation.absent.map((path) =>
228
- existsSync(join(sandboxDir, path))
229
- ? { ok: false, message: `should not exist: ${path}` }
230
- : { ok: true, message: `absent: ${path}` },
231
- )
249
+ return expectation.absent.map((path) => {
250
+ const written = writtenUnder(path, sandboxDir)
251
+
252
+ return written
253
+ ? { ok: false, message: `should not exist: ${written}` }
254
+ : { ok: true, message: `absent: ${path}` }
255
+ })
232
256
  }
233
257
 
234
258
  /**
@@ -9,12 +9,21 @@ const PLANS_DIR = join('.claude', 'plans')
9
9
 
10
10
  /**
11
11
  * Siblings that sit on the board without being tasks: the generated index, the
12
- * hand-maintained ordering, and the pre-compaction session map. `validate`
13
- * reads the same list, so neither verb can count a sibling as a task the other
14
- * does not.
12
+ * hand-maintained ordering, and a handoff a session wrote before the file took
13
+ * one name per session. `validate` reads the same list, so neither verb can
14
+ * count a sibling as a task the other does not.
15
15
  */
16
16
  export const RESERVED_STEMS = ['index', 'priority', 'session'] as const
17
17
 
18
+ /** The pre-compaction handoff takes one file per session, so its stems vary. */
19
+ const SESSION_MAP_PREFIX = 'session-'
20
+
21
+ export function isReservedStem(stem: string): boolean {
22
+ const reserved: readonly string[] = RESERVED_STEMS
23
+
24
+ return reserved.includes(stem) || stem.startsWith(SESSION_MAP_PREFIX)
25
+ }
26
+
18
27
  /**
19
28
  * `bad-input` describes the command line rather than the board, which is the
20
29
  * split `record.ts` draws for the same reason. A caller naming two selectors
@@ -190,12 +199,11 @@ function isLivePlan(target: string, dir: string, root: string): boolean {
190
199
 
191
200
  export async function listTaskStems(dir: string): Promise<string[]> {
192
201
  const entries = await readdir(dir)
193
- const reserved: readonly string[] = RESERVED_STEMS
194
202
 
195
203
  return entries
196
204
  .filter((entry) => entry.endsWith('.md'))
197
205
  .map((entry) => entry.slice(0, -'.md'.length))
198
- .filter((stem) => !reserved.includes(stem))
206
+ .filter((stem) => !isReservedStem(stem))
199
207
  .sort()
200
208
  }
201
209
 
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { readdir, readFile } from 'node:fs/promises'
3
3
  import { join, resolve } from 'node:path'
4
- import { RESERVED_STEMS, tasksDir } from '@/tasks/archive'
4
+ import { isReservedStem, tasksDir } from '@/tasks/archive'
5
5
 
6
6
  const ORDERING_FILE = 'priority.md'
7
7
 
@@ -204,12 +204,11 @@ export function readBoard(text: string): {
204
204
 
205
205
  async function listTaskStems(dir: string): Promise<string[]> {
206
206
  const entries = await readdir(dir)
207
- const reserved: readonly string[] = RESERVED_STEMS
208
207
 
209
208
  return entries
210
209
  .filter((entry) => entry.endsWith('.md'))
211
210
  .map((entry) => entry.slice(0, -'.md'.length))
212
- .filter((stem) => !reserved.includes(stem))
211
+ .filter((stem) => !isReservedStem(stem))
213
212
  .sort()
214
213
  }
215
214
 
@@ -21,6 +21,7 @@ Reference docs for consistent authoring across the toolkit and target projects.
21
21
  - [Readme reference](readme.md): Readme voice, structure, and content conventions
22
22
  - [Requirements reference](requirements.md): Shape and content rules for .claude/REQUIREMENTS.md
23
23
  - [Governance rule reference](rule.md): Rule frontmatter, body shape, and voice for .claude/rules files
24
+ - [Session map reference](session.md): Filename and location, the sections a handoff carries, the write and read procedures, and how a role extends it
24
25
  - [Claude skill reference](skill.md): Claude skill structure and authoring rules
25
26
  - [Slug reference](slug.md): Transform from a git branch name to a slug, and the three responses to an empty result
26
27
  - [Standard reference](standard.md): Shape and content rules for authoring a standard
@@ -0,0 +1,106 @@
1
+ ---
2
+ title: Session map reference
3
+ description: Filename and location, the sections a handoff carries, the write and read procedures, and how a role extends it
4
+ ---
5
+
6
+ # Session map reference
7
+
8
+ Applies to `.claude/tasks/session-<slug>.md`. A session writes one before a compaction, because a compaction keeps conclusions and drops the reasoning that produced them. It is rewritten whenever the session that owns it learns something the next session would otherwise re-derive, and it is optional: a project whose sessions never approach a compaction carries none.
9
+
10
+ Any session writes one. The role a session holds decides which sections it adds on top of the core, never whether it may write at all.
11
+
12
+ ## Scope
13
+
14
+ Governs the pre-compaction handoff at `.claude/tasks/session-<slug>.md`: its filename, its sections, what a writer puts in each, and how a reader picks one up.
15
+
16
+ Does not govern:
17
+
18
+ - The task board the file sits beside, its filenames, and its archiving: `tasks.md`
19
+ - The transform from a branch name to the slug in the filename: `slug.md`
20
+ - The role-specific sections a caller adds over the core, which belong to that caller
21
+ - Which memories a session captures before writing, which is the capture surface's own subject
22
+
23
+ ## What a working session map looks like
24
+
25
+ A session map works when the session reading it can act on the reasoning without asking the session that wrote it:
26
+
27
+ - What is clean, what is running, and what is open right now?
28
+ - Which mistake already made would this session repeat?
29
+ - Which command, tool, or held instruction lies about the tree?
30
+ - Where did each claim come from, so a reader can tell a read from a recall?
31
+
32
+ A session map failing these is non-conforming even when it satisfies every shape rule below.
33
+
34
+ ## Filename and location
35
+
36
+ - Write one file per session, named `session-<slug>.md`, where `<slug>` is the branch-derived slug
37
+ - Fall back to `session-latest.md` when the branch resolves empty, since a handoff is scratch rather than a commit and a stop would lose the reasoning it exists to save
38
+ - Resolve the containing folder at the main worktree root, never inside a linked worktree
39
+ - Overwrite the file the writing session already owns. A stale entry read as current is worse than no handoff.
40
+ - Never write into a file another session owns. One file per session is what keeps two sessions closing near each other from each writing the whole file, where the loser leaves no trace.
41
+
42
+ ## Frontmatter
43
+
44
+ - `title` (required): `Session map`, so every handoff reads the same in a generated catalog
45
+ - `description` (required): what the board cannot show, and the date the file was written
46
+
47
+ ## Sections
48
+
49
+ Three sections form the core, in this order, and every session fills all three.
50
+
51
+ - `## State`: what is clean, what is running, what is open, and any untracked file that needs committing
52
+ - `## Mistakes worth not repeating`: what went wrong and the rule it yields
53
+ - `## Standing cautions`: commands that lie, tools that measure the wrong tree, and anything unbacked
54
+
55
+ Add a section only for content that fits none of the three and would otherwise be lost.
56
+
57
+ ## Writing one
58
+
59
+ 1. Capture what the session learned first, so the map cites what was written instead of restating the same lesson in prose.
60
+ 2. Run `aitk claude skills drift <the commit this session started from>` and record what it names under `## Standing cautions`. A skill body enters a session once and re-invoking the skill replays the held copy rather than the file, so the drift is worst at exactly this moment and a name here is a body the session has been following out of date. This step belongs to session length rather than to any role, so every writer runs it, and a refusal names the boundary of what the verb can read rather than a fault.
61
+ 3. Recover that commit from how long the session has been running with `git log -1 --format=%H --before='<duration> ago'`, rounding the duration up rather than down. Nothing on the machine records it. A ref older than the oldest load over-reports, and confirming a name costs one read of the body, so the generous end is the safe one and a guess at the exact commit is not worth making.
62
+ 4. Write only what a compaction destroys and no other artifact already carries. The board holds the ordering and what each task waits on, a task file holds its own findings, and a measurement folder holds its track.
63
+ 5. Cite a commit, a task, or a file and line for every claim, so the next session can tell a read from a recall.
64
+ 6. Fill a section from reasoning the session actually holds. A session with no cross-feature picture that fills `## State` from what is already in git has written a summary of the tree, which the reader can produce faster than they can read it.
65
+
66
+ Do not restate the board, and do not summarize the work that shipped, since version control already carries it.
67
+
68
+ ## Reading one back
69
+
70
+ - Read the newest map in the folder, by modification time, and treat it as the previous session's scratch rather than as a source
71
+ - Say nothing when no map exists. Absence is the common case, and a line reporting it every run is the noise that makes the present case unnoticeable.
72
+ - Re-measure every count, size, and cost the map states, since each was true when written
73
+
74
+ ## Extending it
75
+
76
+ - Carry every core section whatever else a role adds, and state in the surface that adds a section why the core cannot carry it
77
+ - Let the adding surface fix where its own section sits, so a role can place one between core sections where that reads better. The core order holds among the core sections alone.
78
+ - Keep a section out of the core when a session holding no such role would leave it unfilled. An unfilled section teaches a reader to skip the file.
79
+ - Carry a resume instruction inside the map itself when the routing to that instruction is what a compaction takes. The map survives what the instruction was loaded from, so each reaches a reader the other cannot.
80
+
81
+ ## Template
82
+
83
+ ```markdown
84
+ ---
85
+ title: Session map
86
+ description: <what the board cannot show, and the date it was written>
87
+ ---
88
+
89
+ # Session map
90
+
91
+ <one line marking the file throwaway and naming the board as the real source>
92
+
93
+ ## State
94
+
95
+ <what is clean, what is running, what is open, and any untracked file that needs committing>
96
+
97
+ ## Mistakes worth not repeating
98
+
99
+ <what went wrong and the rule it yields>
100
+
101
+ ## Standing cautions
102
+
103
+ <commands that lie, tools that measure the wrong tree, and anything unbacked>
104
+ ```
105
+
106
+ A role-specific section is placed by the surface that adds it, so the skeleton above shows the core alone.
@@ -21,6 +21,7 @@ Does not govern:
21
21
  - Phase-label format and which surfaces a label may appear on: `versioning.md`
22
22
  - Sequencing across versions and why the order is what it is: `roadmap.md`
23
23
  - Architectural reasoning that outlives a task: `architecture.md`
24
+ - The pre-compaction handoff sitting in the folder, its filename and its sections: `session.md`
24
25
  - When a project opens a task at all, which is project policy rather than a shape rule
25
26
 
26
27
  ## Layout
@@ -29,17 +30,21 @@ Does not govern:
29
30
  .claude/tasks/
30
31
  ├── index.md ← generated, never hand-edited
31
32
  ├── priority.md ← hand-maintained execution order
32
- ├── session.md ← optional, what a compaction is about to destroy
33
+ ├── session-<slug>.md ← optional, what a compaction is about to destroy
33
34
  ├── v09.0-sync-paths.md
34
35
  └── v13.0-toolkit-drift.md
35
36
  ```
36
37
 
37
38
  One file per task is what keeps the board safe under parallel sessions. Two sessions working different tasks never write the same file, which matters because a gitignored board has no history to recover a clobbered write from.
38
39
 
39
- Three siblings sit in the folder without being tasks, and each earns its place by being governed somewhere. `index.md` and `priority.md` are governed here. `session.md` is the pre-compaction handoff, written by `orchestrator-handoff` and read by `orchestrator-resume`, and it is optional: a project running no orchestrator carries no such file. Anything reading the folder as a task list skips all three, so a name outside the set is a task whatever it holds.
40
+ Siblings sit in the folder without being tasks, and each earns its place by being governed somewhere. `index.md` and `priority.md` are governed here. Every `session-` file is a pre-compaction handoff governed by `session.md`, and each is optional: a project whose sessions never approach a compaction carries none. Anything filtering the folder to tasks skips all of them, so a name outside the set is a task whatever it holds.
41
+
42
+ The handoff takes one file per session for the reason a task does. A single shared path puts two sessions closing near each other on one file that neither can watch the other write, and the loser leaves no trace on a board with no history behind it.
40
43
 
41
44
  `index.md` is generated from sibling frontmatter. The folder is gitignored, so the whole-repo index walk skips it and a hook passing the changed path regenerates it instead. Never hand-edit it.
42
45
 
46
+ The catalog is the one reader that filters nothing, so it carries a row per sibling alongside the tasks. That is what a folder catalog is for, and the handoffs are what make it worth stating: a board accumulates one row per session that ever wrote one, with nothing pruning them. Anything reading the catalog as the backlog therefore does its own filtering, and a reader that takes every row as a task reports the handoffs as queued work.
47
+
43
48
  The `claude-tasks` skill creates and archives task files. `claude-docs` marks outcomes `[x]` in an existing file and sweeps the plans those tasks cite. Neither does the other's job.
44
49
 
45
50
  ## Ordering