@rasensio/aidlc-content 1.50.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/docs/roadmap.md
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# The roadmap
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
An idea arrives while you are mid-implementation on something else. You have three options: derail
|
|
6
|
+
what you are doing, write it on a sticky note, or lose it. The roadmap is a fourth — capture it in
|
|
7
|
+
five seconds and get back to work.
|
|
8
|
+
|
|
9
|
+
The design decision that matters is that **capture is legal at any moment**. No instance, no claim, no
|
|
10
|
+
phase. Capture never reads or writes anything under `.aidlc/state/`, so two sessions capturing
|
|
11
|
+
simultaneously just produce two files and no locking is needed or exists.
|
|
12
|
+
|
|
13
|
+
The second reason is social. Projects have more than one person proposing work, with mixed technical
|
|
14
|
+
depth, so the roadmap distinguishes *submitted* from *agreed*. An item in `inbox/` is somebody waiting
|
|
15
|
+
on a decision; an item in `backlog/` is a decision already made. Collapsing those two into one list is
|
|
16
|
+
how a backlog becomes a place ideas go to die.
|
|
17
|
+
|
|
18
|
+
**What breaks without it:** the thing this project measured — a backlog that only grows is the same as
|
|
19
|
+
no backlog. Over eleven days the product backlog moved zero items while self-repair items were
|
|
20
|
+
promoted within one to three days. The roadmap did not cause that, but it is what made it visible.
|
|
21
|
+
|
|
22
|
+
## How to use it
|
|
23
|
+
|
|
24
|
+
**Capture is not a CLI command.** The roadmap is files plus a skill, on purpose — capture friction
|
|
25
|
+
kills inboxes, and shelling out to a CLI mid-thought is friction. Ask the agent to capture an item,
|
|
26
|
+
and it writes one. `aidlc roadmap` does exist, but only for the GitHub Issues projection described
|
|
27
|
+
below; it never captures, triages or promotes.
|
|
28
|
+
|
|
29
|
+
**The directory an item sits in is its status.** There is no `status:` field, and you must never add
|
|
30
|
+
one:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
.aidlc/roadmap/
|
|
34
|
+
inbox/ submitted, nobody has triaged it
|
|
35
|
+
backlog/ accepted, will do, not started
|
|
36
|
+
in-progress/ an instance exists
|
|
37
|
+
done/ shipped
|
|
38
|
+
hold/ parked or declined
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Changing status means moving the file. Two representations of one fact drift apart, and a wrong status
|
|
42
|
+
is indistinguishable from a right one.
|
|
43
|
+
|
|
44
|
+
One item per file, `YYYYMMDD-<slug>.md`, with frontmatter carrying `id`, `title`, `created_at`,
|
|
45
|
+
`source`, `promoted_to`, `depends_on` and an append-only `history`.
|
|
46
|
+
|
|
47
|
+
**Always quote the title.** `title: "..."` with double quotes, every time, with no judgement call
|
|
48
|
+
about whether this one needs it. An unquoted YAML plain scalar may not begin with a reserved
|
|
49
|
+
character, so the single most natural way to name a code defect — a title starting with a backtick —
|
|
50
|
+
does not parse. Nothing reports it: the item vanishes from browse, from promotion lookup, from the
|
|
51
|
+
completion action and from `aidlc doctor`, because an item that will not parse and an item that was
|
|
52
|
+
never written look identical. Five of this project's first 49 items were lost this way for up to ten
|
|
53
|
+
days.
|
|
54
|
+
|
|
55
|
+
**Every item opens with a six-line executive summary** — what it is, who benefits, customer value,
|
|
56
|
+
revenue, cost and risk, recommendation. This exists so triage can happen from the top of the file.
|
|
57
|
+
`None` is a real answer for revenue and usually the correct one; writing `Indirect` for everything
|
|
58
|
+
makes the field useless. It is also the cheapest check against building things that only watch the
|
|
59
|
+
lifecycle: a feature that has to write *"Revenue — None"* and *"Customer value — none, it observes the
|
|
60
|
+
lifecycle"* has declared itself.
|
|
61
|
+
|
|
62
|
+
The rule binds **every** path into `inbox/`, including an item written by an instance mid-phase.
|
|
63
|
+
`Not yet decided.` is a valid value for any label; an absent section is not a valid item.
|
|
64
|
+
`aidlc doctor` reports inbox items missing one, by name, and never writes one — see
|
|
65
|
+
[Hygiene and compliance](hygiene-and-compliance.md).
|
|
66
|
+
|
|
67
|
+
**A `hold/` entry says whether it is parked or declined.** One directory holds both, and the
|
|
68
|
+
`history` entry carries the reason: `{ status: hold, at: 2026-09-23, reason: "parked — gated on the
|
|
69
|
+
accounts table" }`, prefixed `parked — ` or `declined — `. Because `history` is frontmatter, browse
|
|
70
|
+
reads it without opening the body. The consequence worth knowing: agreed work waiting on a trigger
|
|
71
|
+
belongs in `hold/`, parked, not in `backlog/` — `backlog/` means "could start now", and an item gated
|
|
72
|
+
on something that does not exist yet makes every count of what is ready wrong.
|
|
73
|
+
|
|
74
|
+
Three flows: **capture** writes to `inbox/`, after checking whether an open item already owns the area
|
|
75
|
+
— if one does, the finding is appended there as a dated `## Received from <source>` section instead of
|
|
76
|
+
becoming a new file. **Triage** moves to `backlog/` or `hold/`, and is the one flow allowed to read
|
|
77
|
+
into an item's body — as far as the executive summary and no further. **Promote** creates an instance,
|
|
78
|
+
sets `promoted_to`, and moves the file to `in-progress/`. When the instance completes, the
|
|
79
|
+
`roadmap-done` action moves every item whose `promoted_to` names it to `done/` by itself; do not do
|
|
80
|
+
that by hand.
|
|
81
|
+
|
|
82
|
+
**Promotion is one command, not a checklist.** `aidlc start <template> --name <name> --roadmap-item
|
|
83
|
+
<item>` finds the item in `backlog/` or `inbox/` by file name or id, sets `promoted_to`, moves it to
|
|
84
|
+
`in-progress/` in the primary checkout, and copies it into the instance as `roadmap-item.md`. It works
|
|
85
|
+
with `--worktree`. Why a flag rather than prose: on 2026-10-02 two unattended runs of the same
|
|
86
|
+
worktree flow disagreed — one linked the item by hand, the other left it in `backlog/` with
|
|
87
|
+
`promoted_to: null`, so `roadmap-done` would never have found it. The reference is matched against
|
|
88
|
+
listed items and never opened as a path, and an item that already has a `promoted_to` is refused before
|
|
89
|
+
anything is created. The move is left uncommitted; committing it is the caller's.
|
|
90
|
+
|
|
91
|
+
**Picking the next item** is `aidlc roadmap next`: the `backlog/` item with the lowest `priority`
|
|
92
|
+
among those whose `depends_on` are all done, unranked items last, ties by file name. `--explain` shows
|
|
93
|
+
the whole order and why anything was skipped; `--json` is for scripts. It only reads. A script that
|
|
94
|
+
works the backlog may ask it what is next, but promoting stays a person's decision, so the command
|
|
95
|
+
that picks has no way to write. `priority` is an ordinary extension field (a rank, 1 first) that a
|
|
96
|
+
project declares in `roadmap.item_fields`.
|
|
97
|
+
|
|
98
|
+
**Ranking the backlog** is `aidlc roadmap rank <item> <n>`: it puts a `backlog/` item at rank `n` and
|
|
99
|
+
renumbers every ranked backlog item 1…k in their existing order, so ranks stay unique and gap-free —
|
|
100
|
+
hand-typed ranks drift, because an item leaving the backlog leaves a hole and two items typed at one
|
|
101
|
+
number tie. `--dry-run` prints the resulting order and writes nothing; the roadmap skill shows it at
|
|
102
|
+
triage, proposes a rank, and runs the command only after the person answers. Only the `priority:`
|
|
103
|
+
lines that change are rewritten, unranked items stay unranked, and the command refuses — writing
|
|
104
|
+
nothing — when the project does not declare `priority`, when the item is not in `backlog/`, or when
|
|
105
|
+
any backlog item carries a malformed rank. Where `priority` is declared, `roadmap next --explain`
|
|
106
|
+
also lists every unranked backlog item, so one sitting last is visible rather than silently last.
|
|
107
|
+
|
|
108
|
+
**Parking from an instance** is `aidlc roadmap park <instance> --reason "<why>"`: it moves the item
|
|
109
|
+
promoted to that instance into `hold/` with a `parked — ` reason. It exists for unattended mode's hard
|
|
110
|
+
stops — see [Skills and platforms](skills-and-platforms.md) — where the agent works in a worktree but
|
|
111
|
+
the item's status lives in the primary checkout.
|
|
112
|
+
|
|
113
|
+
Keep the raw idea count around a dozen. At the cap, promote or drop before capturing anything new. A
|
|
114
|
+
completed instance queues **at most two** follow-up items and records the rest in its own
|
|
115
|
+
`retrospective.md` as observed, not queued — the same cap, in the same shape, as the one on lessons.
|
|
116
|
+
Without it, closing an instance reliably grew the backlog: one project emitted five to nine items per
|
|
117
|
+
close and triage kept three of eighteen.
|
|
118
|
+
|
|
119
|
+
## Working the backlog unattended
|
|
120
|
+
|
|
121
|
+
Ask your agent to "do the next 3 items" (or "do all the items"), or run it yourself:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
aidlc autopilot run --items 3
|
|
125
|
+
aidlc autopilot run --all
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
It starts one background process and returns at once, printing its process id, its log file, and the
|
|
129
|
+
commands to watch and stop it. There is nothing to schedule. The run passes over the backlog every
|
|
130
|
+
minute (`--interval` to change it) and ends when the items it started are each released or parked —
|
|
131
|
+
or, with `--all`, when nothing is ready and nothing is in flight. A restart ends it; asking again
|
|
132
|
+
resumes.
|
|
133
|
+
|
|
134
|
+
Each item goes through these steps:
|
|
135
|
+
|
|
136
|
+
1. **Pick.** `aidlc roadmap next` names the next ready backlog item.
|
|
137
|
+
2. **Build.** `aidlc start … --worktree --roadmap-item <item>` creates the instance in its own
|
|
138
|
+
worktree, then the configured agent runs there with `AIDLC_UNATTENDED=1`. The agent follows the
|
|
139
|
+
unattended rule and stops at the release boundary, or parks the item on a hard stop.
|
|
140
|
+
3. **PR and CI.** Autopilot pushes the branch and opens the pull request. Green checks queue it;
|
|
141
|
+
a failure relaunches the agent once with the failing checks, and a second failure parks the item.
|
|
142
|
+
4. **Up to date with main.** Before merging, autopilot merges `origin/main` into the branch in the
|
|
143
|
+
item's worktree — locally, so the repository's `merge=union` rules apply — pushes, and waits for
|
|
144
|
+
CI on the new head. A conflict goes back to the item's agent to resolve, twice at most, then the
|
|
145
|
+
item is parked. A pull request with no checks that GitHub reports as conflicting is handled the
|
|
146
|
+
same way: GitHub runs no checks on a conflicting PR, so waiting for them would wait forever.
|
|
147
|
+
5. **Merge and release**, one at a time, never while a release workflow is running: merge the exact
|
|
148
|
+
commit CI passed (`--match-head-commit`), pull `main`, run the release step, then
|
|
149
|
+
`aidlc transition` to close the instance. A failure halts the queue. Running
|
|
150
|
+
`aidlc autopilot run` again retries a failure that came before the merge; from the pull onward
|
|
151
|
+
nothing is retried — a half-finished release is for a person.
|
|
152
|
+
|
|
153
|
+
At most two items build at once, and never more than you asked for. Autopilot never moves anything
|
|
154
|
+
between `inbox/`, `backlog/` and `hold/` except to park what it is building, and never edits a rank or
|
|
155
|
+
a dependency: deciding what is agreed work stays a person's job.
|
|
156
|
+
|
|
157
|
+
**Setup** comes first, once per machine: `aidlc autopilot setup` (or `aidlc init --autopilot`). It
|
|
158
|
+
asks four questions, each a list with one choice marked recommended:
|
|
159
|
+
|
|
160
|
+
- **The agent, and what it may do.** Claude Code limited to editing files and running git, pnpm,
|
|
161
|
+
node and read-only shell tools (recommended); Claude Code with full access; Codex in full-auto
|
|
162
|
+
mode; or a command you type. This decides what an unattended agent may do on your machine, so
|
|
163
|
+
it is never picked for you: with no terminal, setup needs `--agent <preset>` or
|
|
164
|
+
`--non-interactive`.
|
|
165
|
+
- **How many items build at once** — two (recommended) or one.
|
|
166
|
+
- **A cost cap per item** — an item past it is stopped and parked.
|
|
167
|
+
- **The release step.** In a project with `scripts/autopilot-release.sh`, running it after each
|
|
168
|
+
merge (recommended); otherwise merge only, and you release when you choose. This repository's
|
|
169
|
+
script runs `release-bookkeeping --write`, commits the manifest and `.aidlc`, and runs
|
|
170
|
+
`scripts/release.sh` with the bump bookkeeping printed.
|
|
171
|
+
|
|
172
|
+
Flags skip the questions: `--agent`, `--parallel`, `--cost-cap <usd|none>`, `--release
|
|
173
|
+
<repo-script|merge-only>`. The answers go to `<git-common-dir>/aidlc/autopilot/config.json`, per
|
|
174
|
+
machine and never committed, beside autopilot's `state.json`, `run.json`, `logs/` and `STOP` file.
|
|
175
|
+
|
|
176
|
+
**Watching and stopping.** `aidlc autopilot status` shows the active run and each item's step, pull
|
|
177
|
+
request, CI failure count and last error. `aidlc autopilot stop` (or `/aidlc-autopilot-stop`) ends the
|
|
178
|
+
run at its next step: nothing further is started, merged or released, and agents already running
|
|
179
|
+
finish their item. `aidlc autopilot forget <instance>` stops tracking an item you finished or
|
|
180
|
+
abandoned by hand; a pull request merged by hand is noticed and dropped on its own. A rate limit
|
|
181
|
+
from `gh` or the agent backs off five minutes, doubling to at most
|
|
182
|
+
two hours. `aidlc autopilot tick --dry-run` prints every command one pass would run and runs only the
|
|
183
|
+
reads.
|
|
184
|
+
|
|
185
|
+
## Projecting the roadmap onto GitHub Issues
|
|
186
|
+
|
|
187
|
+
Opt-in, and off until the config block exists. `aidlc roadmap sync` renders eligible items as
|
|
188
|
+
GitHub issues; `aidlc roadmap import <issue-url>` seeds one `inbox/` item from an existing issue.
|
|
189
|
+
|
|
190
|
+
`aidlc init` offers to write the block for you — it is the last question in the wizard, and it
|
|
191
|
+
defaults to no. Say yes and it lists all five statuses with every one checked, so unchecking
|
|
192
|
+
`inbox` and `hold` is a choice you make with the list in front of you rather than one made on your
|
|
193
|
+
behalf. `done` cannot be turned off; without it the close path never runs and the projection never
|
|
194
|
+
converges. Then it prints `aidlc roadmap sync --dry-run` and stops — nothing is published by
|
|
195
|
+
answering a setup question.
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
# .aidlc/config.yaml, in the PRIMARY checkout
|
|
199
|
+
roadmap:
|
|
200
|
+
sync:
|
|
201
|
+
provider: github # the only value in this version
|
|
202
|
+
repo: owner/name # optional; gh resolves the current repo when absent
|
|
203
|
+
statuses: [backlog, in-progress, done] # the default; must include done
|
|
204
|
+
label: aidlc # optional; the discovery label
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Writing it by hand stays supported, and re-running `aidlc init` never destroys it: the offer arrives
|
|
208
|
+
pre-answered *yes* when a section already exists, `repo` and `label` are preserved, and a section
|
|
209
|
+
that omits `statuses` pre-checks the three defaults rather than all five — so pressing Enter through
|
|
210
|
+
an update cannot start publishing your `inbox/`. Answering an explicit no removes the section and
|
|
211
|
+
says which keys went with it.
|
|
212
|
+
|
|
213
|
+
Two things `init` deliberately does not do. It does not hide the question when the GitHub CLI is
|
|
214
|
+
missing — that would make the wizard's contents depend on the machine it ran on, and initializing on
|
|
215
|
+
a laptop while syncing from CI is normal. It probes `gh` only after you say yes, warns with the
|
|
216
|
+
remedy, writes the section anyway, and exits 0. And it never runs the projection, not even
|
|
217
|
+
`--dry-run`: a fresh project has an empty roadmap, so the run would say nothing, and on a machine
|
|
218
|
+
with no `gh` it could not run at all.
|
|
219
|
+
|
|
220
|
+
`aidlc doctor` reports the matching condition — `roadmap.sync` configured and `gh` not on `PATH` —
|
|
221
|
+
and applies nothing. It probes presence only, never `gh auth status`, so an offline run reports
|
|
222
|
+
only what is true, and it is `report` tier rather than `confirm` for a specific reason: a confirm-tier
|
|
223
|
+
remover applies unprompted under `--yes`, and CI is exactly where `gh` is legitimately absent.
|
|
224
|
+
|
|
225
|
+
It is a **projection, not sync**. Disk stays the source of truth and the tracker is a rendering of
|
|
226
|
+
it, which buys four properties worth knowing about:
|
|
227
|
+
|
|
228
|
+
- **Nothing is written to disk.** There is no `remote:` field and no cache. Creating an issue and
|
|
229
|
+
writing a local link would be two writes to two systems, and every failure of the second leaves an
|
|
230
|
+
issue with no link that the next run turns into a duplicate. Instead the item `id` lives on the
|
|
231
|
+
issue, in an HTML-comment marker, so the projection can be thrown away and rebuilt.
|
|
232
|
+
- **`inbox/` and `hold/` are not projected by default.** The line falls between thinking and work,
|
|
233
|
+
which happens to be the same line that keeps parked pricing thinking and a competitor teardown off
|
|
234
|
+
a public, indexed, notification-generating board.
|
|
235
|
+
- **`done/` is never backfilled.** A shipped item gets an issue only if it already had one, so
|
|
236
|
+
turning this on does not create fifty closed issues nobody will read.
|
|
237
|
+
- **A closed issue is never reopened, and titles you improve on GitHub are overwritten.** Closing an
|
|
238
|
+
issue is the primary triage act of the person this feature is for, so `sync` reports the divergence
|
|
239
|
+
and leaves it alone. Titles follow disk.
|
|
240
|
+
|
|
241
|
+
### What the issue body contains
|
|
242
|
+
|
|
243
|
+
The body carries the item: its frontmatter facts, its whole prose, and links. Specifically a facts
|
|
244
|
+
block (status, created date, author, source, dependencies, promoted-to instance), a permalink to the
|
|
245
|
+
roadmap file, the item's body text, and the `aidlc-item` marker. So a reader with no checkout can
|
|
246
|
+
decide from the issue instead of being redirected to a file they cannot open.
|
|
247
|
+
|
|
248
|
+
Three things about it are worth knowing:
|
|
249
|
+
|
|
250
|
+
- **The projection owns one region, and only that region.** Everything between
|
|
251
|
+
`<!-- aidlc-body:begin -->` and `<!-- aidlc-body:end -->` is regenerated on every sync. Anything you
|
|
252
|
+
write above or below those markers is preserved, so an issue description stays usable as a place to
|
|
253
|
+
add a note. Comments are separate from the body and are never touched.
|
|
254
|
+
- **Editing the item on disk updates the issue on the next sync**, as an `update-body` write. Bodies
|
|
255
|
+
used to be written once at creation, which left every issue quietly contradicting its item as soon
|
|
256
|
+
as the item changed. Issues created before v1.22.0 get the new body on the next run.
|
|
257
|
+
- **Item prose is sanitized before publication, and the sanitizer is code-aware.** HTML comments are
|
|
258
|
+
stripped from prose (so an item that quotes a marker cannot hijack another issue), and `@name` and
|
|
259
|
+
`#123` spans in prose are wrapped in backticks so a sync notifies nobody and cross-links nothing.
|
|
260
|
+
Anything inside a fence or an inline code span is left byte-identical — measured over this
|
|
261
|
+
project's own roadmap, 40 of the 41 hazard-shaped spans are in code (`@clack/prompts`,
|
|
262
|
+
`actions/checkout@v4`, colour hexes such as `#0891b2`), so a code-unaware sanitizer would corrupt
|
|
263
|
+
forty spans to defuse one. Item ids in the prose become links to their item files; they are
|
|
264
|
+
deliberately *not* linked as `#N`, because that is the cross-reference being defused and it would
|
|
265
|
+
make the body depend on remote issue numbers.
|
|
266
|
+
|
|
267
|
+
Nothing volatile ever enters the body — no timestamp, no commit sha, no metrics. A body containing
|
|
268
|
+
the current time would differ from the stored copy on every run, so every item would plan a write
|
|
269
|
+
forever and the projection would never converge.
|
|
270
|
+
|
|
271
|
+
Writes always need consent: `--dry-run` prints the plan and touches nothing, an interactive run asks,
|
|
272
|
+
and a non-interactive run without `--yes` prints the plan and exits **non-zero** — a green run that
|
|
273
|
+
wrote nothing is how an automation comes to believe it is syncing when it never was. The plan names
|
|
274
|
+
the resolved repository before the first write, which is the only moment a projection aimed at the
|
|
275
|
+
wrong repo is still reversible.
|
|
276
|
+
|
|
277
|
+
An item that has left a projected status keeps its open issue and is reported every run;
|
|
278
|
+
`--retire-ineligible` closes those. Nothing here installs a lifecycle action: wrapping `sync` in CI is
|
|
279
|
+
a user-side step, and a shipped action would start firing in every project on the next
|
|
280
|
+
`aidlc update`.
|
|
281
|
+
|
|
282
|
+
### Moving a Project board
|
|
283
|
+
|
|
284
|
+
Put the issues on a GitHub Project and they all land in one column. That is the projection stopping
|
|
285
|
+
where it should: setting a Project's Status field needs a token with `project: write`, and the
|
|
286
|
+
workflow-provided `GITHUB_TOKEN` cannot do it at any permission level. The tool would need every
|
|
287
|
+
project to create and store a token before it could move a board, so it does not try.
|
|
288
|
+
|
|
289
|
+
The other half ships as a file:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
aidlc roadmap board-workflow > .github/workflows/aidlc-project-status.yml
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The five setup steps print to the terminal as it runs; only the workflow goes into the file.
|
|
296
|
+
Do them in the order shown: label changes made before the token and project number exist are
|
|
297
|
+
skipped and never replayed.
|
|
298
|
+
|
|
299
|
+
That prints a GitHub Actions workflow which, on every issue event, reads the issue's current
|
|
300
|
+
`aidlc:<status>` label and sets the Status field to match. Its header has the setup: create the
|
|
301
|
+
Status columns (or edit its `STATUS_MAP` to name the ones you have), store a classic token with the
|
|
302
|
+
`project` and `read:org` scopes — plus `repo` for a private repository — as the secret
|
|
303
|
+
`AIDLC_PROJECT_TOKEN`, and set the variable `AIDLC_PROJECT_NUMBER`. A renamed discovery
|
|
304
|
+
label goes in `AIDLC_DISCOVERY_LABEL`. It needs `gh` 2.97.0 or later on the runner and checks for it.
|
|
305
|
+
|
|
306
|
+
The tool prints the file and never writes it into `.github/workflows/`. The token is the real setup
|
|
307
|
+
cost, not the paste, and CI running in your repository with your token is yours to own.
|
|
308
|
+
|
|
309
|
+
**The labels are a supported interface.** The discovery label (`aidlc` unless `label` renames it)
|
|
310
|
+
and the five `aidlc:<status>` labels — `aidlc:inbox`, `aidlc:backlog`, `aidlc:in-progress`,
|
|
311
|
+
`aidlc:done`, `aidlc:hold` — may be bound to by automation. Renaming or removing one is a breaking
|
|
312
|
+
change and will be released as one. Each issue carries exactly one status label; the workflow refuses
|
|
313
|
+
an issue with two rather than guess which is right.
|
|
314
|
+
|
|
315
|
+
This repository runs the same file from `.github/workflows/project-status.yml`, and a test fails if
|
|
316
|
+
the two differ.
|
|
317
|
+
|
|
318
|
+
## Where the code lives
|
|
319
|
+
|
|
320
|
+
- `packages/content/skills/04-roadmap.md` — the capture, triage and promote protocol. This is the
|
|
321
|
+
authority for the item format and the three flows.
|
|
322
|
+
- `packages/cli/src/roadmap/sync` — the GitHub projection: `plan.ts` is pure and imports no
|
|
323
|
+
filesystem, no subprocess and not the `gh` seam, so planning is testable with no network at all;
|
|
324
|
+
`execute.ts` is the only writer and imports no `node:fs`; `gh.ts` is the single subprocess boundary,
|
|
325
|
+
argument-array only. `body.ts` renders the projection-owned region and merges it into an existing
|
|
326
|
+
issue; `sanitize.ts` defuses item prose; `markdown.ts` is the one place that decides what counts as
|
|
327
|
+
code, shared by the sanitizer and the marker scanner so the two cannot disagree.
|
|
328
|
+
- `packages/cli/src/commands/roadmap.ts` — the `sync`, `import` and `board-workflow` commands,
|
|
329
|
+
consent, and exit codes.
|
|
330
|
+
- `packages/content/resources/recipes/project-status.yml` — the board workflow, loaded by
|
|
331
|
+
`loadBoardWorkflow()` in `packages/content/src/recipes.ts`.
|
|
332
|
+
- `packages/cli/src/commands/init.ts` — the wizard step that offers the projection, plus the
|
|
333
|
+
`gh` warning and the removal notice. `packages/cli/src/setup/setup-compute.ts` owns the merge:
|
|
334
|
+
not-asked, yes, and an explicit no are three different answers, and collapsing any two of them
|
|
335
|
+
makes a re-run of `init` delete somebody's configuration.
|
|
336
|
+
- `packages/cli/src/doctor/migrations/roadmap-sync-gh-missing.ts` — the report-tier check. It
|
|
337
|
+
spawns nothing at all: the primary checkout comes from `ctx.gitCommonDir`, already resolved, not
|
|
338
|
+
from `primaryCheckout()`, which shells out to `git rev-parse`.
|
|
339
|
+
- `packages/content/resources` — a portable version of the item-authoring skill, loadable into any
|
|
340
|
+
agent, so a contributor with no checkout can write an item and commit it to `inbox/`.
|
|
341
|
+
- `packages/cli/src/operational` — the `roadmap-done` completion action. Its history is instructive:
|
|
342
|
+
it resolved the roadmap root to the primary checkout, so an instance finishing on a worktree branch
|
|
343
|
+
stranded its item, found nothing, printed nothing, and the transition reported success over it. An
|
|
344
|
+
action that can legitimately find nothing has to say so. Later it filed only the first of two items
|
|
345
|
+
promoted to one instance; it now files all of them and prints how many it moved.
|
|
346
|
+
- `.aidlc/roadmap` — this project's own items, including the ones that became the features documented
|
|
347
|
+
elsewhere in this folder.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Skills and platforms
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
The lifecycle is not code that runs — it is instructions an AI agent follows. So the framework's real
|
|
6
|
+
payload is prose, and the problem is that every agent platform wants that prose in a different place and
|
|
7
|
+
a different shape. Claude Code wants `.claude/skills/<name>/SKILL.md`. Cursor wants
|
|
8
|
+
`.cursor/rules/<name>.mdc`. Codex wants one `AGENTS.md`. Kiro wants skills plus a steering file.
|
|
9
|
+
|
|
10
|
+
The naive answer is to write the instructions once per platform. That answer fails on the second edit:
|
|
11
|
+
five copies of "how the design phase works" become five slightly different phases, and nobody can tell
|
|
12
|
+
which one is right.
|
|
13
|
+
|
|
14
|
+
So there is exactly one canonical body per skill, and a **platform adapter** that packages it. The seam
|
|
15
|
+
is *platform-neutral content, platform-specific packaging*. Content never knows which platform it is
|
|
16
|
+
going to; adapters never contain instructions.
|
|
17
|
+
|
|
18
|
+
**What breaks without it:** the same instruction drifts across five files, and a fix applied to one
|
|
19
|
+
platform silently leaves the other four wrong. This is the same single-source argument as the glossary
|
|
20
|
+
and the guidance layers, and it is the reason guidance is referenced by skills rather than pasted into
|
|
21
|
+
them.
|
|
22
|
+
|
|
23
|
+
## How to use it
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
aidlc init
|
|
27
|
+
aidlc update
|
|
28
|
+
aidlc add
|
|
29
|
+
aidlc docs
|
|
30
|
+
aidlc explain [topic]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`explain` prints these pages from the installed package: bare, it lists the topics; with a topic id, or
|
|
34
|
+
a command a page covers (`aidlc explain claim`), it prints that page. Nothing is copied into your
|
|
35
|
+
project, so there is no second version to drift.
|
|
36
|
+
|
|
37
|
+
`init` installs skills for the platforms listed in `.aidlc/config.yaml`. `update` refreshes them after
|
|
38
|
+
the framework changes, and also rewrites the managed block in `CLAUDE.md`.
|
|
39
|
+
|
|
40
|
+
**Anything you write inside that managed block is lost on the next update.** Put project instructions
|
|
41
|
+
above the start marker or below the end marker. This repo's own `CLAUDE.md` says so at the top, and the
|
|
42
|
+
"Lessons That Keep Costing Us" section sits deliberately outside the block for exactly that reason.
|
|
43
|
+
|
|
44
|
+
**Run the workspace build, not the installed one.** During development, invoke
|
|
45
|
+
`node packages/cli/dist/cli.js update`, never `npx aidlc update`. `npx` prefers a globally installed
|
|
46
|
+
binary, and it once reported "0 files updated, 35 unchanged" while the edited files sat changed on disk;
|
|
47
|
+
the same command through the built path reported 7. A verification step that reads a different artifact
|
|
48
|
+
than the one you changed fails green.
|
|
49
|
+
|
|
50
|
+
Six adapters ship today — Claude Code, Kiro, Cursor, Codex, Windsurf and GitHub Copilot. Each
|
|
51
|
+
implements one method, `plan(skills, caps, ctx)`, returning an install plan. All of them also
|
|
52
|
+
copy the canonical bodies to `.aidlc/skills/`, which is what lets the framework work with no CLI present
|
|
53
|
+
at runtime: the skill files on disk are self-sufficient.
|
|
54
|
+
|
|
55
|
+
Platform **capabilities** are a separate axis, declared in `packages/content/capabilities`. These are
|
|
56
|
+
per-platform features a phase can opt into — plan mode during ideation, for instance — keyed by platform
|
|
57
|
+
and phase. Note the vocabulary collision: "capability" in that directory means a platform feature, not a
|
|
58
|
+
subsystem of this repo. These pages are organised by **topic** for that reason.
|
|
59
|
+
|
|
60
|
+
Those files also carry a **top-level `questions:` key**, outside the phase-keyed `capabilities:` map, and
|
|
61
|
+
the reason it sits outside is not cosmetic. Capability resolution returns exactly one activation string
|
|
62
|
+
per phase, and three platforms already occupy `requirements` and `design` — the two phases questions
|
|
63
|
+
actually get asked in. A second entry for those phases would silently lose to the first, so question
|
|
64
|
+
rendering could not live there at all.
|
|
65
|
+
|
|
66
|
+
Every question opens with **what it is**: the thing being decided, in plain words, on one concrete
|
|
67
|
+
example. The emitted rendering file states this once, under `## Every platform`, and every directive
|
|
68
|
+
names the line again where it lists what a prompt contains. That looks like duplication and is not. An
|
|
69
|
+
agent follows the most specific list it has, and five directives spell out the prompt's contents in
|
|
70
|
+
order, so a rule stated only in the shared section would be dropped exactly where it is needed. The
|
|
71
|
+
option half of the rule exists because Claude Code shows an option's consequence as its whole
|
|
72
|
+
description: written in schema terms, the prompt was unreadable even when the question file was fine.
|
|
73
|
+
The first real test (fieldmodel-apps, 2026-09-26) had all four questions declined as incomprehensible;
|
|
74
|
+
rewritten with a primer and one running example, all four were answered, three against the
|
|
75
|
+
recommendation.
|
|
76
|
+
|
|
77
|
+
**Not everything that emits a file is an adapter.** Two emitters sit beside the adapter pass rather than
|
|
78
|
+
inside it — the roadmap authoring skill and the per-platform question rendering. Both are excluded for the
|
|
79
|
+
same two reasons. Adapters re-serialize frontmatter from a fixed key whitelist, so any key they do not
|
|
80
|
+
already know about is dropped on round-trip; and an adapter packages one body for one platform, whereas
|
|
81
|
+
these write one file carrying every configured platform's section, so a developer running two agents
|
|
82
|
+
against one checkout reads one file instead of guessing which of two applies. If you are tempted to add
|
|
83
|
+
an adapter to emit a file, check which of those two shapes you have first.
|
|
84
|
+
|
|
85
|
+
If you are adding an operational skill of your own, reuse this seam rather than writing platform files
|
|
86
|
+
directly.
|
|
87
|
+
|
|
88
|
+
**Unattended mode.** Every skill assumes a person is there to answer. With `AIDLC_UNATTENDED=1`, an
|
|
89
|
+
agent follows `.aidlc/resources/rules/unattended.md` instead: each place a skill would ask, wait or
|
|
90
|
+
confirm has one written outcome — take the named default and log it to the instance's
|
|
91
|
+
`unattended-stops.md`, or hard-stop (constitution, deleting data the session did not create,
|
|
92
|
+
credentials and permissions, scope beyond the roadmap item), which parks the item and ends the session.
|
|
93
|
+
Completing testing is the normal end: the agent writes the release plan and stops, and whatever runs
|
|
94
|
+
it owns the push, merge and release. The list of stops came from two dry runs on 2026-10-02 that
|
|
95
|
+
logged the same thirteen on two machines. The mode is a rule the agent reads, not a switch the CLI
|
|
96
|
+
reads — no command behaves differently when the variable is set, because a branch in every command
|
|
97
|
+
would be a cost every future change pays.
|
|
98
|
+
|
|
99
|
+
## Where the code lives
|
|
100
|
+
|
|
101
|
+
- `packages/cli/src/compile` — the compiler and the adapters. `adapters/index.ts` is the registry; each
|
|
102
|
+
adapter file maps one canonical body to one platform's packaging.
|
|
103
|
+
- `packages/content/skills` — the canonical bodies, numbered by lifecycle order (`00-overview.md`
|
|
104
|
+
through `90-retrospective.md`). This is the only place instructions are authored.
|
|
105
|
+
- `packages/content/capabilities` — per-platform capability declarations, plus each platform's
|
|
106
|
+
`questions:` rendering.
|
|
107
|
+
- `packages/cli/src/compile/question-rendering.ts` — emits `.aidlc/resources/questions/rendering.md`, the
|
|
108
|
+
one file holding how each configured platform puts a question in front of the user. The protocol it
|
|
109
|
+
serves is in `packages/content/resources/rules/question-protocol.md` and names no platform tool
|
|
110
|
+
anywhere; that split is what stops the question shape from being written for one platform's UI.
|
|
111
|
+
- `packages/content/resources/rules` — the six rules skills send readers to (the context rule, the
|
|
112
|
+
always-read rule, the constitution rule, the Question Protocol, Creating an Instance, Lifecycle or
|
|
113
|
+
Skill?), one file each. `packages/cli/src/compile/rules.ts` emits them to `.aidlc/resources/rules/`
|
|
114
|
+
on `init` and `update`. They used to be sections of the overview, and following one cross-reference
|
|
115
|
+
cost the reader the whole 24KB file; now it costs the rule. The overview keeps a one-line pointer per
|
|
116
|
+
rule and states none of them. On Cursor the overview is always applied, so its size is also what every
|
|
117
|
+
turn carries.
|
|
118
|
+
- `packages/content/glossary.yaml` — canonical vocabulary, resolved into skill bodies where they write
|
|
119
|
+
`{{glossary:<term>}}`. Deliberately not installable into a project: guidance is yours to edit,
|
|
120
|
+
framework vocabulary is not, and a second copy drifts.
|
|
121
|
+
- `packages/cli/test/skill-code-contract.test.ts` — the guard that keeps prose and code honest. It
|
|
122
|
+
asserts that every artifact filename a skill mentions is one some template actually requires, and that
|
|
123
|
+
every `aidlc <cmd>` a skill mentions resolves on the real command tree. Skills claiming commands that
|
|
124
|
+
do not exist is a class of defect this closed.
|
|
125
|
+
- `packages/content/test/skill-prose.test.ts` — forbids internal type names standing in for English in
|
|
126
|
+
text a user reads.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# State, claims and concurrent work
|
|
2
|
+
|
|
3
|
+
## Why this exists
|
|
4
|
+
|
|
5
|
+
Two agent sessions in one checkout do not collide on state. They collide on **git**.
|
|
6
|
+
|
|
7
|
+
That sentence cost this project real money to learn. The intuition is that the danger is two sessions
|
|
8
|
+
writing the same YAML file, so the obvious fix is a lock on the state. It is the wrong fix, because
|
|
9
|
+
the actual damage happens elsewhere: one session runs `git add -A` and stages whatever the other
|
|
10
|
+
session left on disk. Creating a branch in place yanks the other session off its branch. And on
|
|
11
|
+
2026-08-26 both live instances lost their cost data entirely — the recorder saw overlapping claims,
|
|
12
|
+
found `6 candidate records — ambiguous`, and attributed nothing to either.
|
|
13
|
+
|
|
14
|
+
So there are two mechanisms here, and they answer different questions.
|
|
15
|
+
|
|
16
|
+
A **claim** is a lock saying which session is currently working on an instance. It is not an
|
|
17
|
+
assertion, a ticket, or an expense claim. It exists so that the ownership gate can tell "the session
|
|
18
|
+
entitled to advance this phase" from "some other session", and so the cost recorder knows whose
|
|
19
|
+
tokens these were.
|
|
20
|
+
|
|
21
|
+
A **worktree** is the answer to the git problem. Two sessions get two checkouts of one repository,
|
|
22
|
+
each on its own branch, sharing one `.git`. Nothing either one stages can surprise the other.
|
|
23
|
+
|
|
24
|
+
**What breaks without them:** cost becomes unattributable, and one session's `git add -A` commits the
|
|
25
|
+
other's half-finished work. Neither failure announces itself — you find out at the retrospective when
|
|
26
|
+
the numbers are null.
|
|
27
|
+
|
|
28
|
+
## How to use it
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
aidlc claim my-thing
|
|
32
|
+
aidlc release my-thing
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Claims live in `<git-common-dir>/aidlc/claims/`, not in the instance folder, so they are shared
|
|
36
|
+
across worktrees of the same repository. A claim carries `claimed_at`, `renewed_at`, the session id,
|
|
37
|
+
and the `worktree_path` it was taken from. It goes **stale** after `claim_timeout_minutes` (default
|
|
38
|
+
60, set in `.aidlc/config.yaml`) — a long conversation will outlive its own claim, and re-claiming
|
|
39
|
+
renews it.
|
|
40
|
+
|
|
41
|
+
Note the wrinkle, because it will bite you: `owner` in a phase file is a *label*, not a lock. Nothing
|
|
42
|
+
in the CLI writes it. The ownership gate resolves the current actor from the live claim, falling back
|
|
43
|
+
to `$USER` when no claim is live. So a phase file whose `owner` was hand-written to one identity,
|
|
44
|
+
plus a claim that has since gone stale, produces a gate failure that names two identities and blames
|
|
45
|
+
the wrong one. Re-claim rather than reaching for `--override`.
|
|
46
|
+
|
|
47
|
+
For concurrent work, use a worktree:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
git worktree add -b feat/20260907-my-thing ../aidlc-my-thing main
|
|
51
|
+
aidlc start quick-feature --name my-thing --worktree
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The `--worktree` flag does this for you, including the branch-prefix convention (`fix/` for bugfix,
|
|
55
|
+
`chore/` for micro-task and spike, `feat/` otherwise). When another live session holds a claim, the
|
|
56
|
+
entry-point skill offers exactly three options — separate worktree, stop, or continue with no code
|
|
57
|
+
changes — and takes none of them without being told.
|
|
58
|
+
|
|
59
|
+
The hook declarations are tracked, but their capture-shim target is machine-local and gitignored.
|
|
60
|
+
`aidlc start --worktree` now regenerates that shim in the new checkout before returning,
|
|
61
|
+
using the same helper as `aidlc doctor`; a real worktree test keeps the raw, unarmed Git path as its
|
|
62
|
+
negative control. If you create the worktree with `git worktree add` yourself, run `aidlc doctor`
|
|
63
|
+
inside it before starting the agent session.
|
|
64
|
+
|
|
65
|
+
## Where the code lives
|
|
66
|
+
|
|
67
|
+
- `packages/cli/src/concurrency` — the claim store, staleness logic, and worktree helpers.
|
|
68
|
+
`claim-store.ts` is the single place a claim is written or read.
|
|
69
|
+
- `packages/cli/src/state` — instance folders, `phase-<name>.yaml`, and the append-only
|
|
70
|
+
`transitions.log`.
|
|
71
|
+
- `packages/cli/src/commands/claim.ts` — the `claim` and `release` commands, which also write the
|
|
72
|
+
session intervals that cost attribution later reads.
|
|
73
|
+
- `packages/cli/src/commands/transition.ts` — `getActorIdentity`, the function that decides whose
|
|
74
|
+
turn it is. Its docblock records both 2026-09-01 reproductions where comparing a Unix username
|
|
75
|
+
against an `agent-<uuid>` session id blocked the one session entitled to proceed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rasensio/aidlc-content",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.51.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"tutorials/",
|
|
23
23
|
"howto/",
|
|
24
24
|
"resources/",
|
|
25
|
+
"docs/",
|
|
25
26
|
"glossary.yaml"
|
|
26
27
|
],
|
|
27
28
|
"engines": {
|
package/skills/00-overview.md
CHANGED
|
@@ -133,7 +133,7 @@ An instance's **completion moment** is when the last applicable phase (template
|
|
|
133
133
|
|
|
134
134
|
- Project context lives in `.aidlc/context/` (style-guide.md, architecture.md)
|
|
135
135
|
- Guidance layers live in `.aidlc/guidance/` — load only those listed in `index.yaml`
|
|
136
|
-
-
|
|
136
|
+
- Why AIDLC works as it does: `aidlc explain` lists topics, `aidlc explain <topic>` prints one
|
|
137
137
|
|
|
138
138
|
### The always-read rule
|
|
139
139
|
|