@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.
@@ -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"}
@@ -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 type { Skill, WorkflowTemplate, GuidanceLayer, GlossaryTerm, CapabilityMap, QuestionRendering, PhaseDefinition, Rule, CodeSample, ContentReference, TutorialStep, Tutorial, WidgetKind, WidgetReference, HowTo } from './types.js';
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
@@ -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
@@ -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.