Context Window

Every lesson in this course builds one system: a stablecoin reserve attestation registry that discovers issuers, reads the PDFs they publish, and refuses to publish a field it could not read. What it is and where this lesson sits.

Tier
1 Noob
Build position
5
Throughlines
A Context economics · B Instruction packaging · C Delegation topology
Verdict
load-bearing
Commit
0794a06
Demo
scripts/terms/demo-02.ts
Runs in
the course repository

The constraint every later term is a workaround for. Sub-agents exist because one window is not enough. Caching exists because refilling one costs money. Progressive disclosure exists because filling it with things you did not need is how you run out. Teach the limit and half the curriculum stops looking like a list of tricks and starts looking like one problem seen from five angles.

The failure it fixes

The window is finite, so at some size your input stops fitting. That part is obvious and nobody gets hurt by it, because a hard error is a good error.

The failure that hurts is the one where it nearly fits. Something upstream trims to make room, the request succeeds, the model answers, and the answer is fluent and specific and about a document that was missing its last third. In this domain that third is often the composition table, because attestation PDFs put the summary first and the breakdown last. You publish a reserve figure with a page citation, and the citation is now a lie: the page exists, the model never saw it.

Nothing in the response tells you this happened. Truncation upstream and a confident answer downstream look exactly like a correct run. The only defence is to make the trimming step report what it removed, and to decide deliberately whether that report is a warning or a stop.

Break it first

Pack the real issuer universe into a budget that cannot hold it, and use a packer that returns only what fit:

$ npx tsx scripts/terms/demo-02.ts
domain: local-docs (a folder of text documents)

30 items, 997 tokens to send them all

budget   747 ( 75%)  kept 22/30  dropped  8  lost: units/14-skills,units/17-background-agents,...
budget   498 ( 50%)  kept 15/30  dropped 15  lost: units/04-projects,units/06-cli,...
budget   249 ( 25%)  kept  7/30  dropped 23  lost: units/13-git-worktrees,front-matter/20-overlap-matrix,...
budget    99 ( 10%)  kept  2/30  dropped 28  lost: front-matter/60-verdicts,units/02-context-window,...

The curve is 30 → 22 → 15 → 7 → 2, and it was 30 → 22 → 15 → 7 → 3 when this ran against stablecoin issuers instead. That near-identity is worth naming rather than glossing: with records of roughly uniform size, the number kept is close to the budget fraction, so this curve is mostly arithmetic. It is a weaker figure than it first appears. What makes it worth printing is not the shape but the lost: column, which is the thing a run without it never tells you.

Now imagine those lost: lines were not printed. You would have a run that reported "30 issuers discovered" at the top and classified seven of them, with nothing in between saying so. That is the shape of the bug, and it is invisible by construction.

The mechanism

src/context.ts makes one decision, and it is not "does it fit". It is: when it does not fit, what leaves, and does the caller find out.

pack() fills a budget by priority and returns two lists. included is what fit, in input order, because packing decides inclusion and must not silently reorder a document. dropped is everything else, each entry carrying a reason.

There are two reasons and the distinction matters. over-budget means the chunk lost a competition and a bigger budget would have kept it. single-chunk-too-large means it exceeds the whole budget and no amount of trimming elsewhere will ever help. Collapsing those into one reason sends the caller looking for something to delete when nothing would work.

Ties in priority break by input order, so packing is deterministic. A packer whose output depends on Array.prototype.sort stability across engines is a packer that will one day produce two different contexts from one input.

packOrThrow() is the default for extraction work. A reserve figure read from a silently trimmed document is worse than no figure, because absence is honest and a wrong number with a citation is not.

Build it

Priority here is circulating supply: if something must go, lose the small issuer. That is a policy choice, not a property of the packer, and it is recorded in the artifact rather than buried in a sort comparator where nobody will find it. Someone will disagree with it, and they should be able to find it in order to disagree.

The receipt

artifacts/02-context.json carries the full drop curve, the identities of every dropped issuer at each budget, and the refusal message packOrThrow produces at fifty per cent.

Ten tests. Two failed on first run, and the tests were wrong rather than the code: they hard-coded budgets from my mental model of the estimator, and word12 costs two tokens rather than one. Budgets are now derived from measured cost. A test whose setup encodes a guess about the code under test fails for reasons that have nothing to do with the behaviour it is checking, and it fails confusingly.

Where it overlaps

Line A is context economics, and this is the constraint the whole line escalates from. Line B is instruction packaging: the prompt occupies part of every window, which is exactly the tension interstitial I is about. Line C is delegation topology, and it starts here, because the first reason to delegate is that one window is not enough. Term 11, next in build order, measures what that delegation costs.

Sharp edges

  • The estimator is not the tokenizer. Every budget in this file is measured in estimated tokens, biased twenty per cent high per term 3. Packing is therefore conservative: it drops chunks that would have fit.
  • Priority is not importance. Circulating supply is a proxy chosen for this demo. The largest issuer is not automatically the one whose attestation matters most, and a registry that only ever reads the top seven has a coverage problem it cannot see.
  • packOrThrow is right for extraction and wrong for summary. Refusing to answer at all is correct when a number will be published with a citation, and obstructive when a human just wants an overview.
  • A chunk that fits alone may never fit together. single-chunk-too-large is computed against the whole budget, not the remaining budget, so a chunk can be reported over-budget on one run and included on another with the same budget and different neighbours. That is correct, and it surprises people.
What a budget drops, and how quietly0102030what the run reports: 30 discovered30100%9972275%7471550%498725%249210%99issuers actually classifiedbudget as a share of the 997 tokens all 30 need · artifacts/02-context.json
Thirty documents need 997 tokens to send in full. Each smaller budget silently returns fewer of them: the run still reports thirty discovered.

A personal teaching project, in development. The registry it builds is a teaching artifact and is not an assurance opinion about any issuer. Not affiliated with or endorsed by any employer, or by any vendor whose tools it describes. Where a unit depends on a specific flag or path, it names the version it was verified against. All units.