Skip to content
mdlake

Safe content refresh

How a snapshot site takes new content without breaking the code you wrote. Covers the site snapshot, which of your files read which keys, and what to do when a refresh breaks something.

On this page

Editors keep publishing. Your hand-written pages and components read that content. A refresh moves the site to newer content, so the question is what it does to your code. The builder answers it in three layers: it finds the files that read each key, it fails loudly when a key is gone, and it compares the routes of the old and the new build. You decide at each step, and nothing moves before you approve.

This page explains the layers and what to do when a refresh breaks something. The steps of the refresh itself are in Refresh content.

The snapshot site

A site in snapshot mode builds against one lake commit, which lake.lock pins. Publishing in the CMS does not change the site. The pin moves only through the refresh-content skill. That gives you a fixed point to compare against, and it means your code never meets new content by surprise. See How sites work.

Note

A live site follows the newest commit on its own and has no refresh step. A missing document then breaks the build the next time it runs. Prefer snapshot when hand-written pages read single documents.

Layer 1: who reads which key

Your pages and components read content through getLakeDocument, getLakeCollection and getLakeImage, with a string key. The tools scan the site-owned files under src/ for these calls. Kit and generated files are not scanned. Adopted files are, because they are yours now.

The result is a map from key to files. A slot counts under its own name and under the key it is bound to. A change to the document bound to pricing is also a change to the slot pricing.

The scan is static. It reads the text of the call, so it needs a string literal as the key:

CallTracked
getLakeDocument('pricing')Yes
getLakeDocument(name)No. check-bindings warns, and refresh-content cannot name the file.
getLakeDocument(`plan-${n}`)No. Same warning.

A match inside a comment counts as well. The tool prefers a spurious name to a missed file.

Layer 2: the content report

refresh-content compares the lake at the locked commit and at the newest one, for this site only. For every changed, unpublished or deleted key it lists the files that read it.

src/pages/pricing.astro reads pricing, which was removed

You see this before the candidate is built. It tells you which of your files to open first, and which edits are only informational (a new item in a list).

Layer 3: the check fails loudly

With the candidate commit, npm run check fails when a site-owned file reads a key whose document is missing. It never hides the problem and it never falls back to old content.

error   documents.pricing: Document 01JBXQ3F7M2N8P4R6S9T1V5W0A is missing or not published (key "pricing"); read by site code: src/pages/pricing.astro

The message gives the binding, the document ID, the key and the site files that read it. A collection that has no published documents is a warning (“Collection has no published documents”), and an error only when it fills a required slot of the site type. Your code must cope with an empty collection.

Layer 4: the route snapshot

site-snapshot.json is committed. npx mdlake snapshot writes it from a build. It records, per route, the title, the heading outline, a hash of the visible text, the internal links, the broken links and the asset IDs. It holds nothing random, so the same site gives the same file.

refresh-content builds the candidate and runs npx mdlake snapshot --compare against it. The report says which routes were added or removed, which headings were lost, which links now lead nowhere and which assets are gone. This catches what the type system cannot: a page that still builds but now shows less than before.

npm run check warns when site-snapshot.json is missing or older than lake.lock. Fix it with npm run build and then npx mdlake snapshot. Commit the file with the lock.

What to do when a refresh breaks something

Work through the cases in this order.

A key is gone

A document that your page reads was unpublished or deleted.

  1. Ask the editor whether it was on purpose. If not, ask them to publish it again. Then run the refresh again.
  2. If it was on purpose, pick a fix in your file:
FixWhen
Bind another document to the same key in lake.config.tsA replacement exists.
Handle the missing case in the pageThe content is optional.
Remove the page, or the section that used itThe content is gone for good.
Change the collection queryA list lost an item that you addressed by key.
  1. Edit your site-owned file. If the binding is in lake.config.ts, change plan.md and run plan-to-config.mjs, or adopt the file. See Ownership zones.
  2. Run npm run check again with LAKE_LOCK_CANDIDATE set. See the command in Refresh content.

The skill proposes such a fix and applies it only after you agree. It never edits the lake.

The route comparison names them. A page of the plan may have lost its document. A link in a document may point at one that is no longer published, and the kit turns a link without a page into plain text.

  • Restore the document, or
  • change the plan so the route goes.

A heading or an asset is gone from a page

Usually an editor rewrote the document. Check whether the loss is wanted. If not, ask the editor. If your component expects a heading by name, make it tolerant, because editors will change headings.

Generated copy is stale

A source of a generated file changed. The report lists the file as stale. Regenerate it through generate-copy, or revert the edit in the lake. See Generated copy.

The primary call to action lost its target

The brief says which page the call to action leads to. A refresh that removes that page is reported as a conflict before the stop. You decide, and the brief is updated in the same change when the answer changes.

You do not want the refresh

Reject it at the stop. The lock and the snapshot stay as they are, the skill puts the preview back, and the site is unchanged. Nothing is lost.

Make your code refresh-proof

  • Use string literals as keys.
  • Bind documents by key in lake.config.ts so a replaced document is one change.
  • Prefer a collection to a list of single documents when the number of items changes.
  • Treat optional fields as possibly missing, and render nothing for a missing one.
  • Do not copy lake text into code. A copy hides the change from every check above.
  • Commit site-snapshot.json with every lock change.
  • Run npm run check before you merge the change that moves the lock. The generated pipelines run it on pushes to main, not on pull requests, so a pull request check needs a pipeline you add yourself.

What can go wrong

SymptomCause and fix
A file that reads a changed key is not namedThe key is not a string literal. Change the call.
“No committed site-snapshot.json”Build at the locked commit, run npx mdlake snapshot and commit.
The check passes and a page looks wrongThe key still exists but its content changed. Read the route comparison and the preview.
The check fails only with the candidateThat is what it is for. Fix the file that it names.

What’s next