Page Editing
The editor at GET /_admin/pages/{path} has three modes, switchable from the page header.
- Edit (default) renders one input per declared model field. Custom frontmatter the model does not declare is still surfaced so authors never lose a value silently.
- Source shows the raw YAML frontmatter and Markdown body in a single textarea — useful for spotting frontmatter the form pane does not surface, or for copy-pasting.
- Diff renders a read-only working-tree-vs-HEAD diff of the underlying content file. This is available on git-aware builds with a
.gitdirectory under the content tree.
All fields are disabled for the Publisher role.
Field validation
As you tab between inputs, each blur posts to POST /_admin/validate-field, which validates that single field without saving and returns the validation errors inline beneath the input (or clears a prior error). This lets authors fix problems before attempting a save.
Live preview
In Edit mode, POST /_admin/preview-render renders a live preview of the page using the current form values, giving visual feedback as you type.
Save and conflict detection
The Save button posts the form to POST /_admin/pages/{path}. The save goes through an orchestrator that validates, conflict-checks, writes, and appends an audit-log entry.
Concurrent edits are detected via a hidden __mtime field. When the editor form is opened it records the on-disk file’s modification time (Unix seconds). On save, the server reads the file’s current mtime via std::fs::metadata() and compares it against the recorded value. A mismatch triggers a conflict outcome and no file is written — see Conflict resolution.
After a successful save, the fresh on-disk mtime is re-rendered into the hidden __mtime field so subsequent edits use the updated timestamp.
Frozen content guard
When your site uses versioned content, every released version is frozen so its pages keep documenting what that release actually shipped. Each version under versioning.roots carries a lifecycle state:
versioning: roots: /docs: versions: v0.19: label: "0.19" state: released # frozen: feature edits rejected, errata allowed v0.20-dev: label: "0.20 (Dev)" state: dev # the single writable bucket default: v0.19
state values are dev (the one writable bucket), released (frozen, correctable through the errata overrule), and archived (frozen, no edits at all). When state is omitted the last-declared version defaults to dev and the rest to released, so existing configs keep working. Exactly one version per root may be dev.
A page’s freeze state is read from the physical version folder it lives in, never the URL it is served at, so a page surfaced through version fallback is still edited where its file actually lives.
Opening a frozen page in the editor shows a banner and replaces the Save control:
- A released page offers an “Edit as errata” action. It reveals a required reason field; saving writes the page, records an
errata_updateaudit entry (who, when, reason, target version), and re-baselines that page’s hash in the frozen manifest soaccent validatestays green. - An archived page is read-only; even the errata overrule is refused until the version is moved back to
releasedin config.
The banner is advisory — the guard is enforced server-side. A plain save of a frozen page returns 403 Forbidden and writes nothing; an errata save of an archived page returns 403 too. The errata overrule is offered only to write-capable roles (the Publisher role cannot write at all).
Frozen content validation
Direct filesystem or CLI edits bypass the admin, so accent validate enforces the same freeze. At release-promotion time a per-root manifest (.frozen-manifest.toml, stored next to the version folders) records a content hash for every page in each frozen version:
# Write/refresh the baseline once the new dev bucket is cut: accent validate --write-manifest --config config.yaml # Normal runs (and CI) fail on any unrecorded change to a frozen page: accent validate --config config.yaml
validate recomputes the hashes and exits non-zero on any modification, unrecorded addition, or deletion of a frozen page. An errata correction made through the admin updates the manifest automatically, so a legitimate fix stays green while accidental drift fails the build.