Freshness

Every page can declare who owns it, when it was last updated, how often it should be reviewed, and where the sources of truth live. kazam turns that into a banner — yellow if a review is due within a week, red if it's already overdue — and a build-time report of everything that needs attention.

The metadata

One block of YAML per page

Add a freshness: block to any page's YAML. Every field is optional — a page with only updated: and review_every: will still compute status correctly; fields you don't set just aren't shown in the banner.

title: Onboarding guide
shell: standard

freshness:
  updated: 2026-01-15              # ISO date of the last content change
  review_every: 90d                # Nd | Nw | Nm | Ny | weekly | monthly | quarterly | yearly
  owner: owner@example.com          # free-form — email, Slack handle, team name
  sources_of_truth:                # bare URL or { label, href }
    - https://notion.so/abc123
    - label: "#ts-hub"
      href: https://company.slack.com/archives/C012345
    - label: "Linear: TSH project"
      href: https://linear.app/co/project/tsh

components:
  - type: header
    title: Onboarding guide
  ...
Banner variants

Yellow, then red

kazam injects a banner at the top of the rendered page based on how close it is to the review deadline. No runtime JavaScript — the check runs at kazam build time and the banner is baked into the HTML. To keep the banner accurate between rebuilds, run a scheduled daily build (a GitHub Action on schedule: works).

Review due soon

Review is due in 5 days. Last updated Jan 15, 2026 (85 days ago). Review cadence: every 90d. Site last built: Apr 21, 2026. Owner: owner@example.com.

Review overdue

Review is 20 days overdue. Last updated Jan 15, 2026 (110 days ago). Review cadence: every 90d. Site last built: Apr 21, 2026. Owner: owner@example.com.

When sources_of_truth: is set, the real banner also renders a list of links underneath — the reviewer clicks straight through to the doc / Slack channel / Linear project to do the refresh. (The two callouts above show the banner body; the real banner adds a sources-of-truth row beneath.)

The build report

Every build tells you what's stale

kazam build always prints a grouped summary of every page past (or nearly past) its review window. Overdue items sort first, most-overdue at the top. Silent when nothing is stale — no noise on healthy builds.

$ kazam build .
  _site/guide.html
  _site/onboarding.html
  _site/api/reference.html

✓ 3 page(s) → _site

⚠ 2 overdue page(s):
    onboarding.html                         20 day(s) overdue (cadence: every 90d) — owner owner@example.com
    api/reference.html                       5 day(s) overdue (cadence: every 180d) — owner eng@example.com

⏳ 1 page(s) due for review soon:
    guide.html                              due in 3 day(s) (cadence: every 30d)
Wire it into CI

Run kazam build . on every PR and capture the log; the report becomes a free review surface. Or on a nightly cron: a weekly gh action that emails the owner field for anything overdue costs nothing and keeps the KB honest.

Sources of truth

kazam doesn't fetch — your agent does

The sources_of_truth: list is deliberately just labeled URLs. kazam never reaches out to Notion, Linear, Slack, or anything else at build time — that preserves the zero-supply-chain, no-network-at-build-time guarantee.

Where this gets interesting: an agent refreshing the page can see the sources, fetch what it's able to (URLs via WebFetch, Linear via its MCP, Slack via its MCP), and ask the user to paste anything it can't reach. User owns the fetcher; agent owns the integration; kazam stays narrow.

Shorthand — just a URL. The label becomes the URL itself.

sources_of_truth:
  - https://notion.so/abc

Labeled — any link (Slack channel, Linear project, HubSpot dashboard) gets a readable label.

sources_of_truth:
  - label: "#ts-hub"
    href: https://company.slack.com/archives/C01
  - label: "Linear: TSH"
    href: https://linear.app/co/project/tsh
How status is computed

`updated + review_every` vs today

The check is a pure date comparison — updated + review_every vs today (from KAZAM_TODAY when set, else the system clock). Three states:

Fresh
No banner
More than 7 days until the review deadline.
Due soon
Yellow
Within 7 days of the deadline. Reviewer sees the nudge.
Overdue
Red
Past the deadline. Report surfaces on every build.

Pages missing either updated: or review_every: are always Fresh — there's nothing to compare against. The yellow-window size (currently 7 days) will become configurable per site in a future release if demand surfaces.

Agent workflow

Point your agent at `_site/stale.md`

Every build that finds any stale pages also writes a markdown report to _site/stale.md. Overdue pages first, due-soon below, each with path, cadence, and owner. Perfect for handing an agent one path and saying "fix these."

# Build, then hand the stale report straight to Claude Code.
# Claude reads _site/stale.md, then opens each page's .yaml and
# its sources_of_truth to propose updates.
kazam build .
claude -p "Read _site/stale.md. For each stale page, open its source YAML in the repo + the sources_of_truth links, then propose concrete updates."

The file is rewritten on every build and deleted when no pages are stale, so "does _site/stale.md exist?" is a cheap CI check: any PR that introduces stale pages will produce the file; a clean build leaves it absent.