D301 — A KB stays cheap to read and to maintain¶
Decision. Cartographer keeps a KB cheap for an agent, not only conformant.
A map may hand its concept list to the server (index: generated): the server
rewrites a marked block of its index.md after every write, in the same commit.
Read cost becomes visible: bytes on graph_context/graph_neighbors results,
kb_status.read_cost (concept and neighbourhood size percentiles over what the
caller sees), and result_bytes on audit completions. The two structural causes
of fan-out become kb_review kinds, repeated_fact and read_hotspot, ranked
after zombie_work. A link-only links-section item whose target already links
back is reciprocal_link_item, an opt-in mechanical fix. Thresholds are
constants pinned by tests, overridable per map (repeated_fact_min,
hotspot_in_degree, hotspot_bytes, oversize_bytes).
Why. On a large work KB the operator measured that one page update pulled in many others. Most of the write cascade was a single file: a journal index the agent re-read and re-sent in 38 % of sessions to add one line, because the KB's rule was "a new page also goes in its map's index". Reads cost more than writes: a page plus its neighbours was ~37 k tokens at the median and megabytes around the hubs. Duplicated facts and hand-written reverse links made each change touch N pages. The server already holds the data for all of this (graph cache, stat signatures, backlinks), so it can measure it and do the mechanical part. It does not judge content (D14).
This refines three decisions. D107: an index can be generated, but only inside a marked block of a map that opted in. Curated prose outside the block is never touched. D298: two new kinds, still candidates the doctor decides. D287: reciprocal items may go, but only one at a time, only when the reverse edge exists, and only when the operator asks. The section itself stays.
Costs: every write now reads the descriptors of all maps and rewrites generated
indexes whose bytes changed. The tool-description budget (D285) was met by
removing the enumerated kinds and fixable checks from the kb_review and
kb_repair descriptions: those lists grow every release, and the items and
findings already name them. A narrowed caller's read_cost is computed fresh on
every call. Percentiles over a filtered cache entry would still count hidden
concepts.
Alternatives rejected.
- Regenerate only the maps the write touched: this needs every handler to
report what it moved, and it never heals an index edited out of band.
Recomputing every generated map is in-memory work plus one small read per map.
- Let the server own the whole index.md: operators keep curated prose there,
and losing it would be the cost of opting in.
- A hand-maintained index with a smarter index_patch: the agent still reads
and re-sends the file. The cost is the round-trip, not the edit.
- Rewrite duplicated facts or split hotspots automatically: that is a content
judgement (D14), and the right owner of a fact is the operator's call.
- Drop reciprocal items in auto_repair by default: a reader of the raw file
loses the reverse link. That trade is the operator's (D287).
- Raise the tools/list budget to 23 KiB: the descriptions shrank without
losing a calling rule, so no new budget was needed.
- Log read sizes with content hashes or excerpts: the size alone measures
amplification, and it is safe to keep.
Consequences. A generated block's bytes depend only on the map's direct
concept set and their titles. A test pins that, and also that bytes outside the
block never change. index_patch refuses an edit inside the block, and
concept_move leaves a generated index to the server. index_stale replaces
index_incomplete for a generated map. result_bytes is the last field of the
canonical audit encoding and is omitted when zero, so logs written before it
verify unchanged. A future audit field must keep that property. Every new review
kind needs a fixture in reviewFixtures, and every new fixable check needs a
case in TestFixableChecksCoverEveryEmittedFix. reciprocal_link_item stays
out of conformanceChecks. The kb-doctor skill (2.1) proposes
index: generated and the reciprocal opt-in once per KB. Updating a KB's
instructions.md rules about indexes or reverse links is the skill's job, not
the server's.