Why the changelog is hash-linked
Redundant enforcement, and why the read path verifies rather than a script somebody remembers to run.
There are four places a tampered changelog is caught here, and the redundancy is the point: there is no single check to bypass.
The read path verifies
getChangelog() verifies the chain before it returns anything. Every route that
shows a changelog entry goes through it, so a broken chain cannot be built,
served, or previewed — prerendering throws instead. A verifier that only runs in
a script is a verifier somebody eventually forgets to run.
The build refuses to start
prebuild runs the verifier before next build does anything, which turns a
render-time stack trace into a one-line error at the top of the log.
CI checks every push
Including pushes that touch nothing else, with signatures required. The chain is
audited against full history, because each seal pins the commit that was HEAD
when it was written.
CI checks the diff, separately
Verification proves the chain is internally consistent. It does not prove nobody
rewrote an entry and resealed it. So on every pull request, a second job asks
git a blunter question: did this change modify, rename, or delete anything under
content/changelog/? Adding files is fine. Touching what already exists is not.
The two checks answer different questions, which is why both exist.