Local dev and contributing

Running locally

cargo run from the repo root. The server binds :3031; open http://127.0.0.1:3031/ and you have the full stack — content tree, editor, permissions, language prefixes, sitemap, backup writer.

Hot-reload boundaries

Not everything is live. Knowing what survives an edit-and-refresh vs. what needs a restart saves a lot of head-scratching:

  • Content tree edits under programfiles/content/ — picked up live. Every request re-reads ctx.json and the markdown body through fop::read_json_file / fop::read_text_file; there is no in-process cache.
  • programfiles/op/ config files — also re-read at request time for most keys. backup.rs::read_config re-opens config.json on every cycle, so backup_interval_secs / backup_retain / backup_dir edits land at the next tick. But some statics in src/content.rs (NAVBAR, FOOTER, SUPPORT_LANGS, CONFIG) are wrapped in Lazy::new and capture their value on first read — restart to see edits to navbar.json, footer.json, support_lang.json, or config.json’s public_origin / default_icon / default_og_image reflected reliably.
  • Akari templates under templates/ — reloaded by the renderer on each call; no restart needed.
  • Rust code under src/ — requires cargo run again. The build.rs codegen for lang_endpoints.rs re-runs only when support_lang.json changes, so a pure Rust edit recompiles fast.

Restart shortcut

Kill the running cargo run and re-run it. The first request after a restart triggers an initial backup snapshot when backups are enabled — that’s by design in spawn_writer, which calls refresh().await once before entering the interval loop so even a host that restarts more often than backup_interval_secs always has a recent archive.

Style guidelines

  • No comments unless the why is non-obvious (and that’s rare). Identifiers carry the what; commit messages and PR descriptions carry the story. A comment that paraphrases the next line of code is noise.
  • No back-compat shims for removed code. No _unused renames, no // removed markers, no leftover pub use re-exports of types that no longer exist. Delete cleanly — git is the archive.
  • No error handling for impossible cases. Trust internal code and framework guarantees; validate only at system boundaries (user input, HTTP request bodies, filesystem reads). An unwrap on a path you literally just constructed is fine; an unwrap on a user-supplied slug is a bug.
  • JA UI labels prefer kanji-mixed over full-katakana. 規約 over ポリシー, お知らせ over ニュース, 案内 over ナビゲーション. See the existing navbar.json and footer.json for the established style.
  • Editor button palette. btn-pink for primary actions (Save, Create), btn-outline-secondary for grey neutral (Cancel, Back), btn-outline-danger for destructive (Delete, Remove), btn-outline-pink for non-primary actions in the same flow. Do not introduce btn-success or btn-warning — they conflict with the rest of the UI.

Tests

See the #[cfg(test)] mod tests block at the bottom of src/backup.rs for the clean test-workspace pattern. Each test calls workspace("<name>"), which removes and recreates target/test_backup_<name>/, so parallel test execution (cargo’s default) doesn’t see cross-test interference. target/ is gitignored, so test residue can’t leak into a commit even if cleanup fails. When adding tests for any subsystem that touches the filesystem, follow the same shape: per-test directory, predictable name, no shared mutable state.

HTML escape discipline

Every user-derived string passed to Akari must go through crate::content::html_escape — or breadcrumb_html_escape for breadcrumb item arrays, or the JSON-context escaping inside breadcrumb_jsonld for the structured-data body. Akari does not auto-escape. A page title containing Q&A: when to use "file" will produce broken HTML if you forget. The dispatcher in src/content.rs enforces this at the boundary on name, description, canonical, og_image, and breadcrumb fields; mirror the pattern in any new render arm. This is a foot-gun that has bitten before — when in doubt, escape.

Contributing back

Pull requests against the FDS doc-server repo. Match the surrounding style — read three files near your change before you write the fourth, and your diff will fit. When touching language, prefix-endpoint, or SEO behavior, reference LANG_AND_SEO_BEHAVIOR.md at the repo root: it is the source of truth alongside the code for hreflang emission, prefix-endpoint dispatch, cookie-vs-prefix precedence, and sitemap shape. Updates to that doc go in the same PR as the behavior change.

Cross-link: Backup writer, Language system, SEO and sitemap, Route: edit.