@rasensio/aidlc-content 1.50.0 → 1.52.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/capabilities/claude-code.yaml +7 -0
- package/dist/explain.d.ts +30 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +56 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +70 -0
- package/docs/cost-tracking.md +188 -0
- package/docs/hygiene-and-compliance.md +135 -0
- package/docs/index.yaml +115 -0
- package/docs/knowledge-graph.md +121 -0
- package/docs/lifecycle.md +198 -0
- package/docs/metrics.md +67 -0
- package/docs/reviews-and-guidance.md +144 -0
- package/docs/roadmap.md +347 -0
- package/docs/skills-and-platforms.md +133 -0
- package/docs/state-and-concurrency.md +75 -0
- package/package.json +2 -1
- package/resources/rules/task-agents.md +55 -0
- package/skills/00-overview.md +3 -3
- package/skills/30-design.md +3 -0
- package/skills/40-implementation.md +3 -0
|
@@ -25,3 +25,10 @@ capabilities:
|
|
|
25
25
|
the problem space, identify constraints, and outline the approach before
|
|
26
26
|
committing to a structure. Exit plan mode only when you begin writing the
|
|
27
27
|
artifact.
|
|
28
|
+
subagents:
|
|
29
|
+
phases: [implementation]
|
|
30
|
+
activation: >-
|
|
31
|
+
Sub-agents are available on this platform: the Agent tool starts an agent
|
|
32
|
+
with a fresh context of its own. When a tasks.md is being implemented, one
|
|
33
|
+
agent per task may be offered, as .aidlc/resources/rules/task-agents.md
|
|
34
|
+
describes.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DocTopic } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Where the explanation pages live. Resolved from this module's own location, so
|
|
4
|
+
* in an installed project it points inside the installed package — the pages
|
|
5
|
+
* are read where npm put them and never copied out
|
|
6
|
+
* (docs-unreachable-from-installed-projects/AC-3).
|
|
7
|
+
*/
|
|
8
|
+
export declare const DOCS_DIR: string;
|
|
9
|
+
/**
|
|
10
|
+
* Every topic page the manifest declares, in manifest order.
|
|
11
|
+
*
|
|
12
|
+
* Throws when the manifest is missing or has no `entries` list rather than
|
|
13
|
+
* returning nothing: an absent manifest and an empty one must not look alike,
|
|
14
|
+
* or a packaging mistake reads as "no topics" instead of failing.
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadDocTopics(): DocTopic[];
|
|
17
|
+
/**
|
|
18
|
+
* Resolve what a reader typed to a topic: its id, or a top-level command a page
|
|
19
|
+
* claims. Ids are matched first, so a future command named like a topic cannot
|
|
20
|
+
* shadow the page.
|
|
21
|
+
*
|
|
22
|
+
* Anything containing a path separator or `..` is unknown before any lookup.
|
|
23
|
+
* The query is only ever compared with manifest values and never joined into a
|
|
24
|
+
* path, so this is belt and braces — but it keeps "is it a path?" from being a
|
|
25
|
+
* question anyone has to re-derive (docs-unreachable-from-installed-projects/AC-9).
|
|
26
|
+
*/
|
|
27
|
+
export declare function findDocTopic(query: string): DocTopic | undefined;
|
|
28
|
+
/** The page's Markdown, exactly as written. */
|
|
29
|
+
export declare function readDocPage(topic: DocTopic): string;
|
|
30
|
+
//# sourceMappingURL=explain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../src/explain.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,QAAgC,CAAC;AAWtD;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAgB1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAOhE;AAED,+CAA+C;AAC/C,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAEnD"}
|
package/dist/explain.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { resolve, dirname, join } from 'node:path';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { parse as parseYaml } from 'yaml';
|
|
5
|
+
const CONTENT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
6
|
+
/**
|
|
7
|
+
* Where the explanation pages live. Resolved from this module's own location, so
|
|
8
|
+
* in an installed project it points inside the installed package — the pages
|
|
9
|
+
* are read where npm put them and never copied out
|
|
10
|
+
* (docs-unreachable-from-installed-projects/AC-3).
|
|
11
|
+
*/
|
|
12
|
+
export const DOCS_DIR = resolve(CONTENT_ROOT, 'docs');
|
|
13
|
+
/**
|
|
14
|
+
* Every topic page the manifest declares, in manifest order.
|
|
15
|
+
*
|
|
16
|
+
* Throws when the manifest is missing or has no `entries` list rather than
|
|
17
|
+
* returning nothing: an absent manifest and an empty one must not look alike,
|
|
18
|
+
* or a packaging mistake reads as "no topics" instead of failing.
|
|
19
|
+
*/
|
|
20
|
+
export function loadDocTopics() {
|
|
21
|
+
const parsed = parseYaml(readFileSync(join(DOCS_DIR, 'index.yaml'), 'utf8'));
|
|
22
|
+
if (!parsed || !Array.isArray(parsed.entries)) {
|
|
23
|
+
throw new Error(`${join(DOCS_DIR, 'index.yaml')} did not parse into a manifest with an entries list`);
|
|
24
|
+
}
|
|
25
|
+
return parsed.entries
|
|
26
|
+
.filter((e) => e.kind === 'topic')
|
|
27
|
+
.map((e) => ({
|
|
28
|
+
id: String(e.id),
|
|
29
|
+
title: String(e.title),
|
|
30
|
+
file: String(e.file),
|
|
31
|
+
source: String(e.source),
|
|
32
|
+
commands: Array.isArray(e.commands) ? e.commands.map(String) : [],
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve what a reader typed to a topic: its id, or a top-level command a page
|
|
37
|
+
* claims. Ids are matched first, so a future command named like a topic cannot
|
|
38
|
+
* shadow the page.
|
|
39
|
+
*
|
|
40
|
+
* Anything containing a path separator or `..` is unknown before any lookup.
|
|
41
|
+
* The query is only ever compared with manifest values and never joined into a
|
|
42
|
+
* path, so this is belt and braces — but it keeps "is it a path?" from being a
|
|
43
|
+
* question anyone has to re-derive (docs-unreachable-from-installed-projects/AC-9).
|
|
44
|
+
*/
|
|
45
|
+
export function findDocTopic(query) {
|
|
46
|
+
const wanted = query.trim().toLowerCase();
|
|
47
|
+
if (wanted.length === 0 || /[/\\]|\.\./.test(wanted))
|
|
48
|
+
return undefined;
|
|
49
|
+
const topics = loadDocTopics();
|
|
50
|
+
return (topics.find((t) => t.id === wanted) ?? topics.find((t) => t.commands.includes(wanted)));
|
|
51
|
+
}
|
|
52
|
+
/** The page's Markdown, exactly as written. */
|
|
53
|
+
export function readDocPage(topic) {
|
|
54
|
+
return readFileSync(join(DOCS_DIR, topic.file), 'utf8');
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../src/explain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAG1C,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;AAWtD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CAEnE,CAAC;IACT,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,qDAAqD,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,MAAM,CAAC,OAAO;SAClB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC;SACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QACxB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;KAClE,CAAC,CAAC,CAAC;AACR,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IACvE,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,OAAO,CACL,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CACvF,CAAC;AACJ,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,WAAW,CAAC,KAAe;IACzC,OAAO,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,6 @@ export { loadRules } from './rules.js';
|
|
|
8
8
|
export { loadBoardWorkflow, BOARD_WORKFLOW_PATH } from './recipes.js';
|
|
9
9
|
export { loadTutorials, getTutorial, getSeries, parseTutorialSource } from './tutorials.js';
|
|
10
10
|
export { loadHowTos, getHowTo, parseHowToSource, HOWTO_SECTIONS } from './howto.js';
|
|
11
|
-
export
|
|
11
|
+
export { DOCS_DIR, loadDocTopics, findDocTopic, readDocPage } from './explain.js';
|
|
12
|
+
export type { Skill, WorkflowTemplate, GuidanceLayer, GlossaryTerm, CapabilityMap, QuestionRendering, PhaseDefinition, Rule, CodeSample, ContentReference, TutorialStep, Tutorial, WidgetKind, WidgetReference, HowTo, DocTopic } from './types.js';
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpF,YAAY,EAAE,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAClF,YAAY,EAAE,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,4 +8,5 @@ export { loadRules } from './rules.js';
|
|
|
8
8
|
export { loadBoardWorkflow, BOARD_WORKFLOW_PATH } from './recipes.js';
|
|
9
9
|
export { loadTutorials, getTutorial, getSeries, parseTutorialSource } from './tutorials.js';
|
|
10
10
|
export { loadHowTos, getHowTo, parseHowToSource, HOWTO_SECTIONS } from './howto.js';
|
|
11
|
+
export { DOCS_DIR, loadDocTopics, findDocTopic, readDocPage } from './explain.js';
|
|
11
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -151,4 +151,21 @@ export interface HowTo {
|
|
|
151
151
|
checkItWorked: string;
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* One explanation page: why a subsystem exists, what breaks without it, how to
|
|
156
|
+
* use it. Declared in `docs/index.yaml`, shipped in this package and read from
|
|
157
|
+
* it — never installed into a project, for the reason the glossary is not
|
|
158
|
+
* (docs-unreachable-from-installed-projects DD-1).
|
|
159
|
+
*/
|
|
160
|
+
export interface DocTopic {
|
|
161
|
+
/** Short slug, unique across the manifest; what `aidlc explain <topic>` takes. */
|
|
162
|
+
id: string;
|
|
163
|
+
title: string;
|
|
164
|
+
/** Filename under `docs/`. A manifest value, never user input. */
|
|
165
|
+
file: string;
|
|
166
|
+
/** Repo path the page is about. Informational once installed: the path is in this repo, not yours. */
|
|
167
|
+
source: string;
|
|
168
|
+
/** Top-level CLI commands the page covers; each also resolves to this page. */
|
|
169
|
+
commands: string[];
|
|
170
|
+
}
|
|
154
171
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# How AIDLC works
|
|
2
|
+
|
|
3
|
+
Nine pages explaining what this repo built — why each part exists, and how to use it.
|
|
4
|
+
|
|
5
|
+
These are for two readers with the same gap: a person trying to understand the system, and an
|
|
6
|
+
AI agent about to change it. If you are about to modify a subsystem, read its page first. The
|
|
7
|
+
reasons are the point: several of the designs here look arbitrary until you know which bug bought
|
|
8
|
+
them, and an agent that only knows the shape will simplify the reason away.
|
|
9
|
+
|
|
10
|
+
## The pages
|
|
11
|
+
|
|
12
|
+
| Page | What it covers |
|
|
13
|
+
|---|---|
|
|
14
|
+
| [lifecycle.md](lifecycle.md) | Instances, templates, scope, phases, gates, transitions |
|
|
15
|
+
| [state-and-concurrency.md](state-and-concurrency.md) | State files, claims, the registry, worktrees |
|
|
16
|
+
| [roadmap.md](roadmap.md) | Capture, triage, promote — statuses as directories |
|
|
17
|
+
| [reviews-and-guidance.md](reviews-and-guidance.md) | Adversarial review, guidance layers, context docs, the constitution |
|
|
18
|
+
| [cost-tracking.md](cost-tracking.md) | What a feature cost, and how the number is attributed |
|
|
19
|
+
| [metrics.md](metrics.md) | Change failure rate, lead times, true cost |
|
|
20
|
+
| [knowledge-graph.md](knowledge-graph.md) | Persistent codebase memory for agents |
|
|
21
|
+
| [skills-and-platforms.md](skills-and-platforms.md) | One canonical skill body, six platform packagings |
|
|
22
|
+
| [hygiene-and-compliance.md](hygiene-and-compliance.md) | `aidlc doctor`, compliance packs |
|
|
23
|
+
|
|
24
|
+
## What these pages are not
|
|
25
|
+
|
|
26
|
+
**Not the framework reference.** The website documents the framework itself — phases, skills,
|
|
27
|
+
templates, the CLI, the glossary. These pages document what was *built with* it.
|
|
28
|
+
|
|
29
|
+
**Not the release story.** `packages/website/content/blog/` holds a post per release, written as
|
|
30
|
+
narrative at the moment the work landed. Good for "what happened in v1.8.0", no use for "how does
|
|
31
|
+
cost tracking work now". These pages are the second thing.
|
|
32
|
+
|
|
33
|
+
**Not the knowledge graph.** That split is the one worth understanding before you add a page, and
|
|
34
|
+
it is stated in full at the top of [index.yaml](index.yaml). In short: the graph answers *where is
|
|
35
|
+
it and what shape is it*, terse and loaded into every prompt. These pages answer *why does it
|
|
36
|
+
exist, what breaks without it, how do I use it*, in prose, read on demand. A page that restates a
|
|
37
|
+
fact the graph already carries is the drift the split exists to prevent.
|
|
38
|
+
|
|
39
|
+
**Not vocabulary.** Framework terms have exactly one definition, in
|
|
40
|
+
`packages/content/glossary.yaml`, consumed by both the CLI and the website. These pages link to it
|
|
41
|
+
and never restate a definition — a third copy is a copy that goes wrong.
|
|
42
|
+
|
|
43
|
+
## Adding or changing a page
|
|
44
|
+
|
|
45
|
+
Every page is declared in [index.yaml](index.yaml), which is also where the two allowlists live.
|
|
46
|
+
Guards in `packages/cli/test/docs-topics.test.ts` check the structure: that declared pages exist
|
|
47
|
+
and undeclared ones do not, that every page carries its three required sections with content in
|
|
48
|
+
them, that every top-level CLI command is claimed by some page, and that every repo path a page
|
|
49
|
+
mentions still resolves. The command check reads the set from
|
|
50
|
+
`packages/website/src/data/cli-reference.json`, which the docs-drift gate keeps in step with the
|
|
51
|
+
shipped binary — so this sentence deliberately names no count. The one it used to name ("all 20")
|
|
52
|
+
was wrong by the next release.
|
|
53
|
+
|
|
54
|
+
That last check is the one you will meet. It exists because the knowledge graph rotted exactly
|
|
55
|
+
this way — 17 of its entries still describe a package deleted months ago — and structural rot is
|
|
56
|
+
the half a test can catch. When it fires you have two honest options: update the page, or, if you
|
|
57
|
+
are deliberately naming something that was removed, add it to `historical_paths:` with a reason.
|
|
58
|
+
Deleting the guard is not the third option.
|
|
59
|
+
|
|
60
|
+
Each topic page needs these three headings, matched literally:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
## Why this exists
|
|
64
|
+
## How to use it
|
|
65
|
+
## Where the code lives
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`## Why this exists` is the section the whole folder is for. It carries the reason and what breaks
|
|
69
|
+
without the thing. Anything that could equally have been read off the code belongs in the
|
|
70
|
+
knowledge graph instead.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Cost tracking
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
You cannot decide whether a feature was worth building if you do not know what it cost. With an AI
|
|
6
|
+
agent doing the work, the cost is tokens, and the tokens are spent in a session that has no idea which
|
|
7
|
+
feature it is working on.
|
|
8
|
+
|
|
9
|
+
Cost tracking closes that gap: it reads what the agent platform recorded about a session and attributes
|
|
10
|
+
it to a lifecycle instance, so `aidlc cost` can answer "what did this feature cost".
|
|
11
|
+
|
|
12
|
+
The number changed decisions here. Cost tracking's *own* cost was $153.65. Its **true** cost — adding
|
|
13
|
+
the two bugfixes it caused — was $219.78. Own cost understated it by a third, and hid the fact that
|
|
14
|
+
building a feature to watch the lifecycle is not a one-week spend but a tax on every future instance.
|
|
15
|
+
That is the whole argument for the module existing, and it is also the argument for reading `aidlc
|
|
16
|
+
metrics` rather than `aidlc cost`.
|
|
17
|
+
|
|
18
|
+
> **Those two figures are as-metered, and the meter over-reported.** Until 2026-09-10 the
|
|
19
|
+
> claude-code provider counted usage once per transcript *line*, and Claude Code writes one line per
|
|
20
|
+
> content block with the response's `usage` repeated on each — so one API call was charged up to
|
|
21
|
+
> seven times. Measured over 53 transcripts: **1.848x**. Every absolute dollar figure published
|
|
22
|
+
> before that date, including the two above and the warning in `CLAUDE.md` built on them, runs
|
|
23
|
+
> roughly 1.85x high. The *ratio* between them survives, because the duplicate rate was stable day
|
|
24
|
+
> to day, so "own cost understated true cost by a third" still holds. `aidlc cost dedupe` re-derives
|
|
25
|
+
> history; it has not been applied, so figures in older retrospectives and blog posts are still the
|
|
26
|
+
> as-metered ones.
|
|
27
|
+
|
|
28
|
+
## The bill is context, not thinking
|
|
29
|
+
|
|
30
|
+
Measured over 20,920 attributed records: **91% of spend is cache traffic** — re-reading the same
|
|
31
|
+
conversation prefix, turn after turn. Output is 13%. Assistant prose and reasoning together are 2.4%
|
|
32
|
+
of the bytes in context.
|
|
33
|
+
|
|
34
|
+
The prefix only grows, and nothing used to reset it. Average context per unit of work by phase:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
ideation 80k testing 344k
|
|
38
|
+
requirements 164k deployment 395k
|
|
39
|
+
design 173k maintenance 791k
|
|
40
|
+
implementation 259k complete 76k <- the only phase that starts fresh
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`complete` is the proof. It does real work at 76k because it runs in its own session; deployment does
|
|
44
|
+
comparable work at 395k because it inherits every phase before it. That is why **deployment is the
|
|
45
|
+
most expensive phase in the lifecycle, ahead of implementation** — not because it does more, but
|
|
46
|
+
because it runs last.
|
|
47
|
+
|
|
48
|
+
So a phase boundary is a free context reset. `aidlc cost context` reports what this session is
|
|
49
|
+
carrying against a budget (`cost.context_budget_tokens`, default 150,000), and the phase skills name
|
|
50
|
+
exactly what to re-read when a phase starts fresh. The canonical statement of the rule lives in the
|
|
51
|
+
`aidlc-overview` skill; every phase skill points at it rather than restating it.
|
|
52
|
+
|
|
53
|
+
**It is a report and never a gate.** Deliberately: this project shipped 11 features and 7 bugfixes,
|
|
54
|
+
five of the six traceable ones caused by meta-features that each added a gate every future instance
|
|
55
|
+
had to pass. The budget's whole force is that somebody reads it out at the right moment — the same
|
|
56
|
+
force the constitution has, and for the same reason. Do not turn it into a transition check.
|
|
57
|
+
|
|
58
|
+
**What breaks without it:** every estimate is a guess, and meta-work looks cheap because its downstream
|
|
59
|
+
cost lands on somebody else's instance.
|
|
60
|
+
|
|
61
|
+
## How to use it
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
aidlc cost
|
|
65
|
+
aidlc cost my-thing
|
|
66
|
+
aidlc cost --by-template
|
|
67
|
+
aidlc cost context # resident context vs budget — reads the transcript, not the ledger
|
|
68
|
+
aidlc cost dedupe # plan the correction for pre-2026-09-10 double counting
|
|
69
|
+
aidlc cost hooks install
|
|
70
|
+
aidlc cost sync
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`aidlc cost` reports the *driver* alongside the total, because `$578.45` on its own gives a reader
|
|
74
|
+
nothing to act on: cache-read share of spend, average context per response, and cost per response.
|
|
75
|
+
Entries captured before 2026-09-10 carry no response count, so their per-response figures read
|
|
76
|
+
`unmeasured` rather than zero.
|
|
77
|
+
|
|
78
|
+
Two rules keep the per-response figures honest (2026-09-14, cost-driver-integrity). First, the
|
|
79
|
+
averages divide sums taken over exactly the entries that carry a response count — never the whole
|
|
80
|
+
ledger over a subset count, and each ratio narrows further to the entries that can supply *its*
|
|
81
|
+
numerator (a credit-unit entry has a response count and no token count, so counting it against
|
|
82
|
+
context reported `avg context: 0`). The original shipped shape divided all-entry token sums by the
|
|
83
|
+
handful of counted entries and printed a 21.5M-token "average context" as if it were a measurement.
|
|
84
|
+
When the counted entries are only part of the ledger the derived figures carry a `~`, which marks
|
|
85
|
+
them as computed over part of the ledger — not as a floor. `responses` itself *is* a floor, and
|
|
86
|
+
carries `~` for that reason; a subset average can sit either side of the whole-ledger one.
|
|
87
|
+
Second, the response count is a fact the *provider* owns: `claude-code.ts` groups
|
|
88
|
+
by `message.id`, so each segment it emits is exactly one response and says so; both writers — `sync`
|
|
89
|
+
for reconciled entries and `recorder.ts` for metered buckets — *sum what the segments declare* rather
|
|
90
|
+
than counting segments themselves. A provider whose records are not response-shaped (kiro, codex)
|
|
91
|
+
sets nothing, and absence stays "unknown", never a fabricated count. The recorder used to add 1 per
|
|
92
|
+
segment regardless of provider, which put a tally of kiro usage records where a response count
|
|
93
|
+
belongs — on the highest-precedence entries, where it outranked the honest absence beside it.
|
|
94
|
+
|
|
95
|
+
The default report also prices the **unattributed pool** (`.aidlc/cost/unattributed.ndjson`) on its
|
|
96
|
+
last line, with the same read-time precedence weighting instances get. On this repo that pool was
|
|
97
|
+
~44% of total spend while the only surface for it printed a bare entry count. `aidlc cost
|
|
98
|
+
--unattributed` breaks the pool down by fidelity and model; `aidlc cost sync` drains what claim
|
|
99
|
+
windows cover.
|
|
100
|
+
|
|
101
|
+
`aidlc cost context` reads the transcript rather than the ledger on purpose. The question is asked at
|
|
102
|
+
a phase boundary, which is exactly when no capture has run recently and the ledger is stale — and it
|
|
103
|
+
has to answer on a machine where no hook was ever installed.
|
|
104
|
+
|
|
105
|
+
Capture is passive. A hook fires on each agent turn, and the recorder attributes the usage. Without the
|
|
106
|
+
hook installed there is no data, and — the failure that produced two bugfixes — a broken hook looked
|
|
107
|
+
exactly like an idle one. Hence `capture.log` and `lastCaptureError`, so "captured nothing" can be told
|
|
108
|
+
apart from "was never asked".
|
|
109
|
+
|
|
110
|
+
**How attribution works**, because this is where the surprises are. A usage record gets an instance and
|
|
111
|
+
a phase by intersecting two windows:
|
|
112
|
+
|
|
113
|
+
- **Claim intervals** — when was this instance claimed, by which session. From `sessions.ndjson`.
|
|
114
|
+
- **Phase windows** — derived from `transitions.log`, so a record's timestamp resolves to whichever
|
|
115
|
+
phase was current at that moment. Re-entering a phase sums rather than replaces.
|
|
116
|
+
|
|
117
|
+
Two consequences follow, and both cost real money before they were understood:
|
|
118
|
+
|
|
119
|
+
**Concurrent claims destroy attribution.** If two instances were claimed at once in one checkout, a
|
|
120
|
+
record falls inside both windows and the recorder reports `ambiguous` rather than guessing. On
|
|
121
|
+
2026-08-26 both live instances lost their cost data entirely this way. Use a worktree for concurrent
|
|
122
|
+
work — see [state-and-concurrency.md](state-and-concurrency.md).
|
|
123
|
+
|
|
124
|
+
**A finished instance stops collecting.** A claim nobody released stays open for the claim timeout,
|
|
125
|
+
so for up to an hour after an instance closed, capture went on crediting it with whatever its session
|
|
126
|
+
did next, and appending `renew` events to its `sessions.ndjson`. Every one of those writes landed in a
|
|
127
|
+
tracked file of a finished instance, in the checkout releases run from, and `release.sh` refused the
|
|
128
|
+
dirty tree. Now `cost/completion.ts` ends every claim interval at the instance's completion moment
|
|
129
|
+
(`completionInfo`'s answer, so maintenance-terminal templates count from maintenance entry). It does
|
|
130
|
+
this before capture and `cost sync` decide anything. Usage from before the close still attributes.
|
|
131
|
+
Usage after it goes to the unattributed pool. The cut is on the *interval* rather than on each write,
|
|
132
|
+
because capture, reconcile, backfill and recover all decide from intervals, so one cut keeps all four
|
|
133
|
+
in agreement.
|
|
134
|
+
|
|
135
|
+
The pool itself still changes whenever a session with no claim ends, and no recorder change can stop
|
|
136
|
+
that without dropping records. So `release.sh` commits churn that is *only* cost ledger files
|
|
137
|
+
(`scripts/commit-cost-ledgers.sh --preflight`) and still refuses anything else.
|
|
138
|
+
|
|
139
|
+
**Null is not zero.** When there is no ledger data, the report says null. It never zero-fills, because
|
|
140
|
+
a zero reads as "this was free" and a null reads as "we do not know", and those are different claims.
|
|
141
|
+
Anything that aggregates costs nulls the total if any constituent is null, for the same reason.
|
|
142
|
+
|
|
143
|
+
Rates come from a bundled table, refreshed from LiteLLM and cacheable, with model-id normalization so a
|
|
144
|
+
provider-prefixed id matches its bundled rate. An override file exists for models the table does not
|
|
145
|
+
know.
|
|
146
|
+
|
|
147
|
+
## Where the code lives
|
|
148
|
+
|
|
149
|
+
Everything is under `packages/cli/src/cost`:
|
|
150
|
+
|
|
151
|
+
- `ledger.ts` — append-only NDJSON with a closed schema, cursor-based reads, and read-time fidelity
|
|
152
|
+
precedence (a more precise later record reduces an earlier estimate pro-rata rather than
|
|
153
|
+
double-counting the overlap).
|
|
154
|
+
- `sessions.ts` — session events and claim intervals.
|
|
155
|
+
- `completion.ts` — ends claim intervals at the instance's completion moment.
|
|
156
|
+
- `providers` — the platform seam. Each provider parses one agent platform's session records.
|
|
157
|
+
`resolveSource` returns a `ResolveResult` of `ok` or `reason`, deliberately not `null`: a null could
|
|
158
|
+
not distinguish "no transcript on disk" from "transcript found but no matching turns", and telling
|
|
159
|
+
those apart is what a bugfix bought. Do not simplify it back.
|
|
160
|
+
`claude-code.ts` groups usage by `message.id` and **collapses** repeats rather than summing them —
|
|
161
|
+
correct only because the repeats are byte-identical, which was measured (7,935 repeats, zero
|
|
162
|
+
differing) rather than assumed. Two things there look removable and are not: a group's cursor
|
|
163
|
+
advances to its *last* line (keeping the first re-charges the tail on the next capture), and the
|
|
164
|
+
final segment's cursor is *assigned* from the last usage-bearing uuid rather than inherited from
|
|
165
|
+
array order, because array order is only right while response groups stay contiguous and the
|
|
166
|
+
transcript format is internal to Claude Code. If contiguity broke, inferring would send the cursor
|
|
167
|
+
backwards and re-charge a span of transcript.
|
|
168
|
+
- `context.ts` — resident-context reading for `aidlc cost context`. Reads token counts only; never
|
|
169
|
+
copies transcript content, so nothing here can carry a prompt or a secret into a report.
|
|
170
|
+
- `dedupe-history.ts` — the corrective pass over existing ledgers. It distinguishes a *proven
|
|
171
|
+
duplicate* (transcript resolved, but the deduped read emits nothing at that entry's instant, so the
|
|
172
|
+
charge is carried elsewhere) from a *transcript that is gone* (an honest unknown, left
|
|
173
|
+
byte-identical and never scaled by the measured factor). Those two justify opposite actions, which
|
|
174
|
+
is why they are separate fields and not one "unresolvable" count.
|
|
175
|
+
- `recorder.ts` — attribution, the per-session lock, and `capture.log`. `phaseWindows` is the
|
|
176
|
+
phase-attribution seam, called from four places.
|
|
177
|
+
- `rates.ts`, `rates-bundled.ts` — pricing and model-id normalization.
|
|
178
|
+
- `report.ts`, `sync.ts` — per-instance and by-template reports; reconciliation, backfill, and
|
|
179
|
+
recovery of unattributed records.
|
|
180
|
+
- `hooks` — installers that merge into the platform's own settings file rather than replacing it.
|
|
181
|
+
- `packages/cli/src/commands/cost.ts` — the command surface.
|
|
182
|
+
|
|
183
|
+
The one design rule to keep: any figure that becomes money needs a duplicate-cursor audit across every
|
|
184
|
+
path that writes it. Two of the bugfixes here were double-counting — and then the meter fix of
|
|
185
|
+
2026-09-10 was a third, which had been sitting in the *provider* the whole time while both earlier
|
|
186
|
+
audits looked at the recorder and the ledger. The rule was right; the audit had never been run one
|
|
187
|
+
level further out. When auditing for duplicates, start at the thing that produces records, not at the
|
|
188
|
+
thing that stores them.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Repo hygiene and compliance
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
Running a lifecycle in a repository leaves debris. Cost ledgers that must never be committed. Claim
|
|
6
|
+
files from sessions that died. Ephemera from an instance that finished three weeks ago. Nested
|
|
7
|
+
`.gitignore` files that quietly stopped covering what they were written to cover.
|
|
8
|
+
|
|
9
|
+
None of it breaks anything on the day it appears, which is exactly why it accumulates. `aidlc doctor`
|
|
10
|
+
checks for it and repairs what is safely repairable.
|
|
11
|
+
|
|
12
|
+
**Compliance packs** answer a different question: an auditor's. A pack declares controls — what each one
|
|
13
|
+
requires, where to look for evidence, and what counts as failing — and the framework checks them against
|
|
14
|
+
the artifacts the lifecycle already produces. The lifecycle generates requirements, designs, reviews and
|
|
15
|
+
deployment records as a matter of course, so most of the evidence exists already; the pack just knows
|
|
16
|
+
where to look.
|
|
17
|
+
|
|
18
|
+
The design decision that makes packs usable is that **control findings are advisory by default**. They
|
|
19
|
+
are written to `<artifact>.compliance.md`, a separate file, and the phase transition gate reads only
|
|
20
|
+
`*.review.md`. Keeping the two streams apart is what stops a compliance pack from becoming an
|
|
21
|
+
unreviewable blocker on every phase.
|
|
22
|
+
|
|
23
|
+
**What breaks without them:** a cost file gets committed with a session id in it, a stale claim blocks
|
|
24
|
+
the one session entitled to proceed, and an audit becomes an archaeology project.
|
|
25
|
+
|
|
26
|
+
### Why a pack registration says `pack:` and not just `always_pointer:`
|
|
27
|
+
|
|
28
|
+
A guidance layer installed by a compliance pack carries two keys in `.aidlc/guidance/index.yaml`, and
|
|
29
|
+
they are easy to mistake for one thing:
|
|
30
|
+
|
|
31
|
+
- `always_pointer: true` is a **rendering** instruction. It tells the skill compiler to emit a pointer
|
|
32
|
+
to the layer file rather than inlining its text, whatever the layer's size. Pack layers must set it,
|
|
33
|
+
because inlining pack control text into an emitted skill is forbidden outright.
|
|
34
|
+
- `pack: <id>` is **provenance**. It records which pack installed the entry.
|
|
35
|
+
|
|
36
|
+
The keys always travel together on a pack layer, which makes it tempting to read the first as proof of
|
|
37
|
+
the second. `aidlc doctor` did exactly that until v1.25.0, and the consequence was not cosmetic. A
|
|
38
|
+
project may legitimately set `always_pointer` on a layer it wrote itself — that is how you ask for a
|
|
39
|
+
long layer to be linked rather than pasted into every skill — and doctor read those entries as
|
|
40
|
+
registrations belonging to an uninstalled pack. It reported them on every run, could never clear
|
|
41
|
+
(no pack will ever claim a file the project authored), and dropped the registration under `--yes`. The
|
|
42
|
+
file stayed on disk, but the registration is what makes a layer reach an agent, so the layer silently
|
|
43
|
+
stopped being delivered.
|
|
44
|
+
|
|
45
|
+
The rule now: **an entry with no `pack` key is the project's or the framework's, and doctor leaves it
|
|
46
|
+
alone.** Entries whose provenance cannot be proven default to project-authored, because the two errors
|
|
47
|
+
cost very different amounts. Wrongly dropping a registration stops delivering guidance an agent was
|
|
48
|
+
instructed to always read, and nothing announces it. Wrongly keeping one leaves a stale pointer, which
|
|
49
|
+
is visible in the index and cheap to remove. Where the evidence is missing, take the reversible error.
|
|
50
|
+
|
|
51
|
+
The same reasoning governs a broken config. `readComplianceConfig` deliberately degrades an
|
|
52
|
+
unparseable `compliance:` section to "no packs installed" so that a hand-mangled config cannot break
|
|
53
|
+
unrelated commands — right for every consumer asking what governs the project, and dangerous for one
|
|
54
|
+
that acts on a record's *absence*. So the two provenance-dependent migrations consult
|
|
55
|
+
`complianceSectionState()` first and decline to run at all when the section is present but
|
|
56
|
+
unreadable, rather than concluding a live pack was uninstalled.
|
|
57
|
+
|
|
58
|
+
## How to use it
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
aidlc doctor
|
|
62
|
+
aidlc doctor --fix
|
|
63
|
+
aidlc compliance list
|
|
64
|
+
aidlc compliance check
|
|
65
|
+
aidlc compliance report
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`doctor` reports; `--fix` repairs. Run it after anything structural — a new top-level directory, a
|
|
69
|
+
worktree removed, an instance abandoned.
|
|
70
|
+
|
|
71
|
+
Some checks report and repair nothing, on purpose. A `report`-tier migration states a condition whose
|
|
72
|
+
only remedy is a decision, and it never touches `doctor`'s exit code, so `--fix` and `--yes` cannot
|
|
73
|
+
make it write. Four conditions qualify today: an out-of-date compliance pack, a configured roadmap
|
|
74
|
+
projection with no `gh` on `PATH`, `knowledge-stale-sources`, and `roadmap-item-summary`.
|
|
75
|
+
|
|
76
|
+
**`roadmap-item-summary`** names every `inbox/` item with no complete six-line executive summary, and
|
|
77
|
+
the labels each one is missing. Two paths write into the inbox — the capture flow, which has always
|
|
78
|
+
required the summary, and an instance writing an item mid-phase, which until recently required
|
|
79
|
+
nothing — and 13 of the 18 items in one project's inbox had no summary at all, so triaging it meant
|
|
80
|
+
re-deriving thirteen cases from scratch. It is report tier because the only remedy is writing six
|
|
81
|
+
lines of judgement about somebody else's idea: an invented case reads exactly like a real one, and a
|
|
82
|
+
no-op repair at confirm tier would make `doctor` exit 1 forever over one thin item. It looks at
|
|
83
|
+
`inbox/` only — the other statuses have already been decided about — and skips items whose
|
|
84
|
+
frontmatter will not parse, because `roadmap-item-titles` already reports those by name.
|
|
85
|
+
|
|
86
|
+
**`knowledge-stale-sources`** resolves every knowledge-graph entity's `source` against the disk, and
|
|
87
|
+
every relationship endpoint against the entity set, and names what does not resolve. It is the check
|
|
88
|
+
that was missing when 15 graph entities described `packages/web` for 17 days after the website cutover
|
|
89
|
+
deleted it. It is report tier because 12 of those 15 wanted deleting and 3 wanted their path corrected
|
|
90
|
+
to a different manifest, and the data cannot tell you which — so it names the entries and leaves the
|
|
91
|
+
edit to you. See [The knowledge graph](knowledge-graph.md).
|
|
92
|
+
|
|
93
|
+
Two properties it holds deliberately, and which are easy to break by extending it. It **spawns
|
|
94
|
+
nothing** — the graph's own `KNOWLEDGE_DIR` constant had to move out of `engine.ts` because that
|
|
95
|
+
module imports `SqliteIndex`, which would have put `node:sqlite`'s experimental warning on the
|
|
96
|
+
default path of every `doctor` run. And a `source` that is absolute, or that escapes the project root
|
|
97
|
+
once resolved, is reported as a defect in the entry and **never passed to a filesystem call**: a
|
|
98
|
+
tracked YAML file is not a trusted one, and it must not be able to make `doctor` probe paths outside
|
|
99
|
+
the repo.
|
|
100
|
+
|
|
101
|
+
`compliance remove` and `upgrade` follow the same rule for the pack record in `.aidlc/config.yaml`.
|
|
102
|
+
They delete only files whose recorded path stays inside `.aidlc/guidance/`, and only when that
|
|
103
|
+
entry's own recorded hash matches what is on disk. The record is tracked, so a cloned repo writes
|
|
104
|
+
it. The hash used to be looked up by file *basename*, so a second entry named `victim.md` could
|
|
105
|
+
vouch for `../../victim.md` and get a file outside the project deleted (security-review-findings).
|
|
106
|
+
|
|
107
|
+
`compliance list` shows packs available and installed. `soc2` ships with the framework. `check` returns
|
|
108
|
+
the controls applying to the current phase, each with its requirement, where to look, and its fail
|
|
109
|
+
condition.
|
|
110
|
+
|
|
111
|
+
Two rules for anyone acting on a `check`:
|
|
112
|
+
|
|
113
|
+
**Apply the fail conditions as written.** They belong to the pack. Reinterpreting one turns a stated
|
|
114
|
+
control into your opinion, which is the one thing an audit trail cannot absorb.
|
|
115
|
+
|
|
116
|
+
**Never record a control as cleared unless you actually examined it.** Every applicable control must
|
|
117
|
+
appear either as a finding carrying its id, or in `## Controls Checked` as `<id> — no issues`. An
|
|
118
|
+
unchecked control reported as covered is worse than no check at all — it converts an unknown into a
|
|
119
|
+
false assurance, and the next reader has no way to tell.
|
|
120
|
+
|
|
121
|
+
There is a related trap in `doctor`'s own history worth knowing if you extend it: a migration that
|
|
122
|
+
reports pending work and a migration that has already drained its directory can look identical, and
|
|
123
|
+
removal that only runs when the check reports pending work never runs at all. Run a new migration twice
|
|
124
|
+
and read what it says the second time. Steady-state bugs are invisible on the first run by definition.
|
|
125
|
+
|
|
126
|
+
## Where the code lives
|
|
127
|
+
|
|
128
|
+
- `packages/cli/src/doctor` — the checks and repairs. `migrations` holds the one-off repairs, including
|
|
129
|
+
the nested-`.gitignore` coverage fix.
|
|
130
|
+
- `packages/cli/src/compliance` — pack loading, control evaluation, and the evidence report.
|
|
131
|
+
- `packages/content/packs` — the shipped packs; `soc2` is the only one today.
|
|
132
|
+
- `packages/content/skills/80-review.md` — where the compliance findings stream is specified, including
|
|
133
|
+
the two-places rule for every applicable control.
|
|
134
|
+
- `packages/cli/src/operational/redact.ts` — redaction used when repo state is turned into something
|
|
135
|
+
shareable, so a report does not carry what a ledger recorded.
|