Workflow Session ->
All posts
Heritage technology

What a heritage app should refuse to guess

Open heritage data gives you a point and a name. It does not give you meaning. Two schema decisions from HeriTrail about the difference.

In brief

What to take away

  • Open heritage datasets carry geometry, a name, and a statutory reference — not the meaning of a place, and not permission to narrate it.
  • A schema needs a state for 'we have not asked the right people yet', or the import will quietly fill that gap with something invented.
  • Cultural content should arrive at the client already structured, so the app renders it verbatim instead of reinterpreting it at display time.

HeriTrail is a heritage map of Aotearoa New Zealand. It puts what is known about a place where the place actually is: 366 documented sites, archival photographs pinned to the spot they were taken, and narrated trails through Tāmaki Makaurau, Pōneke, and Ōtautahi. The web map is live and free; the iOS and Android apps are in testing.

The map itself was the easy part. PostGIS has had this solved for twenty years — a geography(Point, 4326) column, a GiST index, a radius query, done. We had places rendering on a map in an afternoon.

The hard part started when we imported real data.

What open heritage data actually contains

Our Auckland import comes from Auckland Council's Historic Heritage Overlay, published as open data under CC BY 4.0. It is a good dataset, maintained by people who care about it. Each row gives you a stable reference into the Unitary Plan's Schedule 14, a name, a category, and a point.

Here is a representative row, near enough:

AUP-2419 · "Midden" · MaoriSite · POINT(174.8189 -36.3617)

That is the entire record. A midden is the remains of a cooking and food-storage site — physical evidence that people lived, ate, and buried their refuse at that spot, sometimes for centuries. The council register records that one is present and legally protected. It does not record whose ancestors left it, what the site is called by the people descended from them, what should happen when you visit, or whether it should be pointed out to strangers at all.

Of the Auckland rows we imported, 156 are categorised as Māori sites. Every one of them arrived in roughly that condition: a reference number, a generic English label, and a coordinate.

This is the moment a heritage app either takes a position or drifts into one by accident.

The tempting thing to do

The tempting thing — genuinely tempting, and easy to justify in a sprint planning session — is to fill the gap. We are an AI studio. We have models that will happily write four warm, respectful paragraphs about any pā site you name, in a tone indistinguishable from something a museum would publish. We could have generated protocol text for all 156 sites in an afternoon, marked it "AI-assisted", and shipped a map that looked complete.

It would have been fluent, plausible, and not ours to write. Tikanga associated with a specific site belongs to the people with mana whenua over that place. It is not general knowledge to be interpolated from a model's training data, and a paragraph that reads correctly is in some ways worse than an empty field, because it is harder to spot as unsourced.

So we did the less satisfying thing, which was to give the database a way to say we have not asked yet.

Decision one: a column for the unknown

places has a cultural_review column with three states: pending, reviewed, or null for not applicable. The migration that added it is blunt about why:

Māori sites imported without reviewed protocols are marked pending (flagged for iwi/mana-whenua review) rather than populated with unsourced cultural_protocols text.

All 156 imported Māori sites landed as pending. Not one of them got generated content.

This sounds like a small thing. It is the load-bearing decision in the whole project, because it changes what "done" means for a record. A place is not finished when it has a coordinate and a description. It is finished when the people who should have been asked have been asked. Encoding that as a column rather than a spreadsheet somewhere means the state is visible to every query, every screen, and every future contributor who wonders why a site has no protocol text. The absence is deliberate and legible, rather than looking like a gap someone forgot to fill.

The corollary is that the import has to be re-runnable without damage. Each row carries a source_ref — the Schedule 14 identifier — under a unique index, so the importer upserts on it and a re-run never duplicates a place. Council data gets revised; the pipeline has to absorb that without flattening work done downstream of it.

Decision two: never reinterpret at display time

The second decision came out of a bug, which is the honest way most good decisions arrive.

cultural_protocols started as a free-text column. One paragraph per site, written by a person, stored as prose. The app rendered it as a bulleted list, which meant the client was splitting the paragraph into bullets at display time.

Read that again as a cultural question rather than a formatting one. The app was deciding where one instruction ended and the next began, in content about how to behave on someone's ancestral maunga, based on where the full stops fell. It looked fine. It was the app quietly editing content it had no standing to edit.

We retyped the column to JSONB with an explicit shape:

{
  "heading": "This maunga is a tupuna (ancestor) and a sacred place.",
  "protocols": [
    "Keep to the formed paths.",
    "Take nothing and leave nothing.",
    "Refrain from eating or drinking alcohol on the tihi (summit).",
    "Vehicle access to the summit is restricted out of respect."
  ],
  "attribution": "Cared for by the Tūpuna Maunga o Tāmaki Makaurau Authority on behalf of Ngā Mana Whenua o Tāmaki Makaurau."
}

The client now renders that verbatim. The bullets are bullets because someone decided they were bullets, not because a string split on a delimiter. Attribution is a field rather than a footnote, so it cannot be dropped by a layout change or a smaller screen. And when the protocols exist, they render before the historical content, not after it in a section people scroll past.

Note also what we did not do in that migration: the existing free-text paragraphs were not auto-cast into the new shape. They were dropped and re-authored. A lossy automatic conversion would have been the app making the same judgement call again, once, at migration time — which is exactly the thing we were fixing.

What generalises

Very little of this is specific to heritage, and none of it is specific to New Zealand. Three rules we would carry into any project handling content that belongs to someone else:

Model the absence. If your data can be incomplete in a way that matters, give the schema a state for it. "Null because nobody has asked" and "null because it does not apply" are different facts, and collapsing them into one empty column loses the only information that tells you what to do next.

Put the structure where the authorship is. Any interpretation your client performs at render time — splitting, summarising, truncating, reordering — is an editorial act performed by software. That is fine for a product description. It is not fine for content someone else is accountable for. Move the structure upstream to whoever holds the authority, and have the client render it as given.

Let generative tools do the mechanical work, not the meaning. We used models throughout this project: cross-matching council records against the Heritage New Zealand list, normalising names, wrangling geometry, drafting our own historical copy. We did not use them to write the parts of the record that belong to someone else. That is not a technical limit. Those tools would produce output. It would just be the wrong output, convincingly formatted.

The last one is worth being blunt about, because the pressure runs the other way. A map with 156 blank fields looks unfinished, and looking unfinished is a real cost. But the alternative is a map that looks complete and is partly fiction, and the people best placed to notice are exactly the people you most need to trust it.


HeriTrail is free, and its place data is open and credited on every record. Have a look at heritrail.com. If you are building something that has to carry other people's knowledge carefully, we are always happy to talk about how it is modelled.

About the author

Anil Dongre

Founder, Think and Form Limited. Practical product thinking, AI-assisted workflows, and software built around how people actually work.