v1.32.0 ·
The file that only grew
The problem
.aidlc/guidance/lessons.md is 90,285 bytes (88.2KB, ~22k tokens) across 23
instance sections. Eight phase skills mandate reading all of it:
10-ideation, 20-requirements, 30-design, 40-implementation, 50-testing,
60-deployment, 70-maintenance, 80-review. Once read it sits in the prefix for the
whole session.
The always-read guidance set totals 103.6KB today; lessons.md is 85% of it.
The consolidation trigger cannot fire on this: it fires at 25 sections or 90 days
since last_consolidated. The file sits at 23 sections, consolidated 12 days ago — both
triggers are still in the future while the file is already the largest thing every session
reads. Byte count is the dimension that costs money and the only one not measured.
How it could be solved
Five decisions, and in four of them the tempting answer was the more general one.
The first was where the always-read distinction lives. A layer could declare in its own frontmatter that it is too big to read on entry, which reads naturally and is wrong: whether a session opens a document is a property of how the project registered it, not of what the document says about itself. Two projects can reasonably disagree about the same file. Putting the flag on the registration also means a project can make a shipped layer on-demand without editing its body, and editing a shipped body marks it locally modified, which freezes it out of every future update. The same reasoning was already written down for the neighbouring rendering flag; following it cost nothing and kept one convention instead of two.
On the shape of the flag, a string setting with two named values was rejected in favour of a boolean that defaults by being absent. The string is more expressive and would read better in a file somebody edits by hand. It also needs a default decided at four separate read sites, and every existing project's index would have to be interpreted against that default. A boolean whose absence means the old behaviour gives backward compatibility for free rather than by argument, and matches the flag already sitting on the same entry.
On delivery, the obvious move was to raise the size threshold that decides whether a guidance layer is copied into a compiled skill or merely pointed at. The new digest is deliberately small, which puts it under that threshold and therefore in line to be copied into all eighteen skills — exactly the outcome the whole change exists to avoid. Raising the threshold would have fixed this file by changing delivery for every layer in every project. Marking one layer instead is a local statement with a local consequence. Padding the file past the threshold was considered and rejected outright: a guarantee that holds because a file happens to be big enough is the failure mode the existing flag's own comment was written about.
On what the framework ships inside the digest, the answer was nothing. A distilled digest is only useful if it carries the lessons a project has actually paid for, and those are project data. Shipping one project's retrospective observations to every consumer is wrong on content grounds, and it would also mark the file locally modified in every project that ever regenerates it, cutting that project off from future updates to the surrounding text. The cost is admitted rather than hidden: a fresh project's always-read lessons layer is a stub until its first consolidation. A stale digest of somebody else's project would be worse.
The fifth decision was not to build a command. The acceptance sketch asked that the full corpus stay reachable and that its path be stated where a reader would look for it. A new command to serve a digest, or to list layers and their sizes, is the kind of thing that sounds like completeness and is really a new surface to maintain — and this project's own guidance holds features that watch the lifecycle to a higher bar than features that serve a user, precisely because each one taxes every future piece of work. What shipped instead is a path named in the canonical rule and two extra lines on a status notice that already existed.
One more decision is worth stating because the code for it does not exist. Preserving the new flag across every path that rewrites the registration file looked like it needed defensive code in three places. Reading those three places showed all of them already preserve unknown keys, each with a comment explaining which near-miss taught them to. So the requirement shipped as tests that pin the existing behaviour rather than as a fourth copy of a rule that already held. That is recorded as an explicit design decision, because a reviewer finding no implementation for a stated requirement is entitled to know it was a choice.
How AIDLC solves it
The lessons corpus stops being something every session reads whether it needs to or not.
.aidlc/guidance/lessons.md had reached 90,285 bytes across 23 instance sections, and eight
phase skills mandated reading all of it on entry. It was 85% of every byte a session paid for
before touching task work. Meanwhile the consolidation trigger fired at 25 sections or 90
days, so at 23 sections and 12 days after a consolidation both triggers were still in the
future while the file was already the largest thing resident. Byte count was the dimension
that cost money and the only one nothing measured.
A guidance index entry may now carry on_demand: true, which takes the layer out of the
set a phase reads on entry. The layer stays registered, active, and current — it is simply
read when a phase has a stated reason to rather than by default. Absent or false means
always-read, so every existing index keeps behaving exactly as it did.
In its place the content package ships lessons-digest.md: a distillation under four
kilobytes, registered as always-read, delivered by pointer so it can never be copied into a
compiled skill body. The shipped file is a stub on purpose, because lessons are project data
and a framework package has no business handing one project's observations to every
consumer. The retrospective fills it at consolidation, and what it leaves out is confirmed
entry by entry — distilling a safety mechanism is fine, and quietly dropping it is not.
Consolidation gained a third trigger at twenty kilobytes, independent of the two that
already existed. aidlc status names the size in its notice and separately reports the
always-read total when that total passes the same budget. Both are notices and neither gates
anything; the point is that the number stops being invisible.
Eight phase skills now read the always-read set rather than every layer, and the rule itself
lives in one place — a new section of aidlc-overview that the eight point at instead of
restating. A doctor migration moves existing projects across, at confirm tier because it
changes what every future session reads.
The result on this repository: always-read guidance fell from 103.6KB to 16.9KB, an 84% reduction, with three and a half kilobytes of headroom under the budget. The full corpus is one read away by a path the overview names.