The Lunenburg Budget Project·The database

Lunenburg Budget Project · lineage graph · 3 September 2026

Which questions the data can answer, and why the rest cannot be

A lookup needs one key. A connectivity question needs two sides brought together, and those are the ones that fail — in three different ways, only one of which a schema diagram can show. Click a question to trace it.

Question Key Table Extract Report click a question to trace it · scroll sideways for the full chain
Seven questions reach a report. Six stop — five because no field anywhere carries the link, and one because the column is on both sides and the values never meet.
Click any question node to trace it back to the reports that answer it.

How to read it

The chain is always the same five steps, and each one is a different kind of thing:

question → key → table → extract → report

The columns are those five steps, in that order, left to right. Positions are computed once by scripts/build_lineage_graph.py — nodes are ordered within each column by the average height of what they connect to, swept both directions until the crossings settle — so the picture is the same every time you open it. A force simulation lands somewhere different on every load, and a position that means nothing still looks like it means something.

Two of those links are derived and one is assigned, which matters if you are going to trust it. Table → extract is read out of build_db.py’s own rows(...) calls; table → key and the field lists are read from the live schema. Extract → report is our labelling — which Town report produced a given CSV is not recorded anywhere machine-readable, so it is written in one place in scripts/build_lineage_graph.py rather than implied across a dozen documents.

Three ways a connection fails

Worth separating, because they call for completely different responses — and because only the first is visible in a schema.

1 · One side carries the key, the other never did

Ask for a document. The grants list names 79 awards and never says which account paid them. The budget prints no account number, ever. Nothing is broken; the link was never recorded, and a report could record it.

Questions stuck here: did a grant pay for these staff · which school does this spending belong to · which budget line does a revolving fund pay for.

2 · The column is on both sides and the values never meet

The most deceptive one. fund is a column on fund_activity and on account, so every schema diagram draws a join there. The values are disjoint: the school’s revolving funds are 1300–1303, the ledger we hold carries 5000–6200, and the intersection is zero of 61. The join is expressible, runs without error, and returns nothing.

This is not a missing key. It is a missing report — the ledger for the school’s own funds — and it looks like neither until you intersect the values.

3 · No field anywhere carries the link, and none can

Do not ask. “What did state aid pay for?” and “which categories does state aid pay for?” both need a tie from money in to money out. State aid arrives as unrestricted revenue into the general fund; 29 state-aid revenue accounts share zero organisation codes with any expense account, because no municipal ledger records which dollar paid which bill.

What can be said instead: how much came in (CH 70 AID — $6,870,136), what the general fund spent, and what the town had to raise after aid. Those are real answers to a nearby question, and they are the honest substitute for one nobody can answer.