@rasensio/aidlc-content 1.14.4 → 1.15.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/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tutorials.d.ts +9 -0
- package/dist/tutorials.d.ts.map +1 -1
- package/dist/tutorials.js +127 -13
- package/dist/tutorials.js.map +1 -1
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/tutorials/deep-dive-gates-that-say-no.md +77 -0
- package/tutorials/deep-dive-how-much-process.md +62 -0
- package/tutorials/deep-dive-ideas-you-keep.md +68 -0
- package/tutorials/deep-dive-making-it-yours.md +86 -0
- package/tutorials/deep-dive-memory.md +65 -0
- package/tutorials/deep-dive-two-people.md +70 -0
- package/tutorials/deep-dive-what-it-cost.md +76 -0
- package/tutorials/deep-dive-why-files.md +70 -0
- package/tutorials/getting-started.md +2 -0
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,6 @@ export { loadGuidance } from './guidance.js';
|
|
|
4
4
|
export { loadGlossary, getGlossaryTerm } from './glossary.js';
|
|
5
5
|
export { loadCapabilities, getCapabilityMap } from './capabilities.js';
|
|
6
6
|
export { loadPhases, getPhase } from './phases.js';
|
|
7
|
-
export { loadTutorials, getTutorial, parseTutorialSource } from './tutorials.js';
|
|
8
|
-
export type { Skill, WorkflowTemplate, GuidanceLayer, GlossaryTerm, CapabilityMap, PhaseDefinition, CodeSample, ContentReference, TutorialStep, Tutorial } from './types.js';
|
|
7
|
+
export { loadTutorials, getTutorial, getSeries, parseTutorialSource } from './tutorials.js';
|
|
8
|
+
export type { Skill, WorkflowTemplate, GuidanceLayer, GlossaryTerm, CapabilityMap, PhaseDefinition, CodeSample, ContentReference, TutorialStep, Tutorial, WidgetKind, WidgetReference } from './types.js';
|
|
9
9
|
//# 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,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,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,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC5F,YAAY,EAAE,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,5 @@ export { loadGuidance } from './guidance.js';
|
|
|
4
4
|
export { loadGlossary, getGlossaryTerm } from './glossary.js';
|
|
5
5
|
export { loadCapabilities, getCapabilityMap } from './capabilities.js';
|
|
6
6
|
export { loadPhases, getPhase } from './phases.js';
|
|
7
|
-
export { loadTutorials, getTutorial, parseTutorialSource } from './tutorials.js';
|
|
7
|
+
export { loadTutorials, getTutorial, getSeries, parseTutorialSource } from './tutorials.js';
|
|
8
8
|
//# 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,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,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,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/tutorials.d.ts
CHANGED
|
@@ -6,4 +6,13 @@ import type { Tutorial } from './types.js';
|
|
|
6
6
|
export declare function parseTutorialSource(raw: string): Tutorial;
|
|
7
7
|
export declare function loadTutorials(): Tutorial[];
|
|
8
8
|
export declare function getTutorial(slug: string): Tutorial | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Chapters of one series, ordered by `seriesOrder` and falling back to `order`
|
|
11
|
+
* when a chapter omits it (AC-9).
|
|
12
|
+
*
|
|
13
|
+
* No shipped chapter omits `seriesOrder`, so the fallback has no user — which is
|
|
14
|
+
* exactly why it has a direct unit test. An untested fallback is a fallback that
|
|
15
|
+
* rots until the day something needs it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getSeries(series: string): Tutorial[];
|
|
9
18
|
//# sourceMappingURL=tutorials.d.ts.map
|
package/dist/tutorials.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tutorials.d.ts","sourceRoot":"","sources":["../src/tutorials.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"tutorials.d.ts","sourceRoot":"","sources":["../src/tutorials.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,QAAQ,EAMT,MAAM,YAAY,CAAC;AA+LpB;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAWzD;AAuBD,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CA4B1C;AAkCD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAE9D;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,EAAE,CAIpD"}
|
package/dist/tutorials.js
CHANGED
|
@@ -8,6 +8,22 @@ const SLUG_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
|
8
8
|
const STEP_HEADING_PATTERN = /^## Step (\d+): (.+)$/gm;
|
|
9
9
|
const CONTENT_REF_PATTERN = /\{\{ref:(skill|template|phase|guidance)\/([^}]+)\}\}/g;
|
|
10
10
|
const CODE_BLOCK_PATTERN = /```(\w+)(?:\s+source=(\S+))?\n([\s\S]*?)```/g;
|
|
11
|
+
/*
|
|
12
|
+
* Widget directives (deep-dive-tutorial/AC-1, AC-2).
|
|
13
|
+
*
|
|
14
|
+
* The kind alternation and the slug rule live INSIDE the pattern on purpose.
|
|
15
|
+
* That is what makes a bad kind or a malformed id simply not match — nothing is
|
|
16
|
+
* extracted, nothing throws, and the text stays in the body as literal
|
|
17
|
+
* characters. There is no validation branch to get wrong, and no way to produce
|
|
18
|
+
* a half-extracted widget.
|
|
19
|
+
*
|
|
20
|
+
* The visible cost is that a typo (`{{widget:transcipt/foo}}`) reaches the
|
|
21
|
+
* published page as literal text, which no loader-level check can see. That gap
|
|
22
|
+
* is closed by a test instead: the near-miss check compares the count of
|
|
23
|
+
* `{{widget:` occurrences against the number of extracted widgets per step
|
|
24
|
+
* (AC-58).
|
|
25
|
+
*/
|
|
26
|
+
const WIDGET_PATTERN = /\{\{widget:(transcript|completion|checkpoint)\/([a-z0-9]+(?:-[a-z0-9]+)*)\}\}/g;
|
|
11
27
|
function parseFrontmatter(raw) {
|
|
12
28
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
13
29
|
if (!match) {
|
|
@@ -19,6 +35,8 @@ function parseFrontmatter(raw) {
|
|
|
19
35
|
const description = parsed['description'];
|
|
20
36
|
const order = parsed['order'];
|
|
21
37
|
const slug = parsed['slug'];
|
|
38
|
+
const series = parsed['series'];
|
|
39
|
+
const seriesOrder = parsed['seriesOrder'];
|
|
22
40
|
if (typeof title !== 'string' || title.length === 0 || title.length > 100) {
|
|
23
41
|
throw new Error(`Invalid title: must be a string of 1-100 characters`);
|
|
24
42
|
}
|
|
@@ -31,10 +49,32 @@ function parseFrontmatter(raw) {
|
|
|
31
49
|
if (typeof slug !== 'string' || slug.length === 0 || slug.length > 80 || !SLUG_PATTERN.test(slug)) {
|
|
32
50
|
throw new Error(`Invalid slug: must be lowercase alphanumeric with hyphens, max 80 characters`);
|
|
33
51
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
52
|
+
// `series` and `seriesOrder` are optional (AC-6, AC-9): absent means a
|
|
53
|
+
// standalone tutorial, which is what every tutorial was before series existed.
|
|
54
|
+
// Present-but-wrong is an error, matching how the four keys above behave —
|
|
55
|
+
// a mistyped series would otherwise silently orphan a chapter (AC-8).
|
|
56
|
+
if (series !== undefined) {
|
|
57
|
+
if (typeof series !== 'string' ||
|
|
58
|
+
series.length === 0 ||
|
|
59
|
+
series.length > 80 ||
|
|
60
|
+
!SLUG_PATTERN.test(series)) {
|
|
61
|
+
throw new Error(`Invalid series: must be lowercase alphanumeric with hyphens, max 80 characters`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (seriesOrder !== undefined) {
|
|
65
|
+
if (typeof seriesOrder !== 'number' ||
|
|
66
|
+
!Number.isInteger(seriesOrder) ||
|
|
67
|
+
seriesOrder < 1 ||
|
|
68
|
+
seriesOrder > 999) {
|
|
69
|
+
throw new Error(`Invalid seriesOrder: must be an integer between 1 and 999`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const frontmatter = { title, description, order, slug };
|
|
73
|
+
if (series !== undefined)
|
|
74
|
+
frontmatter.series = series;
|
|
75
|
+
if (seriesOrder !== undefined)
|
|
76
|
+
frontmatter.seriesOrder = seriesOrder;
|
|
77
|
+
return { frontmatter, body };
|
|
38
78
|
}
|
|
39
79
|
function extractCodeSamples(body) {
|
|
40
80
|
const samples = [];
|
|
@@ -65,6 +105,27 @@ function extractContentReferences(body) {
|
|
|
65
105
|
}
|
|
66
106
|
return refs;
|
|
67
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Extract `{{widget:<kind>/<id>}}` references from a step body (AC-1).
|
|
110
|
+
*
|
|
111
|
+
* The pattern is re-instantiated per call rather than shared, exactly as
|
|
112
|
+
* `extractCodeSamples` and `extractContentReferences` do: a module-level global
|
|
113
|
+
* regex carries `lastIndex` between calls, so the second parse of the same
|
|
114
|
+
* source silently skips matches (AC-4).
|
|
115
|
+
*/
|
|
116
|
+
function extractWidgets(body) {
|
|
117
|
+
const widgets = [];
|
|
118
|
+
let match;
|
|
119
|
+
const pattern = new RegExp(WIDGET_PATTERN.source, WIDGET_PATTERN.flags);
|
|
120
|
+
while ((match = pattern.exec(body)) !== null) {
|
|
121
|
+
widgets.push({
|
|
122
|
+
kind: match[1],
|
|
123
|
+
id: match[2],
|
|
124
|
+
raw: match[0],
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return widgets;
|
|
128
|
+
}
|
|
68
129
|
function parseSteps(body) {
|
|
69
130
|
const steps = [];
|
|
70
131
|
const headings = [];
|
|
@@ -88,6 +149,7 @@ function parseSteps(body) {
|
|
|
88
149
|
body: stepBody,
|
|
89
150
|
codeSamples: extractCodeSamples(stepBody),
|
|
90
151
|
contentReferences: extractContentReferences(stepBody),
|
|
152
|
+
widgets: extractWidgets(stepBody),
|
|
91
153
|
});
|
|
92
154
|
}
|
|
93
155
|
return steps;
|
|
@@ -102,13 +164,29 @@ export function parseTutorialSource(raw) {
|
|
|
102
164
|
if (steps.length < 1 || steps.length > 50) {
|
|
103
165
|
throw new Error(`Tutorial must have between 1 and 50 steps, found ${steps.length}`);
|
|
104
166
|
}
|
|
105
|
-
return
|
|
167
|
+
return buildTutorial(frontmatter, steps);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Assemble a Tutorial, omitting the optional series keys when absent rather than
|
|
171
|
+
* setting them to `undefined`.
|
|
172
|
+
*
|
|
173
|
+
* The distinction matters: the round-trip property test serialises a Tutorial
|
|
174
|
+
* back to source and re-parses it, and a present-but-undefined key would emit
|
|
175
|
+
* `series: null` and fail validation on the way back in.
|
|
176
|
+
*/
|
|
177
|
+
function buildTutorial(frontmatter, steps) {
|
|
178
|
+
const tutorial = {
|
|
106
179
|
title: frontmatter.title,
|
|
107
180
|
description: frontmatter.description,
|
|
108
181
|
order: frontmatter.order,
|
|
109
182
|
slug: frontmatter.slug,
|
|
110
183
|
steps,
|
|
111
184
|
};
|
|
185
|
+
if (frontmatter.series !== undefined)
|
|
186
|
+
tutorial.series = frontmatter.series;
|
|
187
|
+
if (frontmatter.seriesOrder !== undefined)
|
|
188
|
+
tutorial.seriesOrder = frontmatter.seriesOrder;
|
|
189
|
+
return tutorial;
|
|
112
190
|
}
|
|
113
191
|
export function loadTutorials() {
|
|
114
192
|
if (!existsSync(TUTORIALS_DIR)) {
|
|
@@ -125,17 +203,53 @@ export function loadTutorials() {
|
|
|
125
203
|
if (steps.length < 1 || steps.length > 50) {
|
|
126
204
|
throw new Error(`Tutorial "${filename}": must have between 1 and 50 steps, found ${steps.length}`);
|
|
127
205
|
}
|
|
128
|
-
return {
|
|
129
|
-
title: frontmatter.title,
|
|
130
|
-
description: frontmatter.description,
|
|
131
|
-
order: frontmatter.order,
|
|
132
|
-
slug: frontmatter.slug,
|
|
133
|
-
steps,
|
|
134
|
-
};
|
|
206
|
+
return { filename, tutorial: buildTutorial(frontmatter, steps) };
|
|
135
207
|
});
|
|
136
|
-
|
|
208
|
+
assertNoSlugSeriesCollision(tutorials);
|
|
209
|
+
return tutorials.map(t => t.tutorial).sort((a, b) => a.order - b.order);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* A tutorial slug must not equal any series name (AC-60).
|
|
213
|
+
*
|
|
214
|
+
* The website renders a series at `/tutorials/<series>/` and a tutorial at
|
|
215
|
+
* `/tutorials/<slug>/`, so an overlap is two pages competing for one URL. This
|
|
216
|
+
* is checked here and *not* in `parseTutorialSource`, because it is a property of
|
|
217
|
+
* the whole set rather than of one file — validating it there would make the
|
|
218
|
+
* single-file parser reject perfectly valid input.
|
|
219
|
+
*
|
|
220
|
+
* Throwing fails every tutorial page, not just the colliding one. That is
|
|
221
|
+
* intended and safe: all content loading happens during `next build` on a static
|
|
222
|
+
* export, so a collision is a build failure and cannot reach a reader.
|
|
223
|
+
*/
|
|
224
|
+
function assertNoSlugSeriesCollision(entries) {
|
|
225
|
+
const seriesOwners = new Map();
|
|
226
|
+
for (const { filename, tutorial } of entries) {
|
|
227
|
+
if (tutorial.series !== undefined && !seriesOwners.has(tutorial.series)) {
|
|
228
|
+
seriesOwners.set(tutorial.series, filename);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
for (const { filename, tutorial } of entries) {
|
|
232
|
+
const owner = seriesOwners.get(tutorial.slug);
|
|
233
|
+
if (owner !== undefined) {
|
|
234
|
+
throw new Error(`Tutorial "${filename}": slug "${tutorial.slug}" collides with the series name ` +
|
|
235
|
+
`declared by "${owner}" — a series page and a tutorial page would share one URL`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
137
238
|
}
|
|
138
239
|
export function getTutorial(slug) {
|
|
139
240
|
return loadTutorials().find(t => t.slug === slug);
|
|
140
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Chapters of one series, ordered by `seriesOrder` and falling back to `order`
|
|
244
|
+
* when a chapter omits it (AC-9).
|
|
245
|
+
*
|
|
246
|
+
* No shipped chapter omits `seriesOrder`, so the fallback has no user — which is
|
|
247
|
+
* exactly why it has a direct unit test. An untested fallback is a fallback that
|
|
248
|
+
* rots until the day something needs it.
|
|
249
|
+
*/
|
|
250
|
+
export function getSeries(series) {
|
|
251
|
+
return loadTutorials()
|
|
252
|
+
.filter(t => t.series === series)
|
|
253
|
+
.sort((a, b) => (a.seriesOrder ?? a.order) - (b.seriesOrder ?? b.order));
|
|
254
|
+
}
|
|
141
255
|
//# sourceMappingURL=tutorials.js.map
|
package/dist/tutorials.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tutorials.js","sourceRoot":"","sources":["../src/tutorials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"tutorials.js","sourceRoot":"","sources":["../src/tutorials.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -64,12 +64,29 @@ export interface ContentReference {
|
|
|
64
64
|
entityName: string;
|
|
65
65
|
raw: string;
|
|
66
66
|
}
|
|
67
|
+
/** The three interactive widget kinds a tutorial step may reference. */
|
|
68
|
+
export type WidgetKind = 'transcript' | 'completion' | 'checkpoint';
|
|
69
|
+
/**
|
|
70
|
+
* A `{{widget:<kind>/<id>}}` occurrence in a step body.
|
|
71
|
+
*
|
|
72
|
+
* This package deliberately knows nothing about what a widget *is* — no React,
|
|
73
|
+
* no rendering, no data. It records that a step asked for `transcript/first-run`
|
|
74
|
+
* and leaves the meaning to whoever renders it (today: the website). That is
|
|
75
|
+
* what keeps a JSX toolchain out of this package, which `packages/cli` depends
|
|
76
|
+
* on (deep-dive-tutorial/AC-45).
|
|
77
|
+
*/
|
|
78
|
+
export interface WidgetReference {
|
|
79
|
+
kind: WidgetKind;
|
|
80
|
+
id: string;
|
|
81
|
+
raw: string;
|
|
82
|
+
}
|
|
67
83
|
export interface TutorialStep {
|
|
68
84
|
index: number;
|
|
69
85
|
title: string;
|
|
70
86
|
body: string;
|
|
71
87
|
codeSamples: CodeSample[];
|
|
72
88
|
contentReferences: ContentReference[];
|
|
89
|
+
widgets: WidgetReference[];
|
|
73
90
|
}
|
|
74
91
|
export interface Tutorial {
|
|
75
92
|
title: string;
|
|
@@ -77,5 +94,13 @@ export interface Tutorial {
|
|
|
77
94
|
order: number;
|
|
78
95
|
slug: string;
|
|
79
96
|
steps: TutorialStep[];
|
|
97
|
+
/**
|
|
98
|
+
* Groups a tutorial into a chapter series, e.g. `deep-dive`. Optional: a
|
|
99
|
+
* tutorial with no series is a standalone page, which is what every tutorial
|
|
100
|
+
* was before this existed (deep-dive-tutorial/AC-6, AC-7).
|
|
101
|
+
*/
|
|
102
|
+
series?: string;
|
|
103
|
+
/** Position within `series`. Falls back to `order` when absent (AC-9). */
|
|
104
|
+
seriesOrder?: number;
|
|
80
105
|
}
|
|
81
106
|
//# 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":"AAAA,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wEAAwE;AACxE,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "The check that says no"
|
|
3
|
+
description: "What a blocked transition looks like, how to read it, and why the review of your own design is written to be hostile on purpose."
|
|
4
|
+
order: 12
|
|
5
|
+
slug: deep-dive-gates-that-say-no
|
|
6
|
+
series: deep-dive
|
|
7
|
+
seriesOrder: 3
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Step 1: The problem — "done" that isn't
|
|
11
|
+
|
|
12
|
+
Everyone has shipped something that was done. The tests passed, the pull request merged, and three weeks later it turned out the requirement nobody wrote down was the one that mattered. "Done" had meant *I stopped working on it*.
|
|
13
|
+
|
|
14
|
+
Checklists don't fix this, because a checklist you fill in yourself measures your optimism. What fixes it is a check that refuses to advance, costs you something now, and tells you precisely what's missing.
|
|
15
|
+
|
|
16
|
+
## Step 2: Watch it refuse
|
|
17
|
+
|
|
18
|
+
Step through a real block, from the refusal to the fix:
|
|
19
|
+
|
|
20
|
+
{{widget:transcript/blocked-transition}}
|
|
21
|
+
|
|
22
|
+
Notice the shape of the refusal. It doesn't say "requirements not met" — it names the file, names the rule, and names what's absent. That's the whole design goal of a [gate](/docs/glossary/#gate): a check that fails should tell you what to do next, not that you have failed.
|
|
23
|
+
|
|
24
|
+
Notice also that the second attempt was *also* refused. Writing the review wasn't enough. A review that found a serious hole and was then ignored is not a review; it's a file.
|
|
25
|
+
|
|
26
|
+
## Step 3: Why the review is hostile
|
|
27
|
+
|
|
28
|
+
The review step doesn't ask "does this look reasonable". It works through a fixed list of ways a document can be wrong:
|
|
29
|
+
|
|
30
|
+
- **Ambiguities** — vague words, undefined behaviour.
|
|
31
|
+
- **Contradictions** — two requirements that can't both hold.
|
|
32
|
+
- **Missing edge cases** — the input nobody considered.
|
|
33
|
+
- **Untestable criteria** — a requirement with no observable pass or fail.
|
|
34
|
+
- **Hidden assumptions** — the precondition nobody wrote down.
|
|
35
|
+
- **Security gaps**, **scalability concerns**, **scope creep**.
|
|
36
|
+
|
|
37
|
+
Every finding gets a severity, and only `critical` findings block. That threshold is deliberate: a check that blocks on everything gets overridden as a matter of routine, and an override that's routine is not a check.
|
|
38
|
+
|
|
39
|
+
The {{ref:skill/aidlc-review}} skill also says something inconvenient: whoever wrote the document should not be the one reviewing it. Self-review finds the problems you already knew about and misses the ones you can't see, which are the expensive ones. Where a genuinely separate reviewer isn't available, the honest move is to say so at the top of the review and read it knowing that.
|
|
40
|
+
|
|
41
|
+
## Step 4: What a gate actually checks
|
|
42
|
+
|
|
43
|
+
Two different things, and they're worth separating.
|
|
44
|
+
|
|
45
|
+
**Conditions for finishing a step.** Do the required documents exist, and are they marked complete? This is mechanical and boring and catches the most common failure, which is moving on with a document half-written.
|
|
46
|
+
|
|
47
|
+
**Conditions for starting the next one.** For example: implementation won't start unless a git branch exists. That sounds fussy until the first time somebody implements a feature straight onto the main branch.
|
|
48
|
+
|
|
49
|
+
Both run the same way. In your terminal, `aidlc transition` evaluates them before moving. In your build pipeline, `aidlc gate <name> <step>` exits 0 or 1, so the same rules can block a merge:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
aidlc gate price-alerts testing
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The strictest of them all guards the testing step, and it is worth knowing about before you meet it: it reads the numbered criteria out of `requirements.md` and fails if any of them has no test referring to it. Which means the practical consequence of writing a vague requirement isn't a philosophical debate — it's a red build.
|
|
56
|
+
|
|
57
|
+
## Step 5: When you need to go anyway
|
|
58
|
+
|
|
59
|
+
Sometimes the check gets it wrong. The requirement it wants was genuinely not applicable; the review finding was a misunderstanding.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
aidlc transition price-alerts --override
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Two things about this. It exists, because a check with no escape hatch gets disabled entirely. And it's recorded, so "we overrode the design review" is a fact somebody can find later rather than a thing that quietly happened.
|
|
66
|
+
|
|
67
|
+
Use it when you've decided the check is wrong. Not when you'd rather not do the work.
|
|
68
|
+
|
|
69
|
+
## Step 6: Checkpoint
|
|
70
|
+
|
|
71
|
+
{{widget:checkpoint/review-found-something}}
|
|
72
|
+
|
|
73
|
+
## Step 7: What you can do now
|
|
74
|
+
|
|
75
|
+
You can read a blocked transition and know exactly what to do about it, and you can tell the difference between a check worth satisfying and one worth overriding on the record.
|
|
76
|
+
|
|
77
|
+
Next: the notes AIDLC keeps about your codebase, and why they mean your assistant stops reading the same files over and over.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "How much process is enough"
|
|
3
|
+
description: "Choosing between five workflows so a one-line fix doesn't get treated like a new subsystem — and why the choice is about the kind of work, not its size."
|
|
4
|
+
order: 11
|
|
5
|
+
slug: deep-dive-how-much-process
|
|
6
|
+
series: deep-dive
|
|
7
|
+
seriesOrder: 2
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Step 1: The problem — process you route around
|
|
11
|
+
|
|
12
|
+
Any process that treats a typo the same as a new subsystem gets abandoned for the typo. Then, because you've abandoned it once, you abandon it for the small feature too. Within a month the process applies only to the work nobody minds slowing down, which is not the work that needed it.
|
|
13
|
+
|
|
14
|
+
This is why AIDLC doesn't have *a* process. It has five, and picking the right one is the first real decision on any piece of work.
|
|
15
|
+
|
|
16
|
+
## Step 2: Five workflows, three amounts of ceremony
|
|
17
|
+
|
|
18
|
+
Step through the same tool making three different amounts of noise:
|
|
19
|
+
|
|
20
|
+
{{widget:transcript/template-choice}}
|
|
21
|
+
|
|
22
|
+
Each of those named workflows — AIDLC calls them [templates](/docs/glossary/#template) — fixes which steps the work runs through and which documents it owes. There are five:
|
|
23
|
+
|
|
24
|
+
- {{ref:template/micro-task}} — implementation and testing. For changes where the design decision was made years ago.
|
|
25
|
+
- {{ref:template/bugfix}} — reproduce it, fix it, test it, ship it. No design step.
|
|
26
|
+
- {{ref:template/quick-feature}} — requirements through deployment. For a feature whose shape is already obvious.
|
|
27
|
+
- {{ref:template/full-feature}} — all seven steps, with a hostile review of the requirements and the design.
|
|
28
|
+
- {{ref:template/spike}} — for a question you can't answer without experimenting. Ends in a findings document, not a change.
|
|
29
|
+
|
|
30
|
+
## Step 3: The word that catches everybody out
|
|
31
|
+
|
|
32
|
+
How far back the work starts has a name — [scope](/docs/glossary/#scope) — and it's the single most misread word in the framework.
|
|
33
|
+
|
|
34
|
+
It does **not** mean how big or how ambitious the change is. It means *which steps apply*:
|
|
35
|
+
|
|
36
|
+
- `micro` starts at implementation.
|
|
37
|
+
- `standard` starts at requirements.
|
|
38
|
+
- `full` starts at ideation.
|
|
39
|
+
|
|
40
|
+
So a small change can be `full` and a large one can be `standard`. What decides it is whether the *problem* still needs exploring. If you already know what you want and only need to write it down precisely, starting at ideation is theatre. If you don't yet know what the right behaviour would be, skipping ideation means you'll invent it halfway through implementation, undocumented.
|
|
41
|
+
|
|
42
|
+
Ask it as one question: **do I know what this should do?** If yes, `standard`. If no, `full`. If the answer is "this is not a decision, it's a chore" — `micro`.
|
|
43
|
+
|
|
44
|
+
## Step 4: Your turn
|
|
45
|
+
|
|
46
|
+
{{widget:completion/pick-the-template}}
|
|
47
|
+
|
|
48
|
+
## Step 5: Where the choice usually goes wrong
|
|
49
|
+
|
|
50
|
+
Two failure modes, both common.
|
|
51
|
+
|
|
52
|
+
**Reaching for `full-feature` because the work feels important.** Importance isn't the criterion. A payment change that restores documented behaviour is important and still a {{ref:template/bugfix}}. Running ideation on it produces a document nobody reads, and — worse — it teaches everyone that the documents are decorative.
|
|
53
|
+
|
|
54
|
+
**Reaching for `micro-task` because the work feels small.** Size isn't the criterion either. The one-line change that needed requirements is the one where nobody could agree afterwards what the line was supposed to do.
|
|
55
|
+
|
|
56
|
+
There's a third question that isn't about size at all: **is this a change to what the system can do, or a chore the system already supports?** Publishing a release note, regenerating a report, rotating a log — those aren't work items at all. They're operations, and they belong in a skill, which chapter eight covers. Wrapping a repeatable chore in seven steps is the fastest way to make anyone hate this.
|
|
57
|
+
|
|
58
|
+
## Step 6: What you can do now
|
|
59
|
+
|
|
60
|
+
You can look at a piece of work and answer three questions in about ten seconds: change or chore, do I know what it should do, and is there a design decision to make. Those three answers pick the workflow, and the workflow picks everything else.
|
|
61
|
+
|
|
62
|
+
Next: what happens when the tool refuses to let you move on, and why the review is deliberately trying to find fault with your work.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Ideas that don't evaporate"
|
|
3
|
+
description: "Capturing a suggestion in one move, the six lines that make it decidable, and the design choice that stops the list rotting."
|
|
4
|
+
order: 15
|
|
5
|
+
slug: deep-dive-ideas-you-keep
|
|
6
|
+
series: deep-dive
|
|
7
|
+
seriesOrder: 6
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Step 1: The problem — the good idea from Tuesday
|
|
11
|
+
|
|
12
|
+
Somebody said something genuinely useful in a conversation on Tuesday. By Thursday nobody can reconstruct it, including them.
|
|
13
|
+
|
|
14
|
+
Every team knows the fix is "write it down somewhere", and every team has a somewhere that nobody reads. Idea lists fail for two specific reasons, and they're worth naming because the design here is aimed at both.
|
|
15
|
+
|
|
16
|
+
**Capture costs too much.** If recording an idea means opening a tool, choosing a category, estimating effort and picking a priority, you won't do it while you're in the middle of something else — which is exactly when the idea arrives.
|
|
17
|
+
|
|
18
|
+
**The list only grows.** A list of ninety items with no decisions attached is the same as no list, except it also makes you feel bad.
|
|
19
|
+
|
|
20
|
+
## Step 2: Five folders, and that's the design
|
|
21
|
+
|
|
22
|
+
{{widget:transcript/roadmap-move}}
|
|
23
|
+
|
|
24
|
+
Read the first frame again, because it's the whole thing: **the folder an idea sits in tells you its status.** There is no `status:` field in an idea file, and adding one is explicitly forbidden.
|
|
25
|
+
|
|
26
|
+
That sounds like a stylistic preference. It isn't. Two records of one fact eventually disagree, and when they do, a wrong status looks exactly like a right one — nothing reports it, and you find out when somebody works from the stale one. Moving the file makes disagreement impossible rather than merely unlikely.
|
|
27
|
+
|
|
28
|
+
The five folders are `inbox` (somebody suggested it), `backlog` (we agreed to do it), `in-progress` (work has started), `done` (shipped), and `hold` (not now, or no).
|
|
29
|
+
|
|
30
|
+
`hold` deliberately holds both "parked" and "declined". Separating them would mean a judgement at the moment of dropping, which is the moment you have least information; the git history and the item's own history list tell you which it was, if anyone ever asks.
|
|
31
|
+
|
|
32
|
+
## Step 3: The six lines
|
|
33
|
+
|
|
34
|
+
Every idea opens with a summary of exactly six labelled lines: what it is, who benefits, what they can do afterwards, whether it makes money, rough cost and the main risk, and a recommendation.
|
|
35
|
+
|
|
36
|
+
Six, and no more. The purpose is that a decision can be made from the top of the file. An idea whose value cannot be stated in six lines is one nobody can decide about, so it sits there while everyone waits for somebody else to read the whole thing.
|
|
37
|
+
|
|
38
|
+
Two of the rules attached to it are more interesting than they look.
|
|
39
|
+
|
|
40
|
+
**"None" is a real answer for revenue, and often the right one.** Writing "indirect" for everything makes the field useless — and worse than blank, because it reads as answered. It's also the cheapest available honesty check: an idea that has to write *revenue — none* and *value — it observes the process rather than serving a user* has just declared itself.
|
|
41
|
+
|
|
42
|
+
**Write it last.** A summary drafted before the detail is a guess wearing a summary's clothes.
|
|
43
|
+
|
|
44
|
+
## Step 4: Your turn
|
|
45
|
+
|
|
46
|
+
{{widget:completion/change-the-status}}
|
|
47
|
+
|
|
48
|
+
## Step 5: Turning an idea into work
|
|
49
|
+
|
|
50
|
+
Promotion is where the list meets everything in the earlier chapters: pick a workflow, pick how far back to start, create the work, and let the idea seed it. For a `full` start the idea becomes raw material that the ideation step refines; for a `standard` start it's the input to requirements.
|
|
51
|
+
|
|
52
|
+
Then the idea file records the name of the work it became, and moves to `in-progress`. When that work finishes, it moves to `done` on its own — the completion event does it, which is the only reason it reliably happens.
|
|
53
|
+
|
|
54
|
+
## Step 6: The cap, and why it's uncomfortable
|
|
55
|
+
|
|
56
|
+
The rule this project ended up needing: **cap the list at around a dozen raw ideas.** At the cap, promote something or drop something before capturing anything new.
|
|
57
|
+
|
|
58
|
+
That's unpleasant, and it's the point. An idea list that only grows is a list nobody triages. Forcing a decision at capture time means the decisions actually get made, and the ones you drop were mostly going to be dropped in eighteen months anyway, having taken up room the whole time.
|
|
59
|
+
|
|
60
|
+
There's a related discipline about follow-ups. Every finished piece of work generates suggestions for next time, and the default for those is *dropped* — keeping one requires a stated reason. Finishing work reliably generates more work than it retires, and if you keep every follow-up the list becomes a machine for creating work about work.
|
|
61
|
+
|
|
62
|
+
The one exception is worth knowing, because this project paid for it: **if a fix is understood well enough to write down in prose, capture it as an idea in the same breath.** A follow-up described in a completion note here was rediscovered from scratch three days later, re-analysed, and re-scoped as new work. Prose is not a work item.
|
|
63
|
+
|
|
64
|
+
## Step 7: What you can do now
|
|
65
|
+
|
|
66
|
+
You can capture an idea mid-task in one file with no ceremony, decide on somebody else's idea by reading six lines, and change its status by moving it. And you know why there's no status field to edit.
|
|
67
|
+
|
|
68
|
+
Next: what actually breaks when two people work in the same folder at the same time. It isn't what you'd expect.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Making it yours"
|
|
3
|
+
description: "How a lesson from one job reaches the next one, turning a repeatable chore into a skill instead of a project, keeping the repository healthy, and running the same setup on five different assistants."
|
|
4
|
+
order: 17
|
|
5
|
+
slug: deep-dive-making-it-yours
|
|
6
|
+
series: deep-dive
|
|
7
|
+
seriesOrder: 8
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Step 1: The problem — learning the same thing twice
|
|
11
|
+
|
|
12
|
+
You finished something last month, and it went wrong in a way you understood completely by the end. This month it went wrong the same way.
|
|
13
|
+
|
|
14
|
+
The knowledge existed. It was in somebody's head, or in a note at the bottom of a document nobody opens at the start of a job. Whatever mechanism was supposed to carry it forward, it didn't.
|
|
15
|
+
|
|
16
|
+
Same shape for chores. There's a fiddly six-step thing you do every release — the steps are known, the order matters, and you get one wrong roughly every third time.
|
|
17
|
+
|
|
18
|
+
## Step 2: Lessons in, chores out
|
|
19
|
+
|
|
20
|
+
{{widget:transcript/guidance-and-doctor}}
|
|
21
|
+
|
|
22
|
+
Three separate mechanisms in one transcript. Take them one at a time.
|
|
23
|
+
|
|
24
|
+
## Step 3: Guidance — read at the start, not the end
|
|
25
|
+
|
|
26
|
+
`.aidlc/guidance/` holds files that get read at the **start** of a job. That single detail is the whole mechanism.
|
|
27
|
+
|
|
28
|
+
`lessons.md` is the one fed by finishing work. When a piece of work completes, the [retrospective](/docs/glossary/#retrospective) captures what was learned and appends it there. Next job, before anything else happens, those lines get read.
|
|
29
|
+
|
|
30
|
+
Contrast with the failure mode: writing the same lesson into the completed job's own write-up. That file is read by nobody, ever again. The distinction between *guidance* (read at the start of every job) and *record* (read when investigating that particular job) is the entire difference between a lesson that carries forward and one that doesn't.
|
|
31
|
+
|
|
32
|
+
Alongside lessons sit the layers you write yourself — {{ref:guidance/secure-defaults}}, {{ref:guidance/accessibility}}, {{ref:guidance/api-conventions}} — active when listed in `index.yaml`. Those are yours to edit; the framework won't overwrite them.
|
|
33
|
+
|
|
34
|
+
Two disciplines keep the file usable. **A cap** — around three lessons per completed job, or the file becomes something nobody reads, at which point it's decorative. And **consolidation**: when it gets long, the run of near-duplicates about the same mistake collapses into one entry that says it better.
|
|
35
|
+
|
|
36
|
+
## Step 4: Skills — for the chore, not the change
|
|
37
|
+
|
|
38
|
+
The rule is one line: **the lifecycle for changes to the system, a skill for repeatable operations of the system.**
|
|
39
|
+
|
|
40
|
+
Adding a feature so posts can belong to a series changes what the system can do — that's a piece of work with steps and documents. Publishing a post is a chore the system already supports, done the same way every time — wrapping each one in seven steps is ceremony, and it's the fastest way to make everybody route around all of this.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
aidlc add skill publish-release-notes --description "Publish notes for a tagged release"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The important part is what gets scaffolded with it: **the verification lives inside the skill.** Facts checked, links resolved, build run — as steps of the skill itself, not as a review wrapped around each invocation. A skill carrying its own checks produces checked output every time it runs. A skill relying on somebody remembering to review it produces checked output sometimes.
|
|
47
|
+
|
|
48
|
+
There's an escape hatch worth knowing. Sometimes running a chore reveals a missing capability — you go to publish and find there's no way to mark something a draft. At that point the chore stops being the work, and the missing capability becomes an ordinary piece of work. What you must not do is quietly grow the skill into building the feature.
|
|
49
|
+
|
|
50
|
+
`aidlc add action` is the smaller sibling: run something automatically on a lifecycle event. This project uses one to cut a branch whenever work starts, and one to move an idea to `done` when the work it became completes.
|
|
51
|
+
|
|
52
|
+
## Step 5: Doctor — repairing the repository
|
|
53
|
+
|
|
54
|
+
Repositories accumulate rubbish: lock files from crashed sessions, ignore rules that no longer cover what they should, generated files that got committed by accident.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
aidlc doctor --dry-run
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Two tiers, and the split is the interesting bit. **Safe** repairs apply on their own — adding a missing ignore rule harms nobody. **Confirm** repairs describe themselves and wait, because anything that could surprise you must be your decision. `--dry-run` shows both without doing either.
|
|
61
|
+
|
|
62
|
+
The same pass runs during `aidlc update`, with one difference that's a deliberate design choice: `doctor` exits non-zero when confirmable work is pending, and `update` never fails for it. A command you run to check health should be able to fail; a command you run to get the latest version should not fail because of unrelated tidying.
|
|
63
|
+
|
|
64
|
+
## Step 6: The same setup on five assistants
|
|
65
|
+
|
|
66
|
+
The lifecycle instructions live once, in `.aidlc/skills/`, as plain markdown. Setup packages that same text into whatever native format each assistant expects — Claude Code, Cursor, Kiro, Codex, Windsurf all get files in their own convention, generated from one canonical body.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx @rasensio/aidlc init --platform claude-code
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Which means switching assistants, or having two people on different ones, doesn't fork your process. The rules are in your repository; the packaging is per-tool. And when the framework updates, `aidlc update` refreshes the generated files and leaves everything you wrote — your guidance layers, your context documents, your ideas — alone.
|
|
73
|
+
|
|
74
|
+
One caution learned the hard way here: `npx <tool>` will happily prefer a globally installed copy over your local one. If a command reports that nothing changed while you're looking at changed files on disk, that's usually why.
|
|
75
|
+
|
|
76
|
+
## Step 7: Checkpoint
|
|
77
|
+
|
|
78
|
+
{{widget:checkpoint/where-lessons-go}}
|
|
79
|
+
|
|
80
|
+
## Step 8: What you can do now
|
|
81
|
+
|
|
82
|
+
You've been through the whole surface. Concretely, you can now: read a piece of work's state off the disk; pick the right amount of process for it; read a blocked check and fix it; ask what the project already knows instead of re-reading code; find out what something cost, including the bugs it caused; keep an idea list that doesn't rot; run alongside somebody else without losing work; and turn a repeated lesson or a repeated chore into something that carries itself forward.
|
|
83
|
+
|
|
84
|
+
The one thing worth carrying out of all of it: **every check you add is paid for by every future job, forever.** This project shipped eleven features in nine days and then spent five days almost entirely on fixes, because most of what it had shipped were features that watch the process rather than serve a user. Guidance somebody reads costs nothing to anyone who doesn't need it. A [gate](/docs/glossary/#gate) costs everybody, every time. Prefer the former, and hold the latter to a much higher bar.
|
|
85
|
+
|
|
86
|
+
Reference for everything mentioned here: [the command reference](/docs/cli/), [the skills](/docs/skills/), [the workflows](/docs/templates/), and [the glossary](/docs/glossary/) for any word that's been used carefully.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Notes about your code, kept once"
|
|
3
|
+
description: "How AIDLC records what it learns about your codebase so the next session asks a question instead of re-reading the same files."
|
|
4
|
+
order: 13
|
|
5
|
+
slug: deep-dive-memory
|
|
6
|
+
series: deep-dive
|
|
7
|
+
seriesOrder: 4
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Step 1: The problem — paying twice for the same understanding
|
|
11
|
+
|
|
12
|
+
Ask an assistant to change your alert engine and watch what it does first. It opens the alert engine. Then the price feed, because the engine imports it. Then two tests, to work out the intended behaviour. Then it makes a small change.
|
|
13
|
+
|
|
14
|
+
Tomorrow, same question, same reading. You paid for that understanding twice, and you'll pay for it again on Thursday. On a codebase of any size, the reading is most of the cost and the change is a rounding error.
|
|
15
|
+
|
|
16
|
+
The obvious fix is to write down what was learned. The reason nobody does it is that prose documentation about code goes stale and then actively misleads. So the notes have to be small, structured, attributed to a source file, and cheap enough to update as a matter of habit.
|
|
17
|
+
|
|
18
|
+
## Step 2: Write one, connect it, ask for it back
|
|
19
|
+
|
|
20
|
+
{{widget:transcript/knowledge-round-trip}}
|
|
21
|
+
|
|
22
|
+
Three commands, and they're the whole surface: record something, record a connection, ask a question.
|
|
23
|
+
|
|
24
|
+
Each note carries what it is (`module`, `service`, `decision`, `convention`, a few more), a description in plain words, and where in the codebase it came from. The source path matters more than it looks: a note that names its origin can be checked, and a note that can't be checked eventually becomes a lie.
|
|
25
|
+
|
|
26
|
+
## Step 3: What's worth writing down
|
|
27
|
+
|
|
28
|
+
Not everything. A note that restates what the code obviously says is noise, and noise is what makes people stop reading notes.
|
|
29
|
+
|
|
30
|
+
Worth recording:
|
|
31
|
+
|
|
32
|
+
- **The non-obvious dependency.** "Touching the price feed breaks the alert engine's cache assumptions."
|
|
33
|
+
- **The decision and its reason.** "We poll rather than subscribe because the upstream feed drops connections silently." Six months later nobody remembers the reason, and somebody 'improves' it.
|
|
34
|
+
- **The convention.** "Every handler validates before hashing." Conventions are invisible to anyone reading one file.
|
|
35
|
+
- **The debt, and why it's still there.** "This retry loop is wrong under partition; it hasn't mattered because we run single-region."
|
|
36
|
+
|
|
37
|
+
Not worth recording: anything you'd get from reading one function. That's what reading a function is for.
|
|
38
|
+
|
|
39
|
+
## Step 4: Your turn
|
|
40
|
+
|
|
41
|
+
{{widget:completion/ask-before-reading}}
|
|
42
|
+
|
|
43
|
+
## Step 5: How it gets read automatically
|
|
44
|
+
|
|
45
|
+
You mostly won't run `query` by hand. At the start of a step, the relevant notes get pulled in for you:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
aidlc knowledge context --scope phase --phase ideation
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
That returns the notes relevant to the step about to begin, within a token budget. The {{ref:skill/aidlc-ideation}} skill runs it before exploring any code, and the instruction is explicit about the order: the notes come first, and you only go read the codebase for what they don't cover.
|
|
52
|
+
|
|
53
|
+
There's a check attached, too. Leaving the ideation step requires the notes to contain at least three modules, two dependency connections, and one convention. That threshold is low on purpose — it isn't trying to make the notes complete. It's making sure that a piece of work which explored your codebase leaves something behind, rather than throwing the understanding away when the session ends.
|
|
54
|
+
|
|
55
|
+
## Step 6: The failure mode to watch for
|
|
56
|
+
|
|
57
|
+
Notes that nobody trusts are worse than no notes, because they cost a query and then have to be verified anyway.
|
|
58
|
+
|
|
59
|
+
Two habits keep them honest. Each note records a confidence and a last-verified date, so a stale one is visible as stale rather than as fact. And when a note turns out to be wrong, correcting it is the same command that created it — `add` with the same id merges over the top. Fixing a wrong note takes about fifteen seconds, which is the only reason anyone does it.
|
|
60
|
+
|
|
61
|
+
## Step 7: What you can do now
|
|
62
|
+
|
|
63
|
+
You can ask what your project already knows about a module before reading it, and you can leave a note behind when you learn something that took effort to work out. On a codebase you'll return to, that's the difference between paying for understanding once and paying every session.
|
|
64
|
+
|
|
65
|
+
Next: what all of this costs, and the number that's usually a third too low.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Two people, one checkout"
|
|
3
|
+
description: "What actually collides when two sessions share a folder, why the lock isn't the answer, and the one-flag fix."
|
|
4
|
+
order: 16
|
|
5
|
+
slug: deep-dive-two-people
|
|
6
|
+
series: deep-dive
|
|
7
|
+
seriesOrder: 7
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Step 1: The problem — somebody clobbered something
|
|
11
|
+
|
|
12
|
+
Two people, or two assistant sessions, working in the same checked-out folder at the same time. Somewhere around the end of the day, work goes missing and nobody can say quite how.
|
|
13
|
+
|
|
14
|
+
Everyone's first theory is that the two sessions overwrote each other's files. That theory is wrong, which matters, because it sends you looking for a locking mechanism when what you needed was a second folder.
|
|
15
|
+
|
|
16
|
+
## Step 2: What actually breaks
|
|
17
|
+
|
|
18
|
+
{{widget:transcript/two-sessions}}
|
|
19
|
+
|
|
20
|
+
The state files were never the problem. They're small, they're separated per piece of work, and two sessions on two different pieces of work barely touch the same bytes.
|
|
21
|
+
|
|
22
|
+
**Git is the problem.** One checked-out folder has exactly one current branch. When the other session starts something and cuts a branch, your checkout moves too — you are now sitting on their branch with your half-finished changes still on disk. And the moment either session stages everything and commits, the other's work goes in under the wrong name, on the wrong branch, in the wrong change.
|
|
23
|
+
|
|
24
|
+
There's a second casualty that's easier to miss. Two overlapping claims means usage records can't be assigned to either piece of work. On this project, two concurrent sessions in one folder lost all of their cost data — not some of it. Both pieces of work read as unknown, because every record had two equally plausible owners.
|
|
25
|
+
|
|
26
|
+
## Step 3: What the lock is actually for
|
|
27
|
+
|
|
28
|
+
There *is* a lock. It's the [claim](/docs/glossary/#claim) from the cost chapter, and it lives in a shared location so every folder of the same repository sees the same set.
|
|
29
|
+
|
|
30
|
+
What it does: tells you somebody else is already inside this piece of work, before you start editing its documents. It also expires — sixty minutes by default — because a session that crashes must not lock work forever. A claim past its timeout counts as stale and can be taken over, and if you're in a long session you renew it:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
aidlc claim price-alerts --renew
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
What it does **not** do: stop the git collision. It can't. Nothing about a lock on a piece of work prevents a branch switch in a shared folder.
|
|
37
|
+
|
|
38
|
+
So a claim tells the tool who is working on what. It is not the answer to concurrency, and treating it as one is how people end up surprised.
|
|
39
|
+
|
|
40
|
+
## Step 4: The fix, which is one flag
|
|
41
|
+
|
|
42
|
+
A git worktree gives you a second folder that shares one history. Two branches checked out at once, no collision:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
aidlc start bugfix --name login-500 --worktree
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
That creates the folder, creates the branch, and does all further work for that piece of work inside it. Both folders still see the same claims, because claims live in the shared git directory rather than in either checkout — which is exactly the property you want: separate branches, shared knowledge of who's doing what.
|
|
49
|
+
|
|
50
|
+
One thing to know about worktrees: `.aidlc/roadmap/` is written to the **primary** checkout, not the worktree's copy. Two worktrees each capturing ideas into their own copy would give you two idea lists that both look complete.
|
|
51
|
+
|
|
52
|
+
## Step 5: When you can't have a branch
|
|
53
|
+
|
|
54
|
+
Sometimes you want to work on something without touching code at all — writing requirements, reviewing a design, triaging ideas — while somebody else holds the checkout:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
aidlc start quick-feature --name pricing-rework --no-code
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
That records no branch, and the implementation step stays blocked until one exists. It's not a workaround for the collision; it's an admission that this work doesn't need code yet, which is often true for the first two steps of anything.
|
|
61
|
+
|
|
62
|
+
## Step 6: Checkpoint
|
|
63
|
+
|
|
64
|
+
{{widget:checkpoint/what-collides}}
|
|
65
|
+
|
|
66
|
+
## Step 7: What you can do now
|
|
67
|
+
|
|
68
|
+
You can start concurrent work without losing any of it, and you know which of the three options to reach for: a worktree when the second piece of work needs code, `--no-code` when it doesn't, and waiting when neither is true. And you know that a lock on the work is not a lock on the folder.
|
|
69
|
+
|
|
70
|
+
Next, and last: making it yours — the lessons that come back, the chores that become skills, and the tool that repairs the repository.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "What it cost, and what it really cost"
|
|
3
|
+
description: "How AIDLC works out what a piece of work spent, why the first number is usually too low, and why missing data is reported as missing rather than as zero."
|
|
4
|
+
order: 14
|
|
5
|
+
slug: deep-dive-what-it-cost
|
|
6
|
+
series: deep-dive
|
|
7
|
+
seriesOrder: 5
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Step 1: The problem — a bill with no line items
|
|
11
|
+
|
|
12
|
+
At the end of the month you know what you spent on AI assistance in total. You have no idea which feature it went on.
|
|
13
|
+
|
|
14
|
+
That's not a curiosity. It's the number you need to decide what to build next, and without it every estimate is a guess dressed up. Worse, the features that *felt* cheap are the ones most likely to have been expensive, because the expensive part usually happens after they ship.
|
|
15
|
+
|
|
16
|
+
## Step 2: Claim first, then it can be counted
|
|
17
|
+
|
|
18
|
+
{{widget:transcript/cost-attribution}}
|
|
19
|
+
|
|
20
|
+
The [claim](/docs/glossary/#claim) in the first frame is doing more than you'd think. It's another word to be careful with — nothing to do with support tickets or expenses. It's a note saying *this session is working on this piece of work, starting now*, and it's what makes spending attributable at all. Usage records carry a timestamp; the claim says whose timestamps those were.
|
|
21
|
+
|
|
22
|
+
Which produces the one rule worth remembering from this chapter: **spending during an unclaimed session cannot be attributed to anything.** It isn't lost — it goes into an unattributed pile you can reconcile later with `aidlc cost sync` — but it doesn't appear against the work until you do.
|
|
23
|
+
|
|
24
|
+
## Step 3: Read the last column
|
|
25
|
+
|
|
26
|
+
Every line of a cost report says how solid its own number is:
|
|
27
|
+
|
|
28
|
+
- `metered` — measured from real usage records. Trust it.
|
|
29
|
+
- `estimated` — derived from figures the platform reported but didn't itemise. Roughly right.
|
|
30
|
+
- `unknown` — a period of time is recorded with no usage attached to it.
|
|
31
|
+
- `no data` — nothing at all.
|
|
32
|
+
|
|
33
|
+
The reason to expose this rather than smooth it over: averaging a measured number with a guess produces a number with a guess's accuracy and a measurement's authority. That's the worst possible artefact. Here the report says which is which and lets you decide.
|
|
34
|
+
|
|
35
|
+
`unknown` deserves particular attention, because it does not mean zero. It means a stretch of work happened that nobody measured. Reading it as zero makes the cheapest-looking features the least-measured ones.
|
|
36
|
+
|
|
37
|
+
## Step 4: The number that's a third too low
|
|
38
|
+
|
|
39
|
+
Now the part that matters. Ask for the roll-up:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
aidlc metrics
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Two numbers appear against each piece of work:
|
|
46
|
+
|
|
47
|
+
- **cost** — what that work itself spent.
|
|
48
|
+
- **true cost** — that, plus what the bugs it caused spent.
|
|
49
|
+
|
|
50
|
+
They can differ enormously. On this project, cost tracking itself spent $153.65. It went on to cause four bug fixes, and its true cost came out at $357.71. Its own cost understated it by well over half.
|
|
51
|
+
|
|
52
|
+
That gap isn't an accounting curiosity. It hid a bad decision for two weeks. Cost tracking looked like a moderately priced feature and was in fact one of the most expensive things here, and the reason nobody noticed is that the cheap-looking number was the one on the report.
|
|
53
|
+
|
|
54
|
+
The mechanism is deliberately simple. When you fix a bug, you record which piece of work introduced it. That link is never written without somebody confirming it, because a guessed attribution is worse than none. Then the roll-up follows the links one level deep — a bug caused by a bug caused by a feature is not charged onwards, because at two levels the chains get long and the attribution gets dishonest.
|
|
55
|
+
|
|
56
|
+
That same linkage gives you a change failure rate per workflow: of the pieces of work completed under each template, what proportion caused a fix. On this project, `full-feature` runs at 45% and `micro-task` at 0%, which tells you something useful and slightly uncomfortable about which work actually breaks.
|
|
57
|
+
|
|
58
|
+
## Step 5: What these numbers can't tell you
|
|
59
|
+
|
|
60
|
+
Three limits, stated plainly, because a metric you over-trust is worse than one you ignore.
|
|
61
|
+
|
|
62
|
+
**It cannot tell you whether the work was worth doing.** It's a cost, not a return.
|
|
63
|
+
|
|
64
|
+
**It cannot see one level further.** True cost goes one hop. Real causation doesn't stop there.
|
|
65
|
+
|
|
66
|
+
**It cannot account for a session that wasn't claimed.** Two sessions working in one folder at once is the worst case: with overlapping claims, records can't be assigned to either, and on this project two concurrent sessions once lost all their cost data for exactly that reason.
|
|
67
|
+
|
|
68
|
+
## Step 6: Checkpoint
|
|
69
|
+
|
|
70
|
+
{{widget:checkpoint/own-vs-true}}
|
|
71
|
+
|
|
72
|
+
## Step 7: What you can do now
|
|
73
|
+
|
|
74
|
+
You can find out what a feature cost, tell how solid that figure is, and get the honest version that includes the bugs it caused. And you know that `unknown` on a cost report is a gap in measurement, not a free feature.
|
|
75
|
+
|
|
76
|
+
Next: the idea list — how to stop good suggestions evaporating, and the design decision that stops it rotting.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Why the work lives in files"
|
|
3
|
+
description: "Where AIDLC keeps track of a piece of work, why it uses plain files next to your code, and what happens the first time you point it at a codebase that already exists."
|
|
4
|
+
order: 10
|
|
5
|
+
slug: deep-dive-why-files
|
|
6
|
+
series: deep-dive
|
|
7
|
+
seriesOrder: 1
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Step 1: The problem — Monday morning
|
|
11
|
+
|
|
12
|
+
Every session with an AI assistant starts by explaining the project again. You spent Friday afternoon getting it up to speed on how your billing code fits together, what you'd decided about retries, and which half of the change was finished. On Monday it knows none of it.
|
|
13
|
+
|
|
14
|
+
You can paste in a summary. Then you're maintaining a summary. And the summary drifts from the code, quietly, until one day the assistant confidently builds on something that stopped being true a week ago.
|
|
15
|
+
|
|
16
|
+
The usual answer is a longer memory. That doesn't work either, because the thing you need to survive isn't the conversation — it's the *decisions*. Which step you're on. What you agreed the feature should do. What the review found. Those need to be readable by you, by a colleague, by your build pipeline, and by whichever assistant you happen to open tomorrow.
|
|
17
|
+
|
|
18
|
+
So AIDLC writes them down as files, in your repository, next to the code they describe.
|
|
19
|
+
|
|
20
|
+
## Step 2: What one command creates
|
|
21
|
+
|
|
22
|
+
Start a piece of work and watch what appears on disk. Step through it:
|
|
23
|
+
|
|
24
|
+
{{widget:transcript/first-instance}}
|
|
25
|
+
|
|
26
|
+
Three things worth noticing.
|
|
27
|
+
|
|
28
|
+
**The folder is the memory.** Nothing in there depends on a conversation having happened. `aidlc status` read those files cold.
|
|
29
|
+
|
|
30
|
+
**Each step of the work owes documents.** `phase-ideation.yaml` doesn't contain the thinking — it says which document that step is expected to produce and whether it exists yet. The thinking goes in `idea.md`, a normal markdown file you can read, edit, and argue with in a pull request.
|
|
31
|
+
|
|
32
|
+
**One folder per piece of work.** AIDLC calls that folder an [instance](/docs/glossary/#instance) — a word worth taking care with, because in most of software it means something else entirely. Here it just means one unit of work with its own folder: a feature, a fix, a spike.
|
|
33
|
+
|
|
34
|
+
## Step 3: Why a file and not chat history
|
|
35
|
+
|
|
36
|
+
Four things become possible once a decision lives in a file, and none of them are possible otherwise.
|
|
37
|
+
|
|
38
|
+
**You can review it.** `requirements.md` shows up in a pull request as a diff. Somebody can disagree with a line of it before any code exists.
|
|
39
|
+
|
|
40
|
+
**A machine can check it.** Your build pipeline can run `aidlc gate my-feature testing` and get an exit code. You cannot pipeline a conversation.
|
|
41
|
+
|
|
42
|
+
**It survives the tool.** These are markdown and YAML files in your git history. If you stop using AIDLC tomorrow, you still have the record of what was decided and why.
|
|
43
|
+
|
|
44
|
+
**Two people see the same thing.** Not two summaries of the same thing.
|
|
45
|
+
|
|
46
|
+
The documents a step is required to produce have a name — [artifacts](/docs/glossary/#artifact) — and they're the unit everything else is built on. The [gates](/docs/glossary/#gate) check them. Reviews target them. The whole framework is a set of rules about which files must exist before the work moves on.
|
|
47
|
+
|
|
48
|
+
## Step 4: Pointing it at code that already exists
|
|
49
|
+
|
|
50
|
+
Most codebases aren't new, and the first honest question is what any of this knows about yours.
|
|
51
|
+
|
|
52
|
+
On first activation, AIDLC reads your project and writes down what it found:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
aidlc discover
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
That produces context documents under `.aidlc/context/` — a description of the architecture as it actually stands, and a style guide derived from your real code rather than from a preference. It also seeds a set of notes about your modules and how they depend on each other, which the next chapter but two is about.
|
|
59
|
+
|
|
60
|
+
These are files like any other, so the useful move is to read them and fix what's wrong. A generated description of your architecture that you've corrected by hand is worth considerably more than one nobody has looked at.
|
|
61
|
+
|
|
62
|
+
## Step 5: Checkpoint
|
|
63
|
+
|
|
64
|
+
{{widget:checkpoint/where-does-it-live}}
|
|
65
|
+
|
|
66
|
+
## Step 6: What you can do now
|
|
67
|
+
|
|
68
|
+
You can open any `.aidlc/state/<name>/` folder and read, without running anything, what a piece of work is, which step it reached, and which documents it still owes. That's the foundation for everything in the rest of the series — every capability from here on is something that reads or writes those files.
|
|
69
|
+
|
|
70
|
+
Next: how much process a given piece of work should actually get, and how to stop a typo fix from being treated like a new subsystem.
|
|
@@ -62,3 +62,5 @@ Gates aren't just advisory — `aidlc gate <instance> <phase>` exits with code 0
|
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
That's the whole loop: set up once, pick a template per piece of work, and let the gates keep quality honest. Browse the rest of the docs to see what each phase skill does in detail.
|
|
65
|
+
|
|
66
|
+
Ready for the rest of it? [The deep dive](/tutorials/deep-dive/) is eight short chapters covering everything past this first session — cost, the notes it keeps about your code, working alongside somebody else, and why any of it is shaped this way.
|