@erclx/aitk 0.58.0 → 0.60.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-autoship/SKILL.md +2 -0
- package/claude/skills/claude-docs/SKILL.md +1 -1
- package/claude/skills/claude-feature/SKILL.md +8 -55
- package/claude/skills/claude-memory-capture/SKILL.md +10 -24
- package/claude/skills/claude-memory-review/SKILL.md +3 -1
- package/docs/agents/commands.md +1 -0
- package/docs/agents/index.md +1 -0
- package/docs/agents/records.md +58 -0
- package/docs/ai-workflow.md +4 -0
- package/governance/rules/claude/558-plan.md +22 -0
- package/governance/rules/claude/559-memory.md +20 -0
- package/package.json +1 -1
- package/src/cli.ts +4 -0
- package/src/commands/records.ts +161 -0
- package/src/records/validate.ts +767 -0
- package/standards/groundwork.md +1 -0
- package/standards/index.md +2 -0
- package/standards/intake.md +1 -0
- package/standards/memory.md +131 -0
- package/standards/plan.md +145 -0
- package/standards/tasks.md +1 -0
- package/tooling/claude/seeds/CLAUDE.md +2 -5
|
@@ -41,6 +41,8 @@ If the two commands differ, the session is already in a linked worktree. Continu
|
|
|
41
41
|
|
|
42
42
|
Read `.claude/plans/feature-<slug>.md` at the main worktree root. This file is the scope for this run.
|
|
43
43
|
|
|
44
|
+
Its sections and its answer contract are fixed by `.claude/standards/plan.md`, or `${CLAUDE_SKILL_DIR}/../../standards/plan.md` when the project does not have it. A blank `- Answer:` accepts the `- Suggested:` line above it, so an unanswered question is a decision this run executes rather than a reason to stop.
|
|
45
|
+
|
|
44
46
|
## Step 2: implement
|
|
45
47
|
|
|
46
48
|
Implement only what the plan describes. Do not expand scope. Do not refactor neighbors. Do not touch files outside the plan's "Files to touch" list without reason.
|
|
@@ -234,7 +234,7 @@ The base lint-staged config runs `aitk indexes regen` on every committed `*.md`,
|
|
|
234
234
|
|
|
235
235
|
Sweep reviews this session consumed, and sweep plans across the whole board. Resolve all paths at the main worktree root, not the current worktree. See Worktrees in `CLAUDE.md`.
|
|
236
236
|
|
|
237
|
-
**Plans.** Scan every file in `.claude/tasks/`, not only the ones this session touched. For each task file whose outcomes are now all `[x]`, check for a `Plan:` line directly under the title and parse the target. The line carries a markdown link, so read the target out of the parentheses rather than taking the rest of the line. A task still carrying the older bare-path form parses the same way once the link is absent, so accept both. Resolve the target against `.claude/tasks/` before routing on it, which lands `../plans/x.md` and `.claude/plans/x.md` on the same file. The bullets below name resolved locations, so an unresolved target falls to the last one and no plan is ever archived. Never delete a plan.
|
|
237
|
+
**Plans.** Scan every file in `.claude/tasks/`, not only the ones this session touched. For each task file whose outcomes are now all `[x]`, check for a `Plan:` line directly under the title and parse the target. The line carries a markdown link, so read the target out of the parentheses rather than taking the rest of the line. A task still carrying the older bare-path form parses the same way once the link is absent, so accept both. Resolve the target against `.claude/tasks/` before routing on it, which lands `../plans/x.md` and `.claude/plans/x.md` on the same file. The bullets below name resolved locations, so an unresolved target falls to the last one and no plan is ever archived. Never delete a plan. `.claude/standards/plan.md` owns the archive destination and why a shipped plan is moved rather than removed.
|
|
238
238
|
|
|
239
239
|
Board-wide scope is the one place this sweep reaches past Step 3's rule against touching task files the session did not change. A board carrying a task that closed while an earlier run missed its archive is the defect this exists to clear, and skipping those tasks would preserve it. Reaching them is safe because the archive moves the plan and points the task at the new path, so a task from unrelated work ends up with a working pointer rather than a broken one.
|
|
240
240
|
|
|
@@ -35,33 +35,14 @@ Measure against the tree rather than recall. Grep for each construct the plan wi
|
|
|
35
35
|
|
|
36
36
|
## Step 3: build the plan
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
The section list, what each section holds, the suggested-and-answer contract, and the lifecycle are fixed by `.claude/standards/plan.md`, or `${CLAUDE_SKILL_DIR}/../../standards/plan.md` when the project does not have it. Read it before writing the file and follow it rather than working the shape from memory.
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
- **Constraints:** durable rules the work must respect (patterns to reuse, surfaces not to touch, platform limits). Optional. Include when an orchestrator or prior context supplies them, omit the section otherwise. A constraint naming a surface to leave alone carries the distinction under Constraints below.
|
|
42
|
-
- **Files to touch:** each file with a one-line reason
|
|
43
|
-
- **Risks:** conflicts, coupling, or tricky spots. When the plan establishes a resource with more than one consumer, list the consumers and mark each read or write, because a policy stated over that resource has to hold for the writers and not just the consumer that prompted it. If none, use `None identified.`
|
|
44
|
-
- **Questions:** numbered list of things to resolve before starting. Each carries a `- Suggested:` line and an `- Answer:` slot (see Suggestions below). If none, use `None identified.`
|
|
40
|
+
What this skill adds on top of the standard:
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
### Constraints
|
|
49
|
-
|
|
50
|
-
A constraint naming a surface to leave alone forbids two different acts. Name which one, since a constraint carrying only the surface leaves the executing session to guess.
|
|
51
|
-
|
|
52
|
-
- Forbid conforming the surface to whatever shape the change introduces. This is the act a scope constraint means, and it keeps the branch from growing a second concern.
|
|
53
|
-
- Never forbid retargeting a pointer the change breaks. A rename, a split, or a deletion that leaves a citation behind ships a dangling reference, so fixing it is required work rather than scope creep.
|
|
54
|
-
- Decide both acts for every surface the constraint names. Carving the distinction out for one file and leaving its siblings under the bare wording is how a plan ships one correct call beside one broken reference.
|
|
55
|
-
|
|
56
|
-
### Suggestions
|
|
57
|
-
|
|
58
|
-
Attach a `- Suggested:` line to every question, then an empty `- Answer:` slot below it. A blank answer means accept the suggestion at execution time. This makes the plan decision-ready in one pass, with no separate decision-help round.
|
|
59
|
-
|
|
60
|
-
- Apply senior judgment: pick the best option and state it in one line with its reason or main tradeoff. No padding, no alternatives unless they change the pick.
|
|
42
|
+
- Apply senior judgment to every `- Suggested:` line. Pick the best option and state it in one line with its reason or main tradeoff. No padding, no alternatives unless they change the pick.
|
|
61
43
|
- Suggest a real default when best practice, the codebase, or prior context points to one.
|
|
62
|
-
-
|
|
63
|
-
|
|
64
|
-
When three or more questions remain, keep chat output to the file pointer plus a short summary. Inline chat is fine when two or fewer remain.
|
|
44
|
+
- Prefer `None identified.` over low-signal fillers. A small feature should produce a short plan, not a padded one.
|
|
45
|
+
- When three or more questions remain, keep chat output to the file pointer plus a short summary. Inline chat is fine when two or fewer remain.
|
|
65
46
|
|
|
66
47
|
## Step 4: output
|
|
67
48
|
|
|
@@ -74,7 +55,7 @@ A consumer list is a `Risks` entry, which already forces Full. Establishing a re
|
|
|
74
55
|
|
|
75
56
|
### Small mode
|
|
76
57
|
|
|
77
|
-
Output the plan to chat. Do not write a plan file.
|
|
58
|
+
Output the plan to chat. Do not write a plan file. The markers match the standard's, so a plan that later grows into a file keeps the shape it was drafted in.
|
|
78
59
|
|
|
79
60
|
```markdown
|
|
80
61
|
**Files to touch:**
|
|
@@ -98,37 +79,9 @@ Omit empty sections. Do not print `None identified.` in chat.
|
|
|
98
79
|
|
|
99
80
|
Derive a 2-to-4-word kebab-case slug from the feature description. Write the full plan to `.claude/plans/feature-<slug>.md` at the main worktree root, not the current worktree. See Worktrees in `CLAUDE.md`. Create the directory if it does not exist.
|
|
100
81
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
```markdown
|
|
104
|
-
# Feature: <short title>
|
|
105
|
-
|
|
106
|
-
<feature description>
|
|
107
|
-
|
|
108
|
-
## Summary
|
|
109
|
-
|
|
110
|
-
- <one-line bullet covering the goal>
|
|
111
|
-
- <one-line bullet covering the main deliverables>
|
|
112
|
-
- <one-line bullet covering the key decision or trade-off>
|
|
113
|
-
|
|
114
|
-
**Constraints:**
|
|
82
|
+
The file follows the template in `.claude/standards/plan.md`. Copy the shape from there rather than from this body, so one edit to the standard moves every plan.
|
|
115
83
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
**Files to touch:**
|
|
119
|
-
|
|
120
|
-
- `path/to/file`: reason
|
|
121
|
-
|
|
122
|
-
**Risks:**
|
|
123
|
-
|
|
124
|
-
- <risk>
|
|
125
|
-
|
|
126
|
-
**Questions:**
|
|
127
|
-
|
|
128
|
-
1. <question>
|
|
129
|
-
- Suggested: <pick>, <reason or tradeoff>
|
|
130
|
-
- Answer:
|
|
131
|
-
```
|
|
84
|
+
Run `aitk records validate plans` after writing the file when the CLI is on PATH. It reports a section, a filename, or an answer slot that does not hold, and it writes nothing.
|
|
132
85
|
|
|
133
86
|
Then output in chat:
|
|
134
87
|
|
|
@@ -9,6 +9,8 @@ Scan the current session for patterns worth persisting, send each to the surface
|
|
|
9
9
|
|
|
10
10
|
A fact about a domain belongs in that domain's context entry, which the three-tier model already loads on demand. Writing it to memory instead puts it in a folder nothing opens. Routing is therefore the point of this skill and the memory file is the fallback.
|
|
11
11
|
|
|
12
|
+
The filename and its type prefix, the frontmatter, the body shape each type carries, and the lifecycle are fixed by `.claude/standards/memory.md`, or `${CLAUDE_SKILL_DIR}/../../standards/memory.md` when the project does not have it. Read it before writing an entry and follow it rather than working the shape from memory.
|
|
13
|
+
|
|
12
14
|
## Guards
|
|
13
15
|
|
|
14
16
|
- All `.claude/memory/` reads and writes resolve at the main worktree root, not the current worktree. See Worktrees in `CLAUDE.md`.
|
|
@@ -20,7 +22,8 @@ A fact about a domain belongs in that domain's context entry, which the three-ti
|
|
|
20
22
|
|
|
21
23
|
Read in parallel from the project root, skipping any that do not exist:
|
|
22
24
|
|
|
23
|
-
-
|
|
25
|
+
- `.claude/standards/memory.md`: the filename, frontmatter, body shape, and lifecycle every entry follows
|
|
26
|
+
- `CLAUDE.md`: the project's write location and any rule it states over the folder
|
|
24
27
|
- `.claude/memory/index.md`: existing index, to avoid duplicates
|
|
25
28
|
- `.claude/context/index.md`: the domain catalog Step 3 routes against
|
|
26
29
|
- `.claude/standards/prose.md`: voice and banned words applied to memory file bodies
|
|
@@ -30,14 +33,9 @@ Read a standard from `${CLAUDE_SKILL_DIR}/../../standards/` instead when the pro
|
|
|
30
33
|
|
|
31
34
|
## Step 2: classify candidates
|
|
32
35
|
|
|
33
|
-
Scan the session and group candidate patterns
|
|
34
|
-
|
|
35
|
-
- **feedback**: explicit user corrections, stated preferences, or non-obvious confirmations
|
|
36
|
-
- **project**: decisions, initiatives, deadlines, or motivations not derivable from git or code
|
|
37
|
-
- **user**: role, expertise, responsibilities, or working preferences
|
|
38
|
-
- **reference**: pointers to external systems (dashboards, trackers, channels)
|
|
36
|
+
Scan the session and group candidate patterns as `feedback`, `project`, `user`, or `reference`. What each type holds and what makes one fire are the Types table in `.claude/standards/memory.md`. Read the table and classify against it rather than against a recollection of the four names.
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
Scan the whole session rather than its last exchange. A rule the user stated early and you followed since reads as settled and is exactly the one no file records.
|
|
41
39
|
|
|
42
40
|
## Step 3: route what a context entry owns
|
|
43
41
|
|
|
@@ -65,26 +63,14 @@ For each remaining candidate, grep `.claude/memory/` for an existing file on the
|
|
|
65
63
|
|
|
66
64
|
## Step 5: write the residue
|
|
67
65
|
|
|
68
|
-
|
|
66
|
+
Write each remaining candidate to `.claude/memory/<type>-<slug>.md`, following the template and the shape rules in `.claude/standards/memory.md`. Copy the shape from there rather than from this body, so one edit to the standard moves every entry.
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
title: <one-line human title, as it should read in the index>
|
|
73
|
-
description: <one-line description per .claude/standards/prose.md § Frontmatter descriptions>
|
|
74
|
-
category: <Feedback|Project|User|Reference>
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
<memory body>
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
`category` is the type in sentence case, which is what the index renderer groups on, while the filename keeps the lowercase `<type>-` prefix. A description opening with a backtick or a colon needs single quotes, or the frontmatter fails to parse and the index goes stale.
|
|
81
|
-
|
|
82
|
-
Feedback and project bodies must be three lines: the rule or fact on one line, a `**Why:**` line naming the session signal, and a `**How to apply:**` line for when the rule fires next. Keep each line tight. No narrative.
|
|
83
|
-
|
|
84
|
-
User and reference bodies are a single sentence each.
|
|
68
|
+
Two of its rules are the ones a capture pass gets wrong under time pressure. State the rule rather than the incident that produced it, since the session ending is the only reader who has the narrative. Write the `title` as the rule itself, never as the filename stem.
|
|
85
69
|
|
|
86
70
|
Do not edit the index. `.claude/memory/index.md` is generated from sibling frontmatter by a `PostToolUse` hook, the same way the task board's index is, so a hand-appended row is drift the next regeneration discards.
|
|
87
71
|
|
|
72
|
+
Run `aitk records validate memory` when the writes are done and fix what it names. It reads the whole pen rather than this session's writes, so treat a finding on a carried entry as one to fix in place rather than as a reason to stop.
|
|
73
|
+
|
|
88
74
|
## Output
|
|
89
75
|
|
|
90
76
|
Respond with one line per fact routed, written, or updated:
|
|
@@ -7,6 +7,8 @@ description: Reviews `.claude/memory/` and proposes per-entry actions (promote t
|
|
|
7
7
|
|
|
8
8
|
This skill drives the full memory review lifecycle in five phases. Pick the phase from what the user said and whether a review receipt already exists at `<main-root>/.claude/review/memory-review-*.md`.
|
|
9
9
|
|
|
10
|
+
What an entry looks like and why a retired one is moved rather than deleted are fixed by `.claude/standards/memory.md`, or `${CLAUDE_SKILL_DIR}/../../standards/memory.md` when the project does not have it. Read it before rewriting an entry, since a promotion rewrites the rule and a rewrite has to leave the entry conforming.
|
|
11
|
+
|
|
10
12
|
| User intent | Phase | Mutates |
|
|
11
13
|
| ---------------------------------------------------------------------- | --------- | ---------------------------- |
|
|
12
14
|
| "review memory", "promote memory", "sweep stale memories" (no receipt) | Propose | review file only |
|
|
@@ -68,7 +70,7 @@ For each in-scope entry (see Scope), pick one action:
|
|
|
68
70
|
- **Hand off to governance**: the rule is coding-standards class (typescript, testing, naming, error-handling, performance, logging, concurrency, planning). Do not author the rule file inline. In the toolkit repo, point the user at `aitk-governance` and `.claude/standards/rule.md`, which own the source-of-truth rules under `governance/rules/`. In a target project, point the user at the `create-rule` skill, which scaffolds a project-local rule under `.claude/rules/`. Never edit the synced `.claude/rules/` copies of toolkit rules, because `aitk gov sync` overwrites them. Stop at handoff.
|
|
69
71
|
- **Retire**: the rule is stale, already absorbed into a durable surface, too vague to phrase as a rule, or a one-time incident narrative. Apply moves the file to `.claude/.tmp/memory-archive/` rather than deleting it.
|
|
70
72
|
|
|
71
|
-
Retire is an archive, not a deletion
|
|
73
|
+
Retire is an archive, not a deletion, which `.claude/standards/memory.md` states as the rule and this skill executes. The archive is worth less than a plan's, since a promoted entry survives in its destination and a stale one is discarded on purpose, which is why the move is cheap rather than free.
|
|
72
74
|
|
|
73
75
|
When two or more memories collapse into one rule on the same target, propose them as a single merged edit under the matching promote category. The consolidate case is a variant of promote, not a separate action.
|
|
74
76
|
|
package/docs/agents/commands.md
CHANGED
|
@@ -28,6 +28,7 @@ Full help: `aitk <command> --help`. Behavior notes for the install and sync verb
|
|
|
28
28
|
| `aitk transcripts <url>` | Fetch a YouTube transcript with metadata frontmatter (needs `yt-dlp`) |
|
|
29
29
|
| `aitk tasks archive` | Move a shipped task off the board, clear its ordering row, and regenerate the index |
|
|
30
30
|
| `aitk tasks validate` | Report board rows whose plan, task file, group, or file set does not hold (`--json`) |
|
|
31
|
+
| `aitk records validate` | Report a session record against the standard governing it, per kind (`--json`) |
|
|
31
32
|
| `aitk comments scan` | Measure comment density by language and comment kind, with a trend recomputed from git |
|
|
32
33
|
| `aitk context audit` | Report required sections, length, depth, bullet weight, cited paths, provenance, and drift |
|
|
33
34
|
| `aitk claude skills audit` | Report both skill corpora against the mechanical rules in `standards/skill.md` |
|
package/docs/agents/index.md
CHANGED
|
@@ -18,6 +18,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
|
|
|
18
18
|
- [Install and sync](install-and-sync.md): What each install and sync verb writes, refuses, or leaves alone, and how drift is attributed in a target project
|
|
19
19
|
- [Output shape](output-shape.md): Two framed shapes every command renders into, how JSON and --names modes keep stdout clean, and the exit discipline that lets piped output drain
|
|
20
20
|
- [Overview](overview.md): What this folder covers, the invocation rules every command inherits, and where domain behavior is documented instead
|
|
21
|
+
- [Records](records.md): Validating the gitignored session records under .claude/, the per-kind checks, the refusal reasons, and why the root defaults to the main worktree
|
|
21
22
|
- [Sandbox](sandbox.md): Scenario routing, the expectation scoring surface, and the coverage census over scenarios and skills
|
|
22
23
|
- [Scripting](scripting.md): The runtime catalogs that replace hardcoded names, what each carries, and a headless invocation per domain
|
|
23
24
|
- [Skill audit](skills-audit.md): Measuring both skill corpora against standards/skill.md, the checks it reads, and the requirement gate that is the only failing one
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Records
|
|
3
|
+
description: Validating the gitignored session records under .claude/, the per-kind checks, the refusal reasons, and why the root defaults to the main worktree
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Records
|
|
7
|
+
|
|
8
|
+
## Validate
|
|
9
|
+
|
|
10
|
+
`aitk records validate <kind>` reports where a session record and the standard governing it disagree. The four kinds are `plans`, `groundwork`, `intake`, and `memory`, each a gitignored folder under `.claude/`.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
aitk records validate plans
|
|
14
|
+
aitk records validate memory
|
|
15
|
+
aitk records validate intake --json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| Option | Behavior |
|
|
19
|
+
| --------------- | --------------------------------------------- |
|
|
20
|
+
| `--json` | Add a machine-readable record on stdout |
|
|
21
|
+
| `--root <path>` | Project root, defaulting to the main worktree |
|
|
22
|
+
|
|
23
|
+
It reads and never writes. Each folder is per-machine scratch with no history behind it, so a repair that guessed wrong could not be undone, and the report names the record for a session to fix.
|
|
24
|
+
|
|
25
|
+
Nothing fires it automatically. The folders are gitignored, so the standards-audit hook exits early on them and any check reading changed files from git never lists one. The verb runs at the moment a session claims the record is finished, which is the same placement `aitk tasks validate` takes over the board.
|
|
26
|
+
|
|
27
|
+
### What each kind checks
|
|
28
|
+
|
|
29
|
+
| Kind | What it reports |
|
|
30
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| `plans` | A filename that is not `feature-<slug>.md`, a missing `# Feature:` heading, a missing required section, a files-to-touch entry naming no file or saying nothing about one, and a question carrying no suggestion or no answer slot |
|
|
32
|
+
| `groundwork` | A track with no `README.md` or no `01-current-state.md`, a file missing `title` or `description`, a `README.md` with no `date` as `YYYY-MM-DD`, an unnumbered file, and a track holding a decision without its handoff or the reverse |
|
|
33
|
+
| `intake` | A dump with no `00-overview.md`, the same frontmatter and numbering checks, an item missing any of `Problem`, `Fix`, `Worth it`, or `You`, and an item carrying `Open` with no `Suggested` |
|
|
34
|
+
| `memory` | A filename whose prefix names none of the four types, an entry missing `title`, `description`, or `category`, a `category` disagreeing with that prefix, a title repeating the filename, and a rule-bearing body missing a part |
|
|
35
|
+
|
|
36
|
+
The half-closed track is the groundwork check a reader cannot run by eye. A folder holding `06` without `07` reads as closed to anyone scanning filenames while the file a returning session actually opens is absent.
|
|
37
|
+
|
|
38
|
+
The item check skips `00-overview.md` and `99-next-session.md`, since neither holds items and running it over the handoff would report every heading it carries. The memory walk skips `index.md` for the same reason, since the catalog is generated from its siblings rather than authored as an entry.
|
|
39
|
+
|
|
40
|
+
A memory `category` is compared against the sentence-case form of the filename prefix rather than checked field by field, so one finding covers a prefix outside the four types, a field disagreeing with the prefix, and a casing drift that would open a second group in the catalog. The body check runs on `feedback` and `project` entries alone, because a `user` or `reference` entry is a single sentence by design and has no rule to apply.
|
|
41
|
+
|
|
42
|
+
A plan section opens as a bold label or as an H2 and the check counts both, naming the standard's spelling when it reports one missing. The corpus splits roughly four to one between the two forms, so failing the variant would report nearly every plan on a rule that costs a reader nothing.
|
|
43
|
+
|
|
44
|
+
A section runs to the next marker-shaped line whatever it names, so a plan carrying a label of its own closes the section above it rather than collecting into it. Fenced blocks are dropped before any of this, since a plan showing the shape it writes puts real-looking bullets and headings inside a fence.
|
|
45
|
+
|
|
46
|
+
### Exit codes and refusals
|
|
47
|
+
|
|
48
|
+
Exit codes: `0` every check passed, `1` refused, `2` at least one record carries a finding. A `reason` field carries which gate fired: `no-folder` when the kind's directory does not exist, and `unknown-kind` when the argument names no published kind.
|
|
49
|
+
|
|
50
|
+
The folders are shared scratch at the main worktree root, so `--root` defaults to the first entry of `git worktree list` rather than the working directory. A linked worktree validates the same records every other session reads.
|
|
51
|
+
|
|
52
|
+
Skills branch on the findings rather than on the exit code:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
aitk records validate plans --json | jq -r '.findings[] | "\(.kind): \(.subject)"'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For the shapes each check enforces, see `.claude/standards/plan.md`, `.claude/standards/groundwork.md`, `.claude/standards/intake.md`, and `.claude/standards/memory.md`.
|
package/docs/ai-workflow.md
CHANGED
|
@@ -79,6 +79,10 @@ Before a handoff, the orchestrator checks the plan against the tree rather than
|
|
|
79
79
|
|
|
80
80
|
`.claude/plans/`, `.claude/review/`, and `.claude/memory/` all resolve at the main worktree root, so artifacts created in any session are visible from any sibling worktree. See [Claude Code and git worktrees](../wiki/claude/claude-worktrees.md) for the full rule and the domain-level fan-out guidance.
|
|
81
81
|
|
|
82
|
+
The plan's shape is fixed by `.claude/standards/plan.md`: the section list, the filename, the lifecycle, and the contract its questions keep. Every question carries a `- Suggested:` line and an empty `- Answer:` slot, and a blank answer accepts the suggestion at execution time. That default is what makes a plan decision-ready in one pass, and it is the opposite of the contract an intake folder keeps, where an empty slot means nobody reached the item.
|
|
83
|
+
|
|
84
|
+
`aitk records validate plans` reports where a plan and that standard disagree: a filename that is not `feature-<slug>.md`, a missing required section, a files-to-touch entry naming no file, and a question carrying a suggestion with no answer slot. The same verb takes `groundwork`, `intake`, and `memory`, which are governed the same way and were unreachable for the same reason. Nothing fires it automatically, because all four folders are gitignored and every check the repository runs reads changed files from git. It reports and never writes, since the folders are per-machine scratch with no history to recover a wrong repair from.
|
|
85
|
+
|
|
82
86
|
A plan that ships is archived, never deleted. `aitk:claude-docs` moves it to `.claude/plans-archive/` and retargets the task file's `Plan:` line at the new location, so a completed task still leads to the reasoning behind it. Both folders are gitignored, which is why a deleted plan had no recovery path. A plan cited by more than one task stays put until the last of them closes, since moving it early would strand every other pointer.
|
|
83
87
|
|
|
84
88
|
The sweep reads the whole board rather than the tasks the session touched. It is the one place the skill reaches past its own rule against editing a task file the session did not change, because a task that closed while an earlier run missed its archive is exactly what the sweep exists to clear. Reaching it is safe: the archive moves the plan and retargets the pointer in the same pass, so an untouched task ends up with a working link rather than a broken one.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Route .claude/plans/ edits to the plan standard for sections, the answer contract, and the archive move
|
|
3
|
+
paths:
|
|
4
|
+
- '.claude/plans/**'
|
|
5
|
+
- '.claude/plans-archive/**'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Plan standards
|
|
9
|
+
|
|
10
|
+
## The answer contract
|
|
11
|
+
|
|
12
|
+
- Never fill an `- Answer:` slot on behalf of the person who owns it. A blank slot accepts the suggestion at execution time.
|
|
13
|
+
- Never ship a question without a `- Suggested:` line. Write `- Suggested: needs your call, <why>` where the answer turns on preference.
|
|
14
|
+
|
|
15
|
+
## Archiving
|
|
16
|
+
|
|
17
|
+
- Move a shipped plan to `.claude/plans-archive/`. Never delete one.
|
|
18
|
+
- Amend a plan in place when a decision changes. Do not append a second passage narrating the change.
|
|
19
|
+
|
|
20
|
+
## Authority
|
|
21
|
+
|
|
22
|
+
- Follow `.claude/standards/plan.md` for the filename and slug, the required sections, the suggested-and-answer contract, and the lifecycle. It is the single source.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Route .claude/memory/ edits to the memory standard for the filename, frontmatter, body shape, and lifecycle
|
|
3
|
+
paths:
|
|
4
|
+
- '.claude/memory/**'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Memory standards
|
|
8
|
+
|
|
9
|
+
## Routing
|
|
10
|
+
|
|
11
|
+
- Write no memory entry for a fact a per-domain context entry already owns. Memory keeps the residue, which in practice is feedback about how to work.
|
|
12
|
+
|
|
13
|
+
## The pen
|
|
14
|
+
|
|
15
|
+
- Never delete a memory entry. Retire one by moving it to an archive under its own name, since the folder is gitignored and a wrong call has no undo.
|
|
16
|
+
- Never hand-edit `.claude/memory/index.md`. A hook regenerates it from sibling frontmatter.
|
|
17
|
+
|
|
18
|
+
## Authority
|
|
19
|
+
|
|
20
|
+
- Follow `.claude/standards/memory.md` for the filename and type prefix, the frontmatter, the body shape per type, links between entries, and the lifecycle. It is the single source.
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { register as transcripts } from '@/commands/transcripts'
|
|
|
22
22
|
import { register as tasks } from '@/commands/tasks'
|
|
23
23
|
import { register as comments } from '@/commands/comments'
|
|
24
24
|
import { register as context } from '@/commands/context'
|
|
25
|
+
import { register as records } from '@/commands/records'
|
|
25
26
|
import { PROJECT_ROOT } from '@/exec'
|
|
26
27
|
|
|
27
28
|
const GREY = '\x1b[0;90m'
|
|
@@ -53,6 +54,7 @@ function showHelp(): void {
|
|
|
53
54
|
`${GREY}│${NC} tasks [cmd] ${GREY}# Task board commands (archive)${NC}`,
|
|
54
55
|
`${GREY}│${NC} comments [cmd] ${GREY}# Measure comment density and trend (scan)${NC}`,
|
|
55
56
|
`${GREY}│${NC} context [cmd] ${GREY}# Report context folder health (audit)${NC}`,
|
|
57
|
+
`${GREY}│${NC} records [cmd] ${GREY}# Check session records under .claude/ (validate)${NC}`,
|
|
56
58
|
`${GREY}│${NC}`,
|
|
57
59
|
`${GREY}│${NC} ${WHITE}Sandbox:${NC}`,
|
|
58
60
|
`${GREY}│${NC} aitk sandbox ${GREY}# Interactive scenario picker${NC}`,
|
|
@@ -85,6 +87,7 @@ function showHelp(): void {
|
|
|
85
87
|
`${GREY}│${NC} aitk tasks archive --pull-request 673 --json`,
|
|
86
88
|
`${GREY}│${NC} aitk comments scan src --json`,
|
|
87
89
|
`${GREY}│${NC} aitk context audit --json`,
|
|
90
|
+
`${GREY}│${NC} aitk records validate plans`,
|
|
88
91
|
`${GREY}└${NC}`,
|
|
89
92
|
]
|
|
90
93
|
console.log(lines.join('\n'))
|
|
@@ -138,5 +141,6 @@ transcripts(program)
|
|
|
138
141
|
tasks(program)
|
|
139
142
|
comments(program)
|
|
140
143
|
context(program)
|
|
144
|
+
records(program)
|
|
141
145
|
|
|
142
146
|
program.parse()
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { $ } from 'bun'
|
|
2
|
+
import type { Command } from 'commander'
|
|
3
|
+
import {
|
|
4
|
+
type Finding,
|
|
5
|
+
isRecordKind,
|
|
6
|
+
RECORD_KINDS,
|
|
7
|
+
type ValidateOutcome,
|
|
8
|
+
validateRecords,
|
|
9
|
+
} from '@/records/validate'
|
|
10
|
+
import { intro, logError, logInfo, logStep, logWarn, outro } from '@/ui'
|
|
11
|
+
|
|
12
|
+
/** Returned when a record carries a finding, which is the gating result. */
|
|
13
|
+
const EXIT_FINDINGS = 2
|
|
14
|
+
|
|
15
|
+
interface ValidateCommandOptions {
|
|
16
|
+
readonly json?: boolean
|
|
17
|
+
readonly root?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The session-record folders are shared scratch at the main worktree root, and
|
|
22
|
+
* `git worktree list` puts that root first. Trusting the working directory would
|
|
23
|
+
* validate a linked worktree's empty folder and report it clean.
|
|
24
|
+
*/
|
|
25
|
+
async function mainWorktreeRoot(): Promise<string> {
|
|
26
|
+
const result = await $`git worktree list --porcelain`.quiet().nothrow()
|
|
27
|
+
if (result.exitCode !== 0) return process.cwd()
|
|
28
|
+
|
|
29
|
+
const line = result.stdout
|
|
30
|
+
.toString()
|
|
31
|
+
.split('\n')
|
|
32
|
+
.find((entry) => entry.startsWith('worktree '))
|
|
33
|
+
|
|
34
|
+
return line ? line.slice('worktree '.length).trim() : process.cwd()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function register(program: Command): void {
|
|
38
|
+
const records = program
|
|
39
|
+
.command('records')
|
|
40
|
+
.description('Check the gitignored session records under .claude/')
|
|
41
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
42
|
+
|
|
43
|
+
records
|
|
44
|
+
.command('validate')
|
|
45
|
+
.description('Report where a record and the standard governing it disagree')
|
|
46
|
+
.argument('<kind>', `Record folder: ${RECORD_KINDS.join(', ')}`)
|
|
47
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
48
|
+
.option('--json', 'Add a machine-readable record on stdout')
|
|
49
|
+
.option('--root <path>', 'Project root, defaulting to the main worktree')
|
|
50
|
+
.addHelpText(
|
|
51
|
+
'after',
|
|
52
|
+
[
|
|
53
|
+
'',
|
|
54
|
+
'Checks:',
|
|
55
|
+
' plans filename, required sections, and the suggested-and-answer contract',
|
|
56
|
+
' groundwork README and current-state files, numbering, dating, and a half-closed track',
|
|
57
|
+
' intake overview file, numbering, dating, and the four bullets every item carries',
|
|
58
|
+
' memory filename and type prefix, frontmatter, and the body shape each type carries',
|
|
59
|
+
'',
|
|
60
|
+
'Exit codes:',
|
|
61
|
+
' 0 every check passed',
|
|
62
|
+
' 1 refused, with the reason on stderr or in the JSON record',
|
|
63
|
+
' 2 at least one record carries a finding',
|
|
64
|
+
'',
|
|
65
|
+
'It reports and never writes. Each folder is per-machine scratch with no',
|
|
66
|
+
'history behind it, so a session fixes the record the report names.',
|
|
67
|
+
'',
|
|
68
|
+
'Examples:',
|
|
69
|
+
' aitk records validate plans',
|
|
70
|
+
' aitk records validate memory',
|
|
71
|
+
' aitk records validate intake --json',
|
|
72
|
+
'',
|
|
73
|
+
].join('\n'),
|
|
74
|
+
)
|
|
75
|
+
.action(async (kind: string, opts: ValidateCommandOptions) => {
|
|
76
|
+
process.exitCode = await runValidate(kind, opts)
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function runValidate(
|
|
81
|
+
kind: string,
|
|
82
|
+
opts: ValidateCommandOptions,
|
|
83
|
+
): Promise<number> {
|
|
84
|
+
const emitJson = opts.json ?? false
|
|
85
|
+
|
|
86
|
+
if (!isRecordKind(kind)) {
|
|
87
|
+
return report(
|
|
88
|
+
{
|
|
89
|
+
ok: false,
|
|
90
|
+
reason: 'unknown-kind',
|
|
91
|
+
message: `Not a record kind: ${kind}. Expected one of: ${RECORD_KINDS.join(', ')}.`,
|
|
92
|
+
},
|
|
93
|
+
emitJson,
|
|
94
|
+
process.cwd(),
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const root = opts.root ?? (await mainWorktreeRoot())
|
|
99
|
+
|
|
100
|
+
return report(await validateRecords(root, kind), emitJson, root)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function report(
|
|
104
|
+
outcome: ValidateOutcome,
|
|
105
|
+
emitJson: boolean,
|
|
106
|
+
root: string,
|
|
107
|
+
): number {
|
|
108
|
+
if (!outcome.ok) {
|
|
109
|
+
// The framed branch below already reaches stderr through logError, so the
|
|
110
|
+
// bare write is what keeps the JSON mode from reporting the reason on
|
|
111
|
+
// stdout alone.
|
|
112
|
+
if (emitJson) {
|
|
113
|
+
process.stderr.write(`${outcome.message}\n`)
|
|
114
|
+
process.stdout.write(
|
|
115
|
+
`${JSON.stringify({
|
|
116
|
+
ok: false,
|
|
117
|
+
reason: outcome.reason,
|
|
118
|
+
message: outcome.message,
|
|
119
|
+
})}\n`,
|
|
120
|
+
)
|
|
121
|
+
return 1
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
intro('aitk records validate')
|
|
125
|
+
logStep('Refused')
|
|
126
|
+
logError(outcome.message)
|
|
127
|
+
outro()
|
|
128
|
+
return 1
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (emitJson) {
|
|
132
|
+
process.stdout.write(
|
|
133
|
+
`${JSON.stringify({
|
|
134
|
+
ok: true,
|
|
135
|
+
root,
|
|
136
|
+
kind: outcome.kind,
|
|
137
|
+
records: outcome.records,
|
|
138
|
+
findings: outcome.findings,
|
|
139
|
+
})}\n`,
|
|
140
|
+
)
|
|
141
|
+
} else {
|
|
142
|
+
intro('aitk records validate')
|
|
143
|
+
logStep(outcome.kind)
|
|
144
|
+
logInfo(`${outcome.records} record(s) read`)
|
|
145
|
+
|
|
146
|
+
logStep(outcome.findings.length === 0 ? 'Clean' : 'Findings')
|
|
147
|
+
if (outcome.findings.length === 0) {
|
|
148
|
+
logInfo('every record matches the shape its standard fixes')
|
|
149
|
+
} else {
|
|
150
|
+
for (const found of outcome.findings) logWarn(describe(found))
|
|
151
|
+
}
|
|
152
|
+
outro()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return outcome.findings.length > 0 ? EXIT_FINDINGS : 0
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function describe(found: Finding): string {
|
|
159
|
+
const scope = found.record === found.subject ? '' : `${found.record}: `
|
|
160
|
+
return `${scope}${found.subject} ${found.message}`
|
|
161
|
+
}
|