v1.32.0 ·
The file that only grew
.aidlc/guidance/lessons.md had reached 90,285 bytes, about 22k tokens, across 23 instance sections. Eight phase skills told every session to read all of it on entry, and once read it sat in the prefix for the whole session. The always-read guidance totalled 103.6KB, and the lessons file was 85% of it.
The consolidation trigger could not fire. It fires at 25 sections or 90 days since the last consolidation, and the file stood at 23 sections, 12 days after one. Both triggers were in the future while the file was already the largest thing every session carried. Byte count was the one dimension that cost money and the one nothing measured.
What changed
A guidance index entry can now carry on_demand: true. That takes the layer out of the set a phase reads on entry. It stays registered and current, and is read when a phase has a reason to. Absent or false means always-read, so every existing index behaves as before.
In its place the content package ships a lessons digest file, under four kilobytes, registered as always-read and delivered by pointer, so it can never be copied into a compiled skill body. The retrospective fills it at consolidation, and what it leaves out is confirmed entry by entry. Distilling a safety mechanism is fine. Quietly dropping one is not.
Consolidation gained a third trigger at twenty kilobytes. aidlc status names the size in its notice and separately reports the always-read total when it passes the budget. Both are notices, and neither gates anything. A doctor migration moves existing projects across, at confirm tier, since it changes what every future session reads. The rule itself lives in one section of aidlc-overview that the eight skills point at.
Here, always-read guidance fell from 103.6KB to 16.9KB, an 84% reduction, with three and a half kilobytes of headroom. The full corpus is one read away, by a path the overview names.
Why this way
In four of the five decisions the tempting answer was the more general one. A layer could declare in its own frontmatter that it is too big to read on entry. But whether a session opens a document is a property of how the project registered it, not of what the document says about itself. Putting the flag on the registration also lets a project make a shipped layer on-demand without editing its body, and editing a shipped body freezes it out of future updates. I made it a boolean whose absence means the old behaviour. A two-value string reads better but needs a default decided at four read sites.
I did not raise the size threshold that decides whether a layer is copied into a skill or only pointed at. The digest is deliberately small, so it would fall under that threshold and be copied into all eighteen skills, the very outcome this change avoids. Marking one layer is a local statement with a local consequence. I also rejected padding the digest past the threshold, since a guarantee that holds because a file happens to be big enough is the failure the existing flag's comment warns about.
I did not build a command to serve the digest. This project holds features that watch the lifecycle to a higher bar, because each taxes every future instance. What shipped is a path named in the rule and two extra lines on a status notice that already existed.
Preserving the new flag across every path that rewrites the registration looked like it needed defensive code in three places. All three already preserve unknown keys. So the requirement shipped as tests pinning existing behaviour, and I recorded that as a design decision, because a reviewer finding no implementation for a stated requirement is entitled to know it was a choice.
What it costs
The package ships no digest content. Lessons are project data, and shipping one project's observations to every consumer would also mark the file locally modified. So a fresh project's always-read lessons layer is a stub until its first consolidation. A stale digest of someone else's project would be worse.