Build your knowledge base

What the generator produces

Section by section, what lands in the API reference, the design notes and the fleet page.

Section by section, what lands in each of the three documents contextlake kb docs writes, and the rule each one follows about what it will not claim. Read this to know whether a page you are looking at is stating a fact, quoting evidence, or telling you it cannot say.

Document Where it lands What it answers
Reference one per repository What is defined here, and who calls it?
Design notes one per repository What did this repository commit to, and what is not recoverable?
Fleet page one per store Where does the fleet disagree with itself?

What a reference contains#

Every entry names the symbol, its kind, the line it is defined on, and its call sites:

### `Config.from_prefixed_env`

*method, defined at line 258.*

**7 call site(s)** across **4 caller(s)**:

| Caller | File | Line | Source |
| --- | --- | --- | --- |
| `Flask.__init__` *(method)* | `src/flask/app.py` | 211 | `self.config.from_prefixed_env()` |
| `test_from_prefixed_env` *(test)* | `tests/test_config.py` | 96 | `app.config.from_prefixed_env()` |

Every line in that table is a line you can open.

The quoted source is proved, not assumed#

The Source column holds the actual line at the call site, which is what makes an entry an example rather than a pointer. It is also the one part of the document that could be confidently wrong: the graph's line numbers were recorded when the repository was indexed, and the working tree may have moved on since.

So a line is quoted only where it can be proved to be the line that was indexed, meaning the file has not been written since the index ran. Where it cannot, the cell says *changed since indexing* rather than showing today's line at that number. Where nothing in the repository can be quoted at all, the page says why once, near the top:

Re-running contextlake kb index restores the quotes, because it moves the stamp past the files.

Two numbers, not one#

Call sites and callers are different, and both appear because the difference matters. A function called fifty times from inside one loop has fifty call sites and one caller. If you are deciding whether a change is safe, you need to know which of those you are looking at.

A call site is sometimes attributed to a file rather than to a definition, which is what the graph records for a call at module level with no enclosing function. Those sites are listed, and they are not counted as callers, because a file is not something you can read to understand the call. The count says so explicitly when it happens:

**124 call site(s)** across **93 caller(s)**, 4 of which name no enclosing definition

Names carry their scope#

A symbol is shown with its owning type when the graph recorded one, so ostream.flush and detail.glibc_file.flush are distinguishable. Without that, a header-heavy C++ library produces pages with several identical headings in a row and no way to tell them apart.

Where the graph did not record a scope, the bare name is shown rather than a guessed one.

What the design notes contain#

The other document answers "what was chosen here", and the honest answer is narrower than the name suggests. A graph holds no decision records: it never sees what was rejected, or why. What it does hold is two kinds of evidence, and the page keeps them apart because they are not equally strong.

Recorded evidence is a manifest dependency. Somebody wrote blinker>=1.9.0 in a file on purpose, so the package, its constraint and its line are facts. Each commitment the repository's own manifest makes at runtime becomes a numbered entry:

### ADR-001: Depend on `blinker` at `>=1.9.0`

**Status:** proposed, never ratified.

**Decision.** `pyproject.toml:24` declares `blinker` with the constraint `>=1.9.0`,
required at runtime.

**Context.** *Nobody wrote this down. The repository records the choice and not the
reason, so what was weighed against it is not recoverable from the code.*

An entry states the choice and leaves the reasoning visibly absent rather than filling it with a generated guess. That is the whole difference between this and a decision record: a real ADR has Context and Consequences, and a graph can supply neither.

Only the recorded class is numbered, and only what the shallowest manifest commits to at runtime. A dev dependency is a contributor's convenience, an optional extra is opt-in, and a nested project's dependencies are that project's decisions. All of them stay recorded in the tables, one table per manifest with the shallowest first, because a bundled example that depends on this project is not a dependency of it.

"Shallowest" is usually the repository's own root manifest. In a monorepo with no top-level manifest it is whichever sub-project sorts first, which is a tie broken by path rather than by importance, so the entries there describe one sub-project and not the repository. The heading names the file it read, so the page is never wrong about whose commitments it lists, only narrower than the section title suggests.

The numbers are positions in a generated file, not stable identifiers: adding a dependency renumbers everything after it. The page says so, and each heading names its package so there is something stable to cite.

Package Constraint Group Line
blinker >=1.9.0 Required at runtime 24
asgiref >=3.2 Optional extra async 33
python-dotenv unpinned Optional extra dotenv 34

Inferred evidence is a constant read in many places. That is evidence the value is load-bearing and no evidence at all that anyone decided anything, so it stays a plain table: the count is printed and never explained, and no constant is ever numbered as a decision. On one measured tree, three of the seven constants that cleared the evidence bar were typing constructs, so numbering them would have produced "ADR-005: T is a repository-wide type variable" in a document whose entire claim is that it invents nothing.

Three things keep the page honest, and each exists because its absence produced a real wrong answer somewhere:

The page also carries a machine-readable marker, because whoever reads the file receives bytes rather than a rendered page, and a status stated only in prose is a sentence a summariser can drop:

<!-- contextlake:document=design status=proposed-never-ratified evidence=derived-from-code -->

Nothing on that page was ratified by anybody. It is a set of questions to confirm.

What the fleet page contains#

One page for the whole store, at <store>/docs/fleet/design.md. It answers the question no per-repo page can, because disagreement is invisible from inside a single repository: a service pinning >=2.5,<4 and another leaving the same package unpinned each look reasonable on their own page.

Package Repos Manifests Constraints in use (repos)
queuelib 12 14 >=2.0 (9), ==1.8 (2), unpinned (1)
webkit 7 7 unpinned (7)

Every population is a count of distinct repositories, and manifests are counted separately. That separation is not tidiness. Measured on a real four-repository fleet, one package had 11 dependency edges across 2 repositories, because one of them declares it in eleven manifests: its own plus ten bundled examples. Counting edges would have printed "11 repositories" onto a four-repository fleet, which is absurd at four and perfectly plausible at forty.

The page names which packages are pinned inconsistently and then explicitly declines to recommend anything, because a repository may pin tightly for a real reason and nothing in a graph can tell that from drift.

The denominator names the filter it came from: "3 of 15 packages required at runtime", plus how many appear only as development or opt-in dependencies. Without that, a fleet with 15 runtime and 200 development packages reads as a 15-package fleet.

Repositories absent from the tables are split by why, and named rather than counted: they declare only development or opt-in dependencies (a manifest was read), they declare nothing this reads, or their shard could not be loaded -- in which case the page knows nothing about them either way and says so instead of reporting them as declaring nothing.

It is written only when a run covers the whole store. kb docs <repo> skips it and says why: "3 of 15 packages are shared" is a claim about the whole store, and a reader has no way to tell a scoped page from a complete one. Only runtime and peer dependencies reach it; a dev dependency disagreeing is a lesser finding that would bury the one that matters.

See also#

Next steps