PolicykitDocs

Writing a revision

Ship a new version of a policy: copy the newest revision to a dated directory, edit the text, set effectiveFrom, notice and changeSummary.

6 min readUpdated 14 August 2026
Writing a revision

A material change to a policy is a new revision: a new dated directory, never an edit to a published one. Published text is what users accepted or were notified about. This page is the checklist for cutting one.

Material means the meaning moved - a right, a price, a limit, an obligation, a data practice. If you have to ask, it is material. A typo, a broken link or a formatting fix is an erratum: edit the published file in place and stop. Git is the errata trail. If the fix turned out to change what a clause means, it was never an erratum - revert it and cut a revision.

Prerequisites: a configured Policy (see getting started). Its dir, locales, and defaultLocale decide which files this revision needs.

Copy the newest revision to a new dated directory

The revision date is today, the day the change is decided, zero-padded YYYY-MM-DD. That directory name is the revision's whole identity.

cd policies/terms-of-service
cp -r 2026-07-07 2026-08-11

Copy every locale file, then rewrite each one. Translations are made fresh from the new source text - a copied-forward translation validates green and lies. See translations for which locales a revision owes.

Edit the text and set the frontmatter

Only the default locale's file (en.mdx unless you configured otherwise) carries metadata. effectiveFrom, notice, and changeSummary are required; title is optional:

---
effectiveFrom: "2026-08-25"
notice: notify
changeSummary: "Added the monthly fair-use limit and retiered change notice."
title: "Terms of Service"
---

Quote effectiveFrom. An unquoted date works, but YAML rolls an impossible one over to a real different day - policy directories has the traps.

Other locale files may carry an optional title: and nothing else. Every file needs a non-empty MDX body.

effectiveFrom must be a real calendar day on or after the revision date. The gap between the two is your notice window.

Every sentence in a policy is a promise you must then keep, forever, and can only walk back with another revision, a notice, and possibly fresh consent. Draft to the floor the governing law sets and stop there:

  • Write the statutory bound, not a better one - the notice period, retention window, refund or response time the law states. Where the law gives a range, take the bound that costs you least.
  • Volunteer nothing the law does not demand: no uptime or response-time guarantees, no deletion faster than required, no rights the statute does not grant, no clause borrowed from a competitor's more generous policy.
  • No unbounded absolutes - "never", "always", "immediately". They promise more than any operation can guarantee and turn an ordinary incident into a breach. State the actual practice, bounded by what the law requires.
  • If the governing law or the customer class (consumer terms and business terms differ) is not settled, ask your counsel rather than defaulting to the generous reading. Over-promising is not the safe side; it is an obligation.

Removing an over-promise you already published is a material change of its own - it reduces what the customer gets, so it is a new revision at notify (or reconsent if consent hangs on it) with the notice window your current terms promise. Never edit it away in place.

Choose the notice tier

notice is the recorded legal decision, and it drives the UI:

  • reconsent - users must expressly accept again. Queued for notice, announced by the banner, and moves requiredConsentRevision from its effectiveFrom day.
  • notify - a material change that does not require fresh consent. Queued and announced; never re-prompts consent.
  • none - non-material, or a change forced by law or security that owes no notice. Nothing is queued and no banner. It does not move the consent gate - with one exception: a policy's first revision is its baseline and sets the gate whatever its tier, so adding a new policy re-gates consent even at none.

Unsure? Take the stricter tier. A none revision is never announced by pendingNotice or PolicyBanner - if users should see it coming, it was never none.

Write the changeSummary

One plain-English sentence saying what changed and why the tier is right. It is internal changelog and evidence, never rendered to users, so write it for the lawyer reading the git log in two years, not for the customer:

changeSummary: "Cut the refund window from 30 to 14 days; notify, existing consent stands."

Rules that will bite you

  • Never amend a shipped revision. Your notice delivery is deduped on the revision string, so an edited revision reaches nobody. policykit itself never dedupes or re-sends - noticeQueue re-reports every owed revision on every call, and expiring the dedupe row is your side of it.
  • One revision per day. A second material change folds into today's directory only if it has not deployed yet; otherwise date it tomorrow.
  • Never back-fill. A new locale starts at the revision that introduces it. The loader enforces contiguity forward: once a locale appears at revision R it must exist for every revision after R. It may legally be absent before R - not backfilling the archive is your rule, not the loader's.
  • A committed dated directory is published, on the next deploy. Work in progress goes in drafts/ at the policy root, which the loader skips wholesale, or on a branch. Never rewrite git history over policy content - the log is the acceptance-evidence chain.
  • effectiveFrom >= revision is the only rule about the GAP between the two dates. Both must also be real calendar days, and an unquoted date that YAML rolled over is rejected. The notice window your own terms promise is yours to enforce - pin it with your own test. Shipping an adverse change effective tomorrow breaches your contract silently.

Superseding a revision that has not taken effect

If you ship a revision that takes effect on or before a still-pending revision's effectiveFrom, the pending one is superseded: its text never binds, so policykit excludes it everywhere - see how it works for why.

The surviving obligations move to the superseding revision. If the superseded revision was reconsent and its change survives in the new text, record reconsent on the new revision. Otherwise the re-prompt is silently dropped.

Validate before you commit

Every rule above surfaces only when the loader reads the directory. Run your policy's assertValid() - or assertValidAll() across the set - and fix what throws until it is green; a full next build exercises the same loader. Validation covers both, and how to read the PolicyValidationError you get.

Let an agent do it

The package ships a Claude Code skill that runs this whole workflow, and a rules file that carries the judgement calls above into every session. See /policykit for both, including how to install them.

  • /policykit - the shipped skill and rules file.
  • Validation - proving a revision is valid before it deploys.
Was this page helpful?