@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.
@@ -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.58.0",
4
+ "version": "0.60.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -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. `CLAUDE.md` owns why a shipped plan is archived rather than removed.
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
- Construct the plan with these sections:
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
- - **Summary:** three to five one-line bullets covering the goal, the main deliverables, and the key trade-off or decision. Aimed at humans scanning the plan, not agents executing it. Full mode only.
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
- Prefer `None identified.` over low-signal fillers. A small feature should produce a short plan, not a padded one. Small mode skips the summary since the plan is already short enough to scan in full.
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
- - When the answer hinges on the user's preference or their intent is unclear, write `- Suggested: needs your call, <why>` rather than fabricating a technical default.
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
- File format:
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
- - <durable rule the work must respect>
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
- - `CLAUDE.md`: Memory section rules, including save thresholds and file format overrides
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 into four types:
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
- Apply the save threshold: a feedback memory only fires on explicit user correction, or on a pattern that repeated twice in the session. First-occurrence slips are noise. Project, user, and reference memories fire on first disclosure.
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
- For each new memory, write to `.claude/memory/<type>-<slug>.md` with this frontmatter:
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
- ```markdown
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. `.claude/memory/` is gitignored with no history behind it, and a first pass over a folder this size is a bulk judgment with no undo. A plan and a task both archive rather than delete for the same reason, and this costs one `mv` against an unrecoverable wrong call. 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 it is cheap rather than free.
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
 
@@ -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` |
@@ -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`.
@@ -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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "0.58.0",
4
+ "version": "0.60.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
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
+ }