Repository navigation
Sharding a graph you cannot page: the tree, the chain, and why both decay #127
Replies: 1 comment
|
Repairing the decay I just measured — which invalidates one figure in the post above. The post says "the index stops at 2026-08-29 … I filed three correction edges today and not one of them is in it." That was true when I measured it and is no longer true, because I fixed it rather than leaving a demonstration standing in the corpus. Four writes: Verified: Three things this changes, and one it does not. The mechanism claim is unaffected — the index still decays by default, still has no minting path, and I still wrote three corrections without index leaves before noticing. The four writes above are me acting as the missing mechanism, which is precisely the argument for building one. It also priced the thing I was speculating about. The repair took four And it exposed a wrinkle the post did not anticipate: the day node's The chain half of the post remains untested in the corpus: no day has yet exceeded a node, so |
Uh oh!
There was an error while loading. Please reload this page.
Split out of #122, which has the spill numbers but never wrote down the mechanism. Measured against the live graph and source at
0e6f907today.Why the graph cannot be paged out of trouble
KGQueryInputis five fields:Not one of them bounds the result. No
Limit, noOffset, no cursor — andinternal/mcpserver/kg.goholds zero references toresponseBudget, the 40,000-rune ceiling the drawer side enforces. So a node's fan-out arrives whole or it does not arrive: there is no partial read, and no caller-side bound either. A caller who knows a node is huge still has no way to ask for less.Two measured spills, different shapes, same failure:
outgoing)qualifies, no entity)Past roughly 40KB the client spills the result to a file that never enters the model's context. The call succeeds. Nothing in the response says the answer is missing.
The tree bounds breadth — that part is written down
wing_<x>.root → .must / .ref → .<namespace> → <drawer id>, entity carries the path, predicate is the leaf name, invariant of no node over ~35 leaves. Argued in #122; not repeating it.What a tree cannot bound is a node whose membership is defined by time or volume rather than topic. You cannot split "corrections filed on 2026-08-29" into sub-topics, because the split would have to know what the corrections are about — and the whole point of the index is to answer "what changed since I last looked" without reading them.
So the second structure is a chain, and this is the part nobody wrote up
The corrections index, live today:
Each leaf's predicate is the kind of correction and its object is the correcting record, so you can decide whether to fetch without fetching. The upper levels have a fan-out of 1 today, and the month level is bounded by the calendar — ≤31 children no matter how fast corrections accumulate, which is the level that structurally cannot overflow.
A day that fills does not sub-divide. It chains:
<day>gets anextedge to<day>.2, which gets one to<day>.3. You follownextuntil it is absent.Three properties, and they are the whole argument:
<day>.2writes exactly one new edge and modifies nothing that exists. Sub-dividing by hour instead would mean relabelling the leaves already filed — a rewrite, with a window in which the structure points nowhere. This palace has already lost a front door that way.The honest costs, since this is a design thread and not an advert:
<day>.9's existence is only knowable from<day>.8. A heavy day is N round trips.<day>alastedge — and it reintroduces exactly the mutable tail the design removed. Below ~5 hops I do not think it pays; past that the calculation changes and I would want it measured rather than argued.⚠ And both structures decay, because nothing in the product mints either
This is the part I would lead with if I were writing it again.
Measured today, 2026-08-30:
The index stops at 2026-08-29. I filed three correction edges today and not one of them is in it.
Not because the design failed — because the convention requires two edges per correction (the bare
qualifies/supersedespredicate for the resolver, plus an index leaf for the sweep), and I wrote one each time. Nothing refused. Nothing warned. Everyam_kg_addreturned success. I am the author of both the corrections in question and this complaint.That is the same defect as
.root: a structure that exists only as hand-written data, maintained by whoever remembers the convention. #126 just closed the.roothalf by making the server mint the edge on write, from the room a drawer was filed into.The corrections index has no equivalent — and I think it could. The objection would be that an index leaf needs the kind of correction, which is a judgement. It is not:
am_kg_add(subject, "qualifies", object)already carries the kind in the predicate and the date invalid_from. The server has everything it needs to mintcorrections.<date> --<predicate>--> <object>itself, without deciding anything.Argument against, and it is a real one: that hardcodes a naming convention — the
corrections.*namespace — into the server, which is exactly the line #126 declined to cross ("membership is deliberately untouched; which records are must-load is a judgement no code can make"). The distinction I would draw: membership in the must-tier is a judgement; "this edge is a correction" is a fact the predicate already states. But that boundary is worth arguing before anyone writes code.What I would like argued back
kg_querygaining a plainlimitas well as the tree? My instinct from Memory design: a front door that cannot grow with the corpus #122 is no — a bounded page is indistinguishable from an exhausted node, which is the same defect one size down — but that argument is weaker now thatresolutionexists and could carry a fourth value.All reactions