@rasensio/aidlc-content 1.49.0 → 1.51.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/explain.d.ts +30 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +56 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +70 -0
- package/docs/cost-tracking.md +188 -0
- package/docs/hygiene-and-compliance.md +135 -0
- package/docs/index.yaml +115 -0
- package/docs/knowledge-graph.md +121 -0
- package/docs/lifecycle.md +186 -0
- package/docs/metrics.md +67 -0
- package/docs/reviews-and-guidance.md +144 -0
- package/docs/roadmap.md +347 -0
- package/docs/skills-and-platforms.md +126 -0
- package/docs/state-and-concurrency.md +75 -0
- package/package.json +2 -1
- package/skills/00-overview.md +1 -1
- package/skills/04-roadmap.md +1 -1
- package/skills/40-implementation.md +7 -6
- package/skills/84-autopilot.md +2 -1
package/docs/index.yaml
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Manifest for the explanation pages in packages/content/docs/ — the single
|
|
2
|
+
# declaration site. They ship inside @rasensio/aidlc-content and are read from the
|
|
3
|
+
# installed package by `aidlc explain`; no project ever holds a copy
|
|
4
|
+
# (docs-unreachable-from-installed-projects).
|
|
5
|
+
#
|
|
6
|
+
# Four claims live here as data rather than as prose, because a test can compare
|
|
7
|
+
# data and cannot read prose: which pages exist, what code each one is about,
|
|
8
|
+
# which CLI commands each one covers, and which broken-looking things are
|
|
9
|
+
# deliberate. Guards are in packages/cli/test/docs-topics.test.ts.
|
|
10
|
+
#
|
|
11
|
+
# Schema (capability-docs/design.md, "Interfaces"):
|
|
12
|
+
# boundary prose stating the pages-versus-knowledge-graph split
|
|
13
|
+
# entries[] one per file in this directory
|
|
14
|
+
# id short slug, unique
|
|
15
|
+
# kind index | topic
|
|
16
|
+
# file filename in this directory, must exist
|
|
17
|
+
# title topic only
|
|
18
|
+
# source topic only; the code this page is about, must exist on disk
|
|
19
|
+
# commands topic only; top-level CLI commands this page covers
|
|
20
|
+
# undocumented[] commands deliberately covered by no page, each with a reason
|
|
21
|
+
# historical_paths[] paths named on purpose that do not resolve, each with a reason
|
|
22
|
+
|
|
23
|
+
boundary: >-
|
|
24
|
+
These pages and the knowledge graph are split by QUESTION, not by audience — both
|
|
25
|
+
serve agents as well as people. The knowledge graph (.aidlc/knowledge/, surfaced into
|
|
26
|
+
CLAUDE.md by `aidlc knowledge context`) answers where a thing is and what shape it
|
|
27
|
+
has: terse, indexed, loaded into every prompt. These pages (packages/content/docs/,
|
|
28
|
+
read with `aidlc explain`) answer why a thing
|
|
29
|
+
exists, what breaks without it, and how to use it: prose, read on demand. If a page
|
|
30
|
+
here restates a fact the knowledge graph already carries, that is the duplication this
|
|
31
|
+
split exists to prevent — link to the code instead. No page documents the contents of
|
|
32
|
+
the knowledge graph itself, which is why no topic's `source` points into
|
|
33
|
+
.aidlc/knowledge/.
|
|
34
|
+
|
|
35
|
+
entries:
|
|
36
|
+
- id: readme
|
|
37
|
+
kind: index
|
|
38
|
+
file: README.md
|
|
39
|
+
|
|
40
|
+
- id: lifecycle
|
|
41
|
+
kind: topic
|
|
42
|
+
title: The lifecycle
|
|
43
|
+
file: lifecycle.md
|
|
44
|
+
source: packages/cli/src/core
|
|
45
|
+
commands: [init, start, continue, status, transition, gate, menu, amend, templates, spec, cross-check]
|
|
46
|
+
|
|
47
|
+
- id: state-and-concurrency
|
|
48
|
+
kind: topic
|
|
49
|
+
title: State, claims and concurrent work
|
|
50
|
+
file: state-and-concurrency.md
|
|
51
|
+
source: packages/cli/src/concurrency
|
|
52
|
+
commands: [claim, release]
|
|
53
|
+
|
|
54
|
+
- id: roadmap
|
|
55
|
+
kind: topic
|
|
56
|
+
title: The roadmap
|
|
57
|
+
file: roadmap.md
|
|
58
|
+
source: packages/content/skills/04-roadmap.md
|
|
59
|
+
commands: [roadmap, autopilot]
|
|
60
|
+
|
|
61
|
+
- id: reviews-and-guidance
|
|
62
|
+
kind: topic
|
|
63
|
+
title: Reviews, guidance and the constitution
|
|
64
|
+
file: reviews-and-guidance.md
|
|
65
|
+
source: packages/cli/src/review
|
|
66
|
+
commands: [review, constitution]
|
|
67
|
+
|
|
68
|
+
- id: cost-tracking
|
|
69
|
+
kind: topic
|
|
70
|
+
title: Cost tracking
|
|
71
|
+
file: cost-tracking.md
|
|
72
|
+
source: packages/cli/src/cost
|
|
73
|
+
commands: [cost]
|
|
74
|
+
|
|
75
|
+
- id: metrics
|
|
76
|
+
kind: topic
|
|
77
|
+
title: Metrics
|
|
78
|
+
file: metrics.md
|
|
79
|
+
source: packages/cli/src/metrics
|
|
80
|
+
commands: [metrics]
|
|
81
|
+
|
|
82
|
+
- id: knowledge-graph
|
|
83
|
+
kind: topic
|
|
84
|
+
title: The knowledge graph
|
|
85
|
+
file: knowledge-graph.md
|
|
86
|
+
source: packages/cli/src/knowledge
|
|
87
|
+
commands: [knowledge, discover]
|
|
88
|
+
|
|
89
|
+
- id: skills-and-platforms
|
|
90
|
+
kind: topic
|
|
91
|
+
title: Skills and platforms
|
|
92
|
+
file: skills-and-platforms.md
|
|
93
|
+
source: packages/cli/src/compile
|
|
94
|
+
commands: [add, update, docs, explain]
|
|
95
|
+
|
|
96
|
+
- id: hygiene-and-compliance
|
|
97
|
+
kind: topic
|
|
98
|
+
title: Repo hygiene and compliance
|
|
99
|
+
file: hygiene-and-compliance.md
|
|
100
|
+
source: packages/cli/src/doctor
|
|
101
|
+
commands: [doctor, compliance]
|
|
102
|
+
|
|
103
|
+
# Empty on purpose: every top-level command is claimed by a page above. The
|
|
104
|
+
# checker's allowlist branch is exercised on synthetic input in the test rather
|
|
105
|
+
# than by leaving a real command undocumented to keep a code path warm.
|
|
106
|
+
undocumented: []
|
|
107
|
+
|
|
108
|
+
historical_paths:
|
|
109
|
+
- path: packages/web
|
|
110
|
+
reason: >-
|
|
111
|
+
Deleted at the website cutover (nextjs-website-revamp, T27). Named in
|
|
112
|
+
knowledge-graph.md and hygiene-and-compliance.md as the worked example behind the
|
|
113
|
+
`knowledge-stale-sources` check: 15 graph entities described it for 17 days, and
|
|
114
|
+
neither page can make that point without writing the path that no longer resolves.
|
|
115
|
+
The entries were removed by stale-graph-entries; the story is what stays.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# The knowledge graph
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
An agent starting a session knows nothing about your codebase. Left to itself it will re-explore —
|
|
6
|
+
grepping, opening files, reconstructing the same picture the last session built and discarded. That is
|
|
7
|
+
slow, and it is charged per token.
|
|
8
|
+
|
|
9
|
+
The knowledge graph is persistent memory for that problem. Entities (modules, decisions, conventions,
|
|
10
|
+
dependencies, debt, endpoints) with relationships between them, stored as YAML and indexed for
|
|
11
|
+
full-text search. `aidlc knowledge context` compiles a summary of it into `CLAUDE.md`, so the facts
|
|
12
|
+
arrive in the prompt without anyone querying for them.
|
|
13
|
+
|
|
14
|
+
It is deliberately **terse**. Each entry is a compressed paragraph, because it competes for prompt
|
|
15
|
+
space with the actual task. That compression is also its limit, and the reason the pages in this folder
|
|
16
|
+
exist alongside it — see the boundary below.
|
|
17
|
+
|
|
18
|
+
**What breaks without it:** every session pays the discovery cost again, and two sessions reach
|
|
19
|
+
different conclusions about the same code.
|
|
20
|
+
|
|
21
|
+
## How to use it
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
aidlc discover
|
|
25
|
+
aidlc knowledge query cost
|
|
26
|
+
aidlc knowledge get <id>
|
|
27
|
+
aidlc knowledge graph <id>
|
|
28
|
+
aidlc knowledge add --type decision --id <slug> --name "<title>" --description "<why>"
|
|
29
|
+
aidlc knowledge relate <from> <to> --type <kind>
|
|
30
|
+
aidlc knowledge context --scope phase --phase design
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`discover` scans a codebase and seeds the graph — that is how a brownfield project starts. After that,
|
|
34
|
+
record findings as you make them rather than at the end.
|
|
35
|
+
|
|
36
|
+
To add several entities at once, pipe YAML documents (separated by `---`) to
|
|
37
|
+
`aidlc knowledge add --stdin`. A `tags:` string there means what `--tags` means: comma-separated.
|
|
38
|
+
**Every document is checked before any is written**, and a run that fails writes nothing. That rule
|
|
39
|
+
exists because, before 1.40.1, the YAML was written before the index looked at it. A `tags: a,b,c`
|
|
40
|
+
string was saved, then the command crashed with `entity.tags.join is not a function`. The natural
|
|
41
|
+
retry merged into the saved record and turned the string into one tag per character. Four entities in
|
|
42
|
+
this repo's own graph carried 11 to 17 single-letter tags each before anyone noticed.
|
|
43
|
+
|
|
44
|
+
**Query before exploring.** That is the whole point: `aidlc knowledge query <term>` costs one command,
|
|
45
|
+
and re-deriving the same fact costs a session.
|
|
46
|
+
|
|
47
|
+
**Then check what it told you against the source.** This is the rule that matters most on this page.
|
|
48
|
+
The graph is an index into the code, not an authority over it. An entry naming a component, a function
|
|
49
|
+
or a registry is a pointer to open, and an entry written by an earlier instance may name something a
|
|
50
|
+
later one renamed or deleted. A design built on a symbol that is not there produces code against an API
|
|
51
|
+
nobody has, and nothing downstream catches it until implementation.
|
|
52
|
+
|
|
53
|
+
`aidlc knowledge verify <id>` records that you checked. `aidlc knowledge update <id>` corrects an entry
|
|
54
|
+
that no longer matches.
|
|
55
|
+
|
|
56
|
+
That rule is not hypothetical here. For 17 days this graph carried **15 entities describing
|
|
57
|
+
`@rasensio/aidlc-web`, a package deleted at the website cutover** — eight module entries plus seven
|
|
58
|
+
dependencies sourced to its manifest. Its directory, `packages/web`, does not exist; the repo has
|
|
59
|
+
`packages/cli`, `packages/content` and `packages/website`. `aidlc knowledge query "web"` answered
|
|
60
|
+
with the deleted package before it answered with the real one. Nothing reported it, because a stale
|
|
61
|
+
entry and a correct one are the same shape.
|
|
62
|
+
|
|
63
|
+
They are no longer the same shape to `aidlc doctor`. The `knowledge-stale-sources` check resolves
|
|
64
|
+
every entity's `source` against the disk and every relationship endpoint against the entity set, and
|
|
65
|
+
names what does not resolve. It is **report tier**: it applies nothing, under any tier or flag. Of
|
|
66
|
+
those 15 entities, 12 wanted deleting and 3 wanted their path corrected to a different manifest —
|
|
67
|
+
indistinguishable from the data alone, so the remedy is a decision and `doctor` may not make it. See
|
|
68
|
+
[Repo hygiene and compliance](hygiene-and-compliance.md).
|
|
69
|
+
|
|
70
|
+
Two things that episode is worth remembering for. First, the two staleness signals that existed
|
|
71
|
+
could not see it: `lastVerified` age flags most of a real graph, correct entries included, and the
|
|
72
|
+
git-diff change signal goes quiet exactly when a directory is deleted, because a path that no longer
|
|
73
|
+
exists stops appearing in diffs. Second, the entries were **auto-discovered at confidence 0.6**, so
|
|
74
|
+
they lost the `CLAUDE.md` context budget to the hand-written 0.9 entries and never reached a prompt.
|
|
75
|
+
That is the confidence ranking working — and it is also why nobody noticed for 17 days. A low-value
|
|
76
|
+
fact that is wrong is still wrong; it just costs less per day.
|
|
77
|
+
|
|
78
|
+
## The boundary against these pages
|
|
79
|
+
|
|
80
|
+
Both surfaces are read by agents, so the split is by **question**, not audience:
|
|
81
|
+
|
|
82
|
+
| | Answers | Shape | When it is read |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| Knowledge graph | Where is it? What shape is it? | Terse, indexed | Every prompt |
|
|
85
|
+
| These pages | Why does it exist? What breaks without it? How do I use it? | Prose | On demand |
|
|
86
|
+
|
|
87
|
+
A worked example of the difference. The graph records that `resolveSource` returns a `ResolveResult` of
|
|
88
|
+
`ok` or `reason` rather than `null`. True, and useful for finding it. What it cannot afford to carry is
|
|
89
|
+
*why*: a null could not distinguish "no transcript on disk" from "transcript found but no matching
|
|
90
|
+
turns", and the bugfix instance `cost-claim-attribution` bought that distinction. An agent holding only
|
|
91
|
+
the fact will simplify the type back to `null` and re-introduce the bug. An agent that read
|
|
92
|
+
[cost-tracking.md](cost-tracking.md) will not.
|
|
93
|
+
|
|
94
|
+
So: if something belongs in one line and answers *where*, it goes in the graph. If it needs a paragraph
|
|
95
|
+
and answers *why*, it goes here. A page that restates a graph fact is the duplication the split exists
|
|
96
|
+
to prevent — which is why no page here documents the graph's contents, and no topic's `source`
|
|
97
|
+
points into `.aidlc/knowledge/`.
|
|
98
|
+
|
|
99
|
+
## Where the code lives
|
|
100
|
+
|
|
101
|
+
- `packages/cli/src/knowledge` — the engine. YAML files are the source of truth; a SQLite full-text
|
|
102
|
+
index sits behind a `KnowledgeIndex` interface for search. The index is derived and can be rebuilt.
|
|
103
|
+
- `.aidlc/knowledge` — this project's own graph: `modules.yaml`, `decisions.yaml`,
|
|
104
|
+
`conventions.yaml`, `dependencies.yaml`, `debt.yaml`, `endpoints.yaml`, `relationships.yaml`, plus
|
|
105
|
+
the index database. Newer records sit one per file in a directory named for the type —
|
|
106
|
+
`decisions/<id>.yaml`, `relationships/<from>-<type>-<to>-<hash>.yaml` — and both layouts are read.
|
|
107
|
+
|
|
108
|
+
Why one file per record: every instance used to append its decision to the end of `decisions.yaml`.
|
|
109
|
+
Two branches that both append at the end conflict, so once one pull request merged, every other open
|
|
110
|
+
one conflicted — and GitHub never runs the custom merge driver, so none of them could merge. On
|
|
111
|
+
2026-10-02 that stalled a three-item autopilot run for two hours. Separate files make the conflict
|
|
112
|
+
impossible rather than recoverable. The existing records were left where they are on purpose: moving
|
|
113
|
+
them in one commit would itself conflict with every open branch. `aidlc knowledge compact` folds the
|
|
114
|
+
record files back into the per-type files when one file is wanted.
|
|
115
|
+
- `packages/cli/src/operational/knowledge.ts` — the operational hooks around it.
|
|
116
|
+
- `packages/cli/src/discover` — the codebase scan behind `aidlc discover`.
|
|
117
|
+
|
|
118
|
+
One implementation lesson worth carrying: a "preserve this unchanged" path that round-trips through a
|
|
119
|
+
serializer does not preserve anything. Rebuilding the index must copy bytes, not re-emit parsed
|
|
120
|
+
structures, or comment formatting and key order drift on every rebuild — 829 lines of diff versus 108
|
|
121
|
+
on real data.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# The lifecycle
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
Working with an AI agent on real software has a specific failure mode: the agent writes code
|
|
6
|
+
immediately, the code is plausible, and nobody can tell afterwards what it was supposed to do. There
|
|
7
|
+
is no record of the decision, so the next session re-decides it, differently.
|
|
8
|
+
|
|
9
|
+
The lifecycle exists to put a paper trail in front of the code. An **instance** is one unit of work
|
|
10
|
+
with its own folder under `.aidlc/state/`. It moves through **phases**, and each phase must produce a
|
|
11
|
+
named document before the next one starts. Requirements before design, design before code, tests
|
|
12
|
+
before release. That ordering is not ceremony — it is the only thing that makes a later question
|
|
13
|
+
like "why is it built this way?" answerable at all.
|
|
14
|
+
|
|
15
|
+
**What breaks without it:** the work still gets done, but each session starts from scratch. This
|
|
16
|
+
project's own numbers make the case: every instance has left a folder of requirements, designs and
|
|
17
|
+
retrospectives — 47 of them at the time of writing — and the reason anything in this repo can be
|
|
18
|
+
traced back to a decision is that the document existed before the code did. Where the trail is missing — the knowledge graph's stale
|
|
19
|
+
entries, for instance — nobody can tell whether a thing is wrong or merely old.
|
|
20
|
+
|
|
21
|
+
The second reason is subtler. Phases give an agent a place to *stop*. Without a phase boundary, an
|
|
22
|
+
agent asked to fix a typo will refactor the module, because there is no structural moment at which
|
|
23
|
+
"that is enough" is the correct answer.
|
|
24
|
+
|
|
25
|
+
## How to use it
|
|
26
|
+
|
|
27
|
+
Set up a project once:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
aidlc init
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then, per piece of work:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
aidlc start quick-feature --name my-thing
|
|
37
|
+
aidlc status
|
|
38
|
+
aidlc continue
|
|
39
|
+
aidlc transition my-thing
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`start` creates the folder and a branch. `continue` resumes from the next incomplete step, loading
|
|
43
|
+
only the context that step needs. `transition` evaluates the gates and advances the phase — it
|
|
44
|
+
refuses if the phase's documents are not done, which is the point.
|
|
45
|
+
|
|
46
|
+
**Templates** decide which phases apply. There are five, in `packages/content/templates`:
|
|
47
|
+
|
|
48
|
+
| Template | Phases | Use it for |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `micro-task` | implementation, testing | a typo, a version bump |
|
|
51
|
+
| `bugfix` | reproduction first, then the usual | something is broken |
|
|
52
|
+
| `quick-feature` | requirements → deployment | most features |
|
|
53
|
+
| `full-feature` | adds ideation | you do not yet know what you are building |
|
|
54
|
+
| `spike` | research question, no release | finding out whether something is possible |
|
|
55
|
+
|
|
56
|
+
`aidlc templates` lists them; `aidlc templates show <name> --scope <scope>` prints each phase with
|
|
57
|
+
the artifacts it requires. Use it before creating an instance by hand — an instance seeded with
|
|
58
|
+
artifacts its template never asked for fails its gates on files nobody needs, and until this command
|
|
59
|
+
existed the artifact names lived only inside `node_modules`.
|
|
60
|
+
|
|
61
|
+
**Scope** is the other dial, and it is the one people get wrong. Scope is not how big or ambitious
|
|
62
|
+
the work is — it is which phases run. `micro` starts at implementation, `standard` starts at
|
|
63
|
+
requirements, `full` starts at ideation. A large change with a settled design is `standard`; a small
|
|
64
|
+
change nobody has thought about is `full`.
|
|
65
|
+
|
|
66
|
+
**Gates** are the checks between phases. `aidlc gate <instance> <phase>` runs one without advancing,
|
|
67
|
+
which is what CI calls. It fails on any phase not yet marked complete, and `aidlc transition` is the
|
|
68
|
+
step that marks it — so a gate run before the transition fails by design. When either command fails,
|
|
69
|
+
its output ends with a numbered **Next steps** block: one action per kind of failure, with the real
|
|
70
|
+
instance, phase and artifact names filled in. When the `CI` environment variable is set, `aidlc gate`
|
|
71
|
+
words that block for a pipeline and names no command that changes lifecycle state. `--json` output
|
|
72
|
+
carries no such block.
|
|
73
|
+
|
|
74
|
+
Two gates are worth knowing because they block real work:
|
|
75
|
+
|
|
76
|
+
- The **traceability gate** requires every active acceptance criterion in `requirements.md` to be
|
|
77
|
+
referenced by some test. Criteria are written `- AC-1: <text>`, and tests refer to them as
|
|
78
|
+
`<instance>/AC-1`. It has no override flag, deliberately — the one time a gate here had no bypass,
|
|
79
|
+
it forced the discovery that five "criteria" were release process steps rather than properties of
|
|
80
|
+
the software. The requirements skill asks for behaviours in one of four shapes —
|
|
81
|
+
`WHEN …, THE SYSTEM SHALL …`, `WHILE …`, `IF …, THEN …`, `WHERE …` — because each one names its
|
|
82
|
+
own test. The gate does not check the shape, on purpose: a performance budget or a docs change is
|
|
83
|
+
not a behaviour, and a check would force it into one. A bugfix's `requirements.md` also lists
|
|
84
|
+
*unchanged* behaviour as criteria, so the fix cannot quietly break its neighbour.
|
|
85
|
+
- The **ownership gate** checks that the session transitioning a phase is the one that claimed it.
|
|
86
|
+
This one has an override, and the honest observation from the retrospectives is that having the
|
|
87
|
+
override meant it got waved through by reflex on every phase.
|
|
88
|
+
|
|
89
|
+
`aidlc menu`, or bare `aidlc`, opens a picker of the common commands if you would rather not
|
|
90
|
+
remember them.
|
|
91
|
+
|
|
92
|
+
**When requirements move after their phase closed**, `aidlc amend <instance>` is the way back. The
|
|
93
|
+
gates check that artifacts exist, not that they still agree with each other, so editing
|
|
94
|
+
`requirements.md` after its phase completed leaves the phase `complete`, the design describing the old
|
|
95
|
+
criterion, and every gate passing — silent drift.
|
|
96
|
+
|
|
97
|
+
The protocol is: retire the criterion in place, issue its replacement with a fresh ID, then append an
|
|
98
|
+
amendment section naming what changed and which phases it invalidated.
|
|
99
|
+
|
|
100
|
+
```markdown
|
|
101
|
+
## Amendment 1 — 2026-09-07, after the design review
|
|
102
|
+
- **Retires:** AC-4, AC-9
|
|
103
|
+
- **Issues:** AC-31
|
|
104
|
+
- **Reopens:** design
|
|
105
|
+
- **Reason:** the customer integration needs JSON as well as CSV
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`aidlc amend <instance> --impact` is read-only and lists every task, test and artifact that still cites
|
|
109
|
+
the retired criteria. It is deliberately advisory: it *suggests* phases and never chooses them, because
|
|
110
|
+
the first design derived the reopen set from that walk and the result was degenerate —
|
|
111
|
+
`requirements.md` always cites every retired criterion, so every amendment reopened the requirements
|
|
112
|
+
phase. A destructive state write should not depend on a prose-scanning heuristic.
|
|
113
|
+
|
|
114
|
+
Running `aidlc amend <instance>` reopens the phase named in `Reopens:` and every phase between it and
|
|
115
|
+
the current one, setting each back to `in-progress`. That re-arms the gates that already exist — this
|
|
116
|
+
added no new gate — and appends a `type: reopen` record so churn is visible in history rather than
|
|
117
|
+
reading as a normal forward pass. It needs a live claim, prints every change before making it, requires
|
|
118
|
+
`--confirm` from testing onwards, applies amendments in order, and is a stated no-op on a second run.
|
|
119
|
+
A completed instance is refused outright: its change is new work.
|
|
120
|
+
|
|
121
|
+
A correction confined to the current phase does not have to reopen anything. Write `Reopens: none`, or
|
|
122
|
+
name the current phase: the amendment is recorded and no phase moves. That is checked, not taken on
|
|
123
|
+
trust. The walk is the one input a refusal may read: `aidlc amend` refuses any reopen — `none` included —
|
|
124
|
+
that would leave a completed phase still citing a retired criterion, names each such phase, and prints the
|
|
125
|
+
`Reopens:` it would accept. `--impact` ends with the same verdict in one line. The walk still never
|
|
126
|
+
chooses what is reopened; it can only stop a write, never cause one.
|
|
127
|
+
|
|
128
|
+
**What the system does today** lives in `.aidlc/specs/`, one file per capability. Each instance's
|
|
129
|
+
`requirements.md` describes a change; after enough of them, nobody can say what the system does without
|
|
130
|
+
replaying all of them. The corpus is that replay, done once per instance instead of by every reader.
|
|
131
|
+
|
|
132
|
+
A requirements author writes the change as a `## Spec delta` section — `ADDED`, `MODIFIED` and `REMOVED`
|
|
133
|
+
blocks against named capabilities. A `MODIFIED` or `REMOVED` block carries `Base: <revision>`, the
|
|
134
|
+
8-character hash `aidlc spec show <capability>` prints for the requirement it was written against.
|
|
135
|
+
|
|
136
|
+
```markdown
|
|
137
|
+
## Spec delta
|
|
138
|
+
|
|
139
|
+
### MODIFIED cost-tracking: Phase windows
|
|
140
|
+
Base: 3f2a9c1e
|
|
141
|
+
The system attributes each usage record to the phase and turn open at its timestamp.
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
At the completion moment `aidlc transition` folds the delta in, stamps each requirement with the
|
|
145
|
+
instance and date, and files the delta under `.aidlc/specs/archive/<date>-<instance>.md`. If another
|
|
146
|
+
instance changed the same requirement first, its revision no longer matches the base: the fold stops,
|
|
147
|
+
completion is not recorded, and nothing is written. That is the whole conflict rule — a requirement is
|
|
148
|
+
never silently overwritten. `aidlc spec fold <instance> --dry-run` runs the same check any time before.
|
|
149
|
+
An instance with no delta completes exactly as before; nothing was back-filled, so the corpus starts
|
|
150
|
+
empty and grows.
|
|
151
|
+
|
|
152
|
+
**Before code is written**, `aidlc cross-check <instance>` reads `requirements.md`, `design.md` and
|
|
153
|
+
`tasks.md` side by side. `aidlc review` reads one artifact at a time, so a criterion design forgot, or a
|
|
154
|
+
task nobody asked for, passes every review and turns up in the code. The check prints one line per gap,
|
|
155
|
+
naming both artifacts and the criterion or task:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
requirements.md AC-4 → tasks.md: no open or done task cites it
|
|
159
|
+
tasks.md T7 → requirements.md: cites no criterion
|
|
160
|
+
requirements.md AC-2 → design.md: not mentioned
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
It reads criteria and tasks with the parsers the gates use, so the two can never disagree about what a
|
|
164
|
+
file says, and it prints the lines those parsers could not read. It writes nothing, changes no phase,
|
|
165
|
+
exits 0 whatever it finds, and no gate runs it — the implementation skill offers it before the first
|
|
166
|
+
task, and acting on a gap is a person's call: fix the open artifact, or amend a completed one.
|
|
167
|
+
|
|
168
|
+
## Where the code lives
|
|
169
|
+
|
|
170
|
+
- `packages/cli/src/core` — templates, scope resolution, and the gate engine.
|
|
171
|
+
`template-resolver.ts` handles `extends:`; `gate.ts` evaluates phase criteria.
|
|
172
|
+
- `packages/cli/src/state` — reading and writing the instance folders and `transitions.log`.
|
|
173
|
+
- `packages/cli/src/traceability` — the acceptance-criteria parser and the test scanner behind the
|
|
174
|
+
traceability gate. The AC grammar is closed on purpose; near-misses warn rather than vanish.
|
|
175
|
+
- `packages/cli/src/tasks` — the `tasks.md` breakdown parser.
|
|
176
|
+
- `packages/cli/src/change` — the amendment grammar, the read-only impact walk, and the `reopen`
|
|
177
|
+
transition. `parser.ts` is fence-aware and treats a wrapped bullet as one bullet, because the first
|
|
178
|
+
version truncated a wrapped `Retires:` list to half its ids and dropped three fields silently.
|
|
179
|
+
- `packages/cli/src/spec` — the spec corpus: the capability-file parser, the delta parser, and the
|
|
180
|
+
fold. `planFold` computes every file before `applyFold` writes any, which is what makes a conflict
|
|
181
|
+
leave the corpus untouched.
|
|
182
|
+
- `packages/cli/src/crosscheck` — `aidlc cross-check`, a pure comparison of one instance's three
|
|
183
|
+
planning artifacts. Nothing but its own command imports it, and a test keeps it that way.
|
|
184
|
+
- `packages/cli/src/menu` — the interactive picker. It reads no lifecycle state by design, because
|
|
185
|
+
"what should I do next?" belongs to the skill, not the menu.
|
|
186
|
+
- `packages/content/templates` — the five templates, as data.
|
package/docs/metrics.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Metrics
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
The lifecycle already records everything needed to answer whether the process is working: when each
|
|
6
|
+
phase started and ended, which instances were bugfixes, which earlier instance each bugfix blames, and
|
|
7
|
+
what each one cost. Metrics is the read-only roll-up over that, so nobody has to open 42 folders and
|
|
8
|
+
count.
|
|
9
|
+
|
|
10
|
+
The one number that justifies the module is **true cost**. A feature's own cost is what its sessions
|
|
11
|
+
burned. Its true cost adds the bugfixes it caused. Read own cost and meta-features look cheap; read true
|
|
12
|
+
cost and they do not. Cost tracking: $153.65 own, $219.78 true. The gap is a third, and it is exactly the
|
|
13
|
+
gap that hides whether finishing work is generating more work than it retires.
|
|
14
|
+
|
|
15
|
+
The second is **change failure rate** per template — the share of completed instances that some later
|
|
16
|
+
bugfix names as its cause. It is computed from `caused_by` links that a human confirmed, never guessed,
|
|
17
|
+
which is why the number means something.
|
|
18
|
+
|
|
19
|
+
**What breaks without it:** you optimise the thing you can see. This project shipped eleven features in
|
|
20
|
+
nine days and then spent five days on seven bugfixes, and the ratio flipped because most of what shipped
|
|
21
|
+
were features that watch the lifecycle rather than serve a user. That pattern was invisible until it was
|
|
22
|
+
counted.
|
|
23
|
+
|
|
24
|
+
## How to use it
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
aidlc metrics
|
|
28
|
+
aidlc metrics --json
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Read-only and pure — it computes from existing state and writes nothing.
|
|
32
|
+
|
|
33
|
+
Four families come out:
|
|
34
|
+
|
|
35
|
+
**Completion moment.** Scope-aware, which is fiddlier than it sounds: for most templates an instance
|
|
36
|
+
completes when its last applicable phase completes, but maintenance-terminal templates complete on
|
|
37
|
+
maintenance *entry*, because maintenance is open-ended and would otherwise never finish.
|
|
38
|
+
|
|
39
|
+
**Change failure rate per template.** Distinct completed instances named in some bugfix's `caused_by`,
|
|
40
|
+
divided by completed instances.
|
|
41
|
+
|
|
42
|
+
**Lead times**, per phase and per instance. Phase lead time pairs each `to: X` in `transitions.log`
|
|
43
|
+
with the next `from: X`, summing re-entries, and falls back to the phase file when the log is
|
|
44
|
+
incomplete. The fallback matters — a phase that was never transitioned through the CLI would otherwise
|
|
45
|
+
read as zero, which is a bug this project shipped and then fixed.
|
|
46
|
+
|
|
47
|
+
**True cost**, one level deep. Own cost plus the cost of bugfixes that blame this instance. One level,
|
|
48
|
+
not transitive, because a bugfix caused by a bugfix is a chain whose attribution nobody can defend.
|
|
49
|
+
Null when there is no ledger data — never zero-filled, for the reason given in
|
|
50
|
+
[cost-tracking.md](cost-tracking.md).
|
|
51
|
+
|
|
52
|
+
Two cautions when reading the output. The generated release posts snapshot these figures into their
|
|
53
|
+
frontmatter at generation time, so a post's numbers are historical, not live. And `own_cost` is never
|
|
54
|
+
written as `true_cost` — they are separate fields precisely because conflating them is the error the
|
|
55
|
+
module exists to prevent.
|
|
56
|
+
|
|
57
|
+
## Where the code lives
|
|
58
|
+
|
|
59
|
+
- `packages/cli/src/metrics/report.ts` — `computeMetrics(projectRoot, config)`, the whole module. One
|
|
60
|
+
pure function, no writes, no network.
|
|
61
|
+
- `packages/cli/src/commands/metrics.ts` — the command, including `--json`.
|
|
62
|
+
- `packages/cli/src/state` — `transitions.log` and the phase files it reads.
|
|
63
|
+
- `packages/cli/src/cost` — where the money half comes from.
|
|
64
|
+
|
|
65
|
+
The inputs are the instance folders under `.aidlc/state/`, and `caused_by` in `instance.yaml` is the
|
|
66
|
+
link that makes change failure rate and true cost possible. It is written only after a human confirms
|
|
67
|
+
the candidate — an unconfirmed guess is left absent, and absent means unknown rather than none.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Reviews, guidance and the constitution
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
An agent that reviews its own work finds nothing. Not because it is dishonest — because the same
|
|
6
|
+
reasoning that produced the artifact produces the review, so the blind spots are identical. Every
|
|
7
|
+
mechanism on this page is a way of getting a second opinion into the process without a second human.
|
|
8
|
+
|
|
9
|
+
Three of them, at three timescales:
|
|
10
|
+
|
|
11
|
+
**Adversarial review** is per artifact. A separate session reads `requirements.md` or `design.md`
|
|
12
|
+
cold and attacks it: ambiguities, contradictions, missing edge cases, untestable criteria, hidden
|
|
13
|
+
assumptions, security gaps, scope creep. Findings land in `<artifact>.md.review.md` — the artifact's
|
|
14
|
+
own extension, then the suffix — and a `critical` finding blocks the phase.
|
|
15
|
+
|
|
16
|
+
The value is not theoretical. On one design here the review found that a guard intended to catch
|
|
17
|
+
stale file paths would fail on the very page explaining stale file paths — the page has to name the
|
|
18
|
+
deleted path in order to discuss it. That defect would have shipped and then been "fixed" by deleting
|
|
19
|
+
the guard.
|
|
20
|
+
|
|
21
|
+
**Guidance layers** are per project, and they accumulate. Each is a markdown file in
|
|
22
|
+
`.aidlc/guidance/` registered in `index.yaml`. A layer added today applies to the next phase
|
|
23
|
+
immediately — phases read the index at runtime rather than having guidance baked into them, so
|
|
24
|
+
nothing needs recompiling.
|
|
25
|
+
|
|
26
|
+
They split two ways, and the split is the answer to "they accumulate":
|
|
27
|
+
|
|
28
|
+
- **Always-read** — every layer whose index entry does *not* say `on_demand: true`. A phase reads
|
|
29
|
+
these on entry, without being asked.
|
|
30
|
+
- **On-demand** — a layer whose entry carries `on_demand: true`. Registered, active, current; read
|
|
31
|
+
only when a phase has a stated reason to.
|
|
32
|
+
|
|
33
|
+
`lessons.md` is the on-demand one, and it is the reason the split exists. Appended at each
|
|
34
|
+
retrospective, up to three entries per instance, it reached **88KB — 85% of every byte a session read
|
|
35
|
+
before touching task work**, while the consolidation trigger sat at 25 sections and 90 days. At 23
|
|
36
|
+
sections, 12 days after a consolidation, both triggers were still in the future and the file was
|
|
37
|
+
already the largest thing resident. A third trigger now fires on **20KB**, which is the dimension that
|
|
38
|
+
costs money.
|
|
39
|
+
|
|
40
|
+
Its always-read counterpart is **`lessons-digest.md`**: a distillation, under 4KB, regenerated by the
|
|
41
|
+
retrospective at consolidation, with what it *omits* confirmed edit by edit. The full corpus stays at
|
|
42
|
+
`.aidlc/guidance/lessons.md` and the digest names that path, because distilling a safety mechanism is
|
|
43
|
+
fine and silently dropping it is not.
|
|
44
|
+
|
|
45
|
+
**The constitution** is per repo and rarely changes. It states what this repo is for and what it must
|
|
46
|
+
never become, derived from a product vision document. Triage checks items against it, and an item
|
|
47
|
+
that crosses a prohibition is the clearest `hold/` there is — naming the prohibition turns "not for
|
|
48
|
+
us" into a reason the submitter can read.
|
|
49
|
+
|
|
50
|
+
**What breaks without them:** work passes review because the reviewer was the author, the same defect
|
|
51
|
+
recurs every few weeks with nobody noticing it is the same defect, and scope drifts one reasonable
|
|
52
|
+
item at a time.
|
|
53
|
+
|
|
54
|
+
## How to use it
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
aidlc review my-thing requirements.md
|
|
58
|
+
aidlc constitution
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`review` scaffolds the findings file with a placeholder; an agent then performs the actual review and
|
|
62
|
+
overwrites it. Findings carry a severity (`critical`, `major`, `minor`), the section, the issue, a
|
|
63
|
+
concrete suggestion, and a status. A finding without a suggestion is not actionable and should not be
|
|
64
|
+
written.
|
|
65
|
+
|
|
66
|
+
**The findings filename carries the artifact's extension twice, and that is deliberate.** The file
|
|
67
|
+
for `requirements.md` is `requirements.md.review.md`. It looks like a typo. The gate looks artifacts
|
|
68
|
+
up by the name the template declares, so `requirements.review.md` is a file the gate cannot see — and
|
|
69
|
+
for three weeks that is exactly what happened: the command built its filename from the argument as
|
|
70
|
+
typed, so typing `requirements` (the phase name, and what the review skill's own prose called the
|
|
71
|
+
artifact) produced a file that was written, parsed, counted and summarised correctly and then reported
|
|
72
|
+
as absent by the transition. Eleven findings files across six completed instances were sitting on disk
|
|
73
|
+
having never counted toward the gate they were run for.
|
|
74
|
+
|
|
75
|
+
Both spellings of the *argument* now work — `aidlc review my-thing requirements` and
|
|
76
|
+
`aidlc review my-thing requirements.md` write the same file — because one module,
|
|
77
|
+
`review/findings-naming.ts`, owns the mapping in both directions. Prefer the command over naming the
|
|
78
|
+
file by hand. `aidlc doctor` renames any legacy file it finds.
|
|
79
|
+
|
|
80
|
+
The reason both directions live in one module rather than the writer being "fixed": fixing only the
|
|
81
|
+
writer leaves the already-written files dead, and fixing only the reader leaves two valid spellings
|
|
82
|
+
forever. The reader resolves both because it strips the suffix and then canonicalizes, so the two
|
|
83
|
+
spellings converge on one key and no call site has to know which it is looking at.
|
|
84
|
+
|
|
85
|
+
Two further conventions worth copying:
|
|
86
|
+
|
|
87
|
+
**Review `design.md` and `tasks.md` as separate findings files.** The skill can be read as folding
|
|
88
|
+
task findings into the design's file, and doing that once blurred two real defects together — splitting
|
|
89
|
+
by artifact forced findings about the breakdown apart from findings about the design.
|
|
90
|
+
|
|
91
|
+
**State the dimensions you found nothing in.** A review that lists four findings and stops is
|
|
92
|
+
indistinguishable from a review that only looked at four things. Writing "security: no issues, and
|
|
93
|
+
here is why the section does not apply" is what makes the absence evidence.
|
|
94
|
+
|
|
95
|
+
Guidance needs no command — drop a file in `.aidlc/guidance/` and add it to `index.yaml`. There is one
|
|
96
|
+
rule that matters: **guidance is never inlined into a compiled skill.** Skills reference the layer;
|
|
97
|
+
they do not copy it. A copy is a thing that drifts, and a drifted copy of a security rule is worse
|
|
98
|
+
than no rule.
|
|
99
|
+
|
|
100
|
+
That rule has a trap worth knowing before you register a small layer. A layer under 2KB *is* inlined
|
|
101
|
+
when a skill names it in a `{{guidance:<name>}}` placeholder — the threshold exists so a multi-KB
|
|
102
|
+
document becomes a pointer instead of bloating eighteen skill bodies. Set `always_pointer: true` on
|
|
103
|
+
the entry to opt out whatever the size. `lessons-digest.md` does exactly that, and the flag is
|
|
104
|
+
load-bearing rather than decorative: without it, the file the whole change exists to keep small would
|
|
105
|
+
be copied into every emitted skill.
|
|
106
|
+
|
|
107
|
+
`always_pointer` and `on_demand` answer different questions and are independent. `always_pointer` is
|
|
108
|
+
about *rendering* — how a named layer is delivered into a skill body. `on_demand` is about *reading* —
|
|
109
|
+
whether a phase opens the layer at all on entry. A layer may set either, both, or neither.
|
|
110
|
+
|
|
111
|
+
The lessons file is capped at three new entries per retrospective, and follow-ups default to
|
|
112
|
+
`dropped` — keeping one requires a stated reason. Both caps exist because finishing work reliably
|
|
113
|
+
generates more work than it retires.
|
|
114
|
+
|
|
115
|
+
## Where the code lives
|
|
116
|
+
|
|
117
|
+
- `packages/cli/src/review` — `findings-parser.ts` parses the findings format that the phase gate
|
|
118
|
+
reads. The gate looks only at `*.review.md`, which is what lets compliance findings live in a
|
|
119
|
+
separate `*.compliance.md` file and stay advisory. `findings-naming.ts` is the single declaration
|
|
120
|
+
site for how an artifact name and a findings filename map to each other, in both directions; two
|
|
121
|
+
independent derivations of that key is the defect it exists to prevent, so its test asserts the
|
|
122
|
+
inverse property rather than a table of literals.
|
|
123
|
+
- `packages/cli/src/commands/review.ts` — the scaffold generator, and the placeholder detection that
|
|
124
|
+
stops an unreviewed scaffold from counting as a review. Also the artifact allowlist: an argument the
|
|
125
|
+
instance's template does not declare is refused, because a typo used to scaffold a findings file no
|
|
126
|
+
gate would ever read.
|
|
127
|
+
- `packages/cli/src/doctor/migrations/findings-filename.ts` — renames legacy findings files. Safe
|
|
128
|
+
tier, and it never opens the file. A collision (both spellings present for one artifact) is
|
|
129
|
+
*reported*, never resolved: renaming would clobber a real review to tidy a name.
|
|
130
|
+
- `packages/content/guidance` — the layers shipped with the framework: `secure-defaults.md`,
|
|
131
|
+
`accessibility.md`, `api-conventions.md`, `lessons-digest.md`, `content-operations.md`, plus
|
|
132
|
+
`index.yaml`. The shipped digest is a **stub**, deliberately: lessons are project data, and a
|
|
133
|
+
framework package must not hand one project's observations to every consumer.
|
|
134
|
+
- `packages/cli/src/compile/guidance-budget.ts` — `alwaysReadLayers` and the two 20KB constants. One
|
|
135
|
+
module so the skill prose, the `aidlc status` notice and the tests cite one declaration.
|
|
136
|
+
- `packages/cli/src/doctor/migrations/lessons-digest-layer.ts` — moves an existing project onto the
|
|
137
|
+
digest. `confirm` tier, because it changes what every future session reads. Runs directly after
|
|
138
|
+
`install-guidance`, and that order is load-bearing: it registers the digest only when the body is
|
|
139
|
+
already on disk, and reversed it would leave `lessons` on-demand with nothing always-read in its
|
|
140
|
+
place.
|
|
141
|
+
- `.aidlc/guidance/lessons.md` — this project's own lessons, now on-demand. Worth reading before
|
|
142
|
+
writing any guard: it already contains the failure modes of the guard you are about to write.
|
|
143
|
+
- `packages/content/skills/80-review.md` and `05-constitution.md` — the review protocol and the
|
|
144
|
+
constitution protocol.
|