All notable changes to Cherry are documented here. Format follows Keep a Changelog; versioning follows SemVer.

Entries are terse one-liners linked to their PR: - Thing that changed. (#12)

[0.4.0] — 2026-08-21

Fixed

  • cherry serve honours base_path: the site serves under its prefix exactly as production will (banner URL included), the bare root redirects there, and an unprefixed path that would 404 on the real host now 404s in dev too — previously a base_path site served styleless because every emitted asset URL pointed at the prefix the dev server ignored. (#92)

Added

  • cherry gen.action --host cloudflare: first-class Cloudflare deploys via Workers static assets — writes a wrangler.jsonc (no Worker script; _site/ as assets with 404.html wired up, Worker named by --name or a slug of the site title) plus .github/workflows/cloudflare.yml, which builds with the pinned cherry action and ships with wrangler deploy. _headers/_redirects files pass through static/ verbatim, so Cloudflare's header and redirect rules work with no new config. (#91)

[0.3.0] — 2026-08-21

Added

  • cherry serve honours the PORT environment variable when --port is absent (explicit flag still wins; malformed values fall back to 4000) — what proxy runners and PaaS-style tooling hand out. (#83)
  • cherry serve --name NAME registers the bound port with a running cherrypicker daemon, so the site also answers at a stable http://NAME.localhost URL. Zero-cost stdlib client; no daemon running means the port URL as ever, never a failed serve. (#84)

Fixed

  • Template overlays work for sites using Cherry as a hex dependency: the official themes live under the site's own _build there, which the site-local check mistook for a theme living in the site, silently disabling every overlay. Built-in themes are now never site-local, wherever they sit on disk. (#86)

[0.2.0] — 2026-08-20

Added

  • cherry serve --verbose: a request log at last — method, path, status, and response time per line, SSE reload subscribers included. (#62)
  • cherry new PATH: the binary lane can finally scaffold — config, first post, pages, AGENTS.md, and the .claude publish skill, all speaking cherry <verb>. Refuses a non-empty directory; the title humanizes from the directory name. Deliberately no mix cherry.new twin in core: the cherry_new archive owns that name (and adds the mix project files), so both install lanes coexist. (#57)
  • HEEx templates: the three-level lookup now accepts <name>.html.heex beside <name>.html.eex, and HEEx wins at the same level. Interpolation escapes by default (raw/1 is the explicit door), :for/:if and <.component> calls work, and a theme-root components.exs (use Phoenix.Component, runtime-compiled like every .exs escape hatch, binary included) defines function components for that theme's templates. A .heex overlay is rewritten in theme.diff — owned outright, since no three-way merge against an EEx upstream is possible — and theme.eject refuses to write an .eex copy a rewrite would shadow. Malformed templates fail the build with file, line:column, and a caret. Official themes stay EEx. (#54)
  • Content components, framework-level so theme swaps survive them: ::figure{src alt caption} (alt required), ::video{youtube title} — a facade link that makes zero third-party requests until clicked, upgraded in place to a youtube-nocookie embed by a new ~500-byte island, or src= for a native local player — and :::note{title} … ::: containers for the five alert types, in the remark-directive syntax Docusaurus and VitePress authors already know. Directives inside code fences are shown, not expanded; misuse never breaks a build — the line stays visible and cherry check gains a component rule naming the file, line, and problem. (#53)
  • The customization ladder's middle rungs are real: tokens: ["--color-accent": "#7c3aed"] in cherry.exs overrides any token the theme's manifest declares (a typo errors with the nearest real name), and assets/custom.css loads last, always. Both ride the framework-owned head, so every theme honors them without cooperating; official theme CSS now lives in @layer theme, so site overrides win by cascade-layer rules rather than specificity fights. (#51)
  • cherry theme.tokens: the theme's styling API as a command — every token with its default, doc, and any site override, merged. (#51)
  • cherry config tokens.NAME VALUE: token overrides written from the CLI, the one structured setting config edits — entries are distinctive enough to rewrite surgically, and the name is validated against the theme manifest before the file is touched. (#51)

Fixed

  • cherry serve listens on IPv6 as well as IPv4 (a second v6-only listener on the same port — Windows cannot unset ipv6_v6only, so one dual-stack socket is not portable). localhost no longer stalls on hosts that resolve it to ::1 first; hosts without IPv6 keep the IPv4 listener alone. (#62)
  • Mobile tap targets: nav, footer, and post-meta links in both official themes now meet the WCAG 2.5.8 24px floor (hit area grows via padding + negative margin, zero layout shift), and the search input holds 16px at coarse pointers so iOS Safari never zooms on focus. (#55)
  • theme.which arrowed both the .heex rewrite and the shadowed .eex twin with ← renders; only the file that actually renders gets the arrow now, and the JSON envelope carries it as renders. theme.diff reported that shadowed .eex as current; it is now shadowed, with check treating it as inert. (#56)

Changed

  • cherry new and mix cherry.new scaffold search: "cherry" — full-text search on from the first build, no Node anywhere; delete the line to opt out. (#61)
  • Both official themes moved to light-dark() tokens: every color token is one pair instead of three synchronized blocks (light, dark-via-media, dark-via-toggle), the toggle forces a rendition by flipping color-scheme alone, and printing from a forced-dark page now gets the full light rendition — syntax palette included, which the old print block could not reach. Engines without light-dark() get the complete light rendition (the pairs live behind @supports) and the toggle stays hidden there. Token manifests now declare the pair (default: light, dark:), gate-enforced against the CSS, and theme.tokens/theme.list report it. (#52)

[0.1.0] — 2026-08-17

The first stable release. The entries here are what landed since rc.3; the three release-candidate sections below record the rest of the road to 0.1.0.

Added

  • demo/GUIDE.md and the site it builds (demo/site): a CLI-only walkthrough from an empty directory to a deployed site, every command run and its real output pasted in, gated by a test that fails if the guide shows a verb the CLI does not have or the site it describes stops building clean. (#48)
  • cherry config reads and writes cherry.exs, closing the last gap in the CLI-only loop: a scaffolded theme can now be activated (cherry config theme themes/NAME) without an editor. Writes rewrite only the changed value — comments and layout survive — are validated by reloading the site, and roll back if the value is rejected. Structured settings like nav: are refused rather than reformatted. (#47)
  • cherry version reports the git revision it was compiled from, so a build from a branch is no longer indistinguishable from the release it was branched from. null for a build compiled from hex. (#47)
  • check gains empty-body and unfilled-field: a published post with no prose, or frontmatter still holding the empty string a generator wrote, no longer passes silently. (#47)
  • search: "cherry", a built-in search engine that needs no Node: the index is an inverted list built in-process from the parsed documents and emitted as search/index.json, ranked in the browser by a ~2 kB island that ships from priv/search/ so any theme gets it. Pagefind stays available as search: "pagefind" for sites that want it and can afford npx on the build machine. (#46)

Changed

  • The @search template assign carries the configured engine ("cherry", "pagefind", nil) instead of a boolean, since the two engines need different markup; <%= if @search do %> still reads as "search is on". (#46)

Fixed

  • cherry serve claimed live reload on filesystems that never deliver change events (a Docker bind mount from a Windows or macOS host, a network share): the watcher started, the banner promised reloads, and no edit ever rebuilt. Serve now proves the watcher works with a probe in the content directory before believing it, and degrades with a warning naming the likely cause when it does not. Probing the watch root would not do — that mount delivers events for the root and none for its subdirectories, which is exactly where content lives. (#47)
  • cherry serve logged Header timestamp couldn't be fetched from ETS cache on every single response: Bandit was started as a bare child spec, leaving the clock table its own application owns unstarted. (#47)
  • cherry upgrade --check exited 1 when only prereleases existed. "Nothing stable yet" is an answer, not a failure; it now exits 0 with status: "no_stable_release". The upgrade itself still refuses. (#47)
  • copy-code.ts was missing the export {} that keeps an island out of the shared TypeScript global scope, so its top-level names leaked and collided with any new island's. (#46)
  • cherry gen.theme produced a theme that cherry check --strict immediately rejected: because overlays are keyed by theme name under themes/, a site-local theme resolved its own templates as untracked overlays of itself and every one was reported as drift. Overlays now only exist relative to an installed theme; theme.eject refuses a site-local theme instead of writing onto it. (#45)

[0.1.0-rc.3] — 2026-08-15

Fixed

  • cherry serve no longer crashes when no file-watcher backend is available (inotify-tools missing on Linux, the common case in containers and CI): the site serves without live reload, a warning names the platform's remedy, the banner says so, and the envelope carries live_reload: false. (#42)
  • The standalone binary forces UTF-8 filename and terminal encoding (rel/vm.args.eex), so latin1-locale containers stop warning on boot and mangling the banner. (#42)

Changed

  • The release workflow's per-target smoke test now actually serves: scaffold, background serve --port 0 with no TTY, HTTP probe, and a process-still-alive check on all five targets — real macOS serve coverage without owning a Mac, and the class of bug rc.1 shipped can no longer reach a release. (#42)

[0.1.0-rc.2] — 2026-08-15

Fixed

  • The standalone binary exited the moment cherry serve printed its banner (Cherry.Binary halted unconditionally after every command); blocking verbs now hold the VM open, single-sourced in Cherry.CLI.Registry.blocking?/1 so the binary and the mix task cannot drift. (#39)
  • install.sh / install.ps1 downloaded GitHub's HTML 404 page while only prereleases exist (releases/latest never resolves a prerelease); both installers now resolve the tag via the releases API and fall back to the newest release of any kind. (#39)
  • cherry publish accepts the bare slug gen.post returns in its envelope; an ambiguous slug is a usage error naming the candidates, and the path form still works. (#39)
  • cherry check no longer reports protocol-relative URLs (//host/…) as broken internal links; they are external by definition. (#39)
  • Hex package hygiene ahead of the first real publish: dialyzer PLT caches no longer ship in the tarball; hexdocs carry the CherryBomb marks (sidebar logo plus the lockup bundled into the README via ExDoc assets, so docs are self-contained) and a grouped module sidebar; README gains a "Use from Elixir" section and reality-checked Status; cherry.new scaffolds depend on the hex release matching the installer instead of the develop branch, and the cherry_new hex page gets a README. (#34, #35)
  • Pagefind search drawer rendered in document flow inside the nav, pushing the whole page down with an off-centre results column, a dead thumbnail gutter, browser-yellow highlight marks, and a search input that overflowed the viewport on mobile. Both themes now anchor the drawer as a token-themed overlay panel under the input (showImages: false, internal scroll), and the header wraps at 44rem so search gets a full-width row on small screens. (#30)

Changed

  • Serve docs, the skill, and the authoring guide now cover --port, recommending --port 0 (bind a free ephemeral port, reported in the envelope) for scripts, CI, and agents; the skill also teaches publish-by-slug. (#39)
  • gen.action workflows pin the build action to the release tag of the running cherry (holsee/cherry/action@v<version>) instead of @develop, so generated pipelines stop tracking a moving ref; the action README example pins to v0.1.0-rc.1 and the TODO is gone. (#33)
  • CherryBomb brand assets (logo, mascot, wordmark, and all repo derivatives) are excluded from the MIT/Apache-2.0 dual license: assets/LICENSE reserves them while permitting in-product display and nominative use; the hex package ships the notice alongside the theme's nav mark. (#30)
  • Site prose voice pass: no em dashes anywhere on cherrybomb.dev, all JSON code blocks fully pretty-printed. (#30)

Added

  • "Using Cherry from Elixir" guide on cherrybomb.dev: cherry_new scaffolding, the full mix task parity table, the library API (Cherry.build/1 / Cherry.check/1 with struct-accurate examples), and when to pick binary vs package; README gains the same cherry_new story, the task table, and standard badges (hex version, hexdocs, CI, license) on both packages. (#37)
  • Copy buttons on code blocks: a new copy-code.js island in both official themes puts a hover-revealed (always visible on touch) copy button on every pre and any data-copy element, clipboard-API based, token-styled, print-hidden; islands are now compiled into both themes by npm run build and a sync test pins the copies byte-identical. (#31)
  • Search keyboard shortcut: Ctrl+K / ⌘K focuses the Pagefind input in both themes, and the placeholder advertises it per-platform. (#31)
  • nav: entries accept position: :start | :end (default :end), so configured items can lead the nav ahead of the built-ins; cherrybomb.dev puts Guides before Blog. (#31)

  • Landing page pitch pass: a "Not just a blog" section covering the portfolio timeline, story pages, and the JSON-Resume-backed /cv, plus a brush-checked top-ten feature list (AI-agent-friendly CLI + skill included) replacing the poster wall. (#31)
  • cherrybomb.dev, the real thing: full brand pass on the dogfood site — cherrybomb theme + Pagefind search enabled, brand-derived favicon/apple-touch/OG-card/nav-mark assets, a poster-wall landing page with real CLI envelopes, and a core guide set (quick-start, authoring loop, verifier, themes, deploy, scripting & agents, upgrade), all check --strict clean; Pages workflow gains the Node step Pagefind needs. (#29)
  • Site enablers the dogfood exposed: heading anchor links + GitHub-style > [!NOTE] alerts (mdex, styled in both themes with zero new tokens), favicon-by-convention (static/favicon.ico|favicon.svg|apple-touch-icon.png → head links via Cherry.Site.Icons), nav: config for custom nav items (broken-link-checked), a page_class body class per section, and twitter:card upgrading to summary_large_image when social_image is set. (#28)
  • The cherry agent skill (skills/cherry/, fizzy-cli layout): a canonical SKILL.md teaching agents the safe-start checks, the author → build → check → publish → deploy loop, structured-diagnostic handling, provenance-based theme management, and binary self-update — plus a command reference generated from the CLI verb registry and CI-gated against drift (mix run scripts/regen_skill.exs), with .claude/skills/ and .agents/skills/ pointer shims. New verbs cannot land without the skill teaching them. (#27)
  • cherry upgrade: first-class self-update for the standalone binary (ADR 0007) — resolves the latest stable GitHub release (or --version for any tag, prereleases included), verifies this platform's asset against SHA256SUMS, and swaps the running executable rustup-style; --check reports without touching anything and works under mix too. Zero new deps (:httpc + OS trust store). (#26)

[0.1.0-rc.1] — 2026-08-14

Added

  • Standalone binary + release pipeline (ADR 0007): Burrito packaging (Cherry.Binary boots the CLI seam only in CHERRY_RELEASE builds), tag-gated release workflow with a five-target native-runner matrix, per-binary smoke tests, SHA256SUMS + build-provenance attestation, prerelease-aware publishing, and real checksum-verifying install.sh / install.ps1 served from cherrybomb.dev; workflow_dispatch runs the whole matrix as a publish-nothing dry run. (#24)
  • Published build action (holsee/cherry/action): composite GitHub Action doing toolchain setup, caching, cherry.build, and cherry.check (strict by default) in one uses: step — GH Pages users never touch Elixir locally; gen.action workflows now wrap it, and CI exercises the action against a fresh cherry.new site on both OSes. (#23)
  • Optional Pagefind search: search: "pagefind" in cherry.exs — the new Post pipeline stage indexes the emitted site (npx pagefind), both official themes grow a token-styled search island only when enabled, and default builds stay byte-identical and dependency-free. (#22)
  • mix cherry.new PATH (the cherry_new installer, installer/): scaffolds an agent-ready site — content dirs, config (including the Lumis NIF selection consumers must carry), first post, AGENTS.md documenting the operate loop, and a .claude/skills/publish skill; CI dogfoods a generated site with build + check --strict against every commit. (#21)
  • cherry theme.diff: managed drift — every overlay's three-way status against the installed theme (current / auto_updatable / conflict / untracked), --apply re-ejects auto-updatable ones with fresh provenance, and cherry check warns on stale or untracked overlays. Frozen theme copies, answered. (#20)
  • JSON Feed: feed.json (jsonfeed.org 1.1) alongside Atom with a discovery <link> on every page; cherry check now verifies it too. (#19)
  • The machine surface: every content route now carries an index.md markdown mirror alongside its index.html (posts, pages, blog index, tag pages, portfolio timeline, stories, CV), plus a generated /llms.txt per the llmstxt.org convention with absolute links to the mirrors; unlisted pages keep their mirrors but stay out of llms.txt, and mirrors never leak into the sitemap or feed. (#18)
  • --json contract audit: error envelopes now carry structured details (a failing check --strict --json returns its diagnostics machine-readably, not just prose), and a registry-complete contract test proves every verb's success and error envelopes decode — new verbs cannot land without envelope coverage. (#17)
  • cherry check: the verifier — builds in memory (writes nothing) and reports structured diagnostics (broken internal links, missing descriptions, images without alt text, duplicate titles, Atom feed sanity); errors exit 1, --strict promotes warnings, --json feeds the agent's build → check → fix loop. (#16)
  • The cherrybomb theme: second official theme carrying the brand — neon night wall by dark, poster paper by day, brush-stroke title, glowing code blocks — same contract and token API as the default, proving the swap is real; cherry gen.theme NAME [--from THEME] scaffolds an editable site-local copy. (#15)
  • cherry gen.project and cherry gen.talk: portfolio scaffolds with valid frontmatter (build-clean as generated), duplicate refusal, --json for agents. (#14)
  • The CV view: /cv/ as an employer-shaped projection of the portfolio (cv: curation, weight ordering, curated bullets), evidence-backed skills derived from merged calendar spans and linked to story pages, /cv.json in JSON Resume format, print-first stylesheet (black-on-white, no chrome, no split entries), and visibility: public | unlisted | off with noindex/sitemap handling. (#13)

  • Portfolio views: /portfolio/ timeline (profile header, year-railed entries, open source as standing roles), /story/:tag/ pages cross-linking positions/projects/talks/OSS/education with blog posts on one tag, Person JSON-LD, nav that knows the portfolio exists, and blog tag pages linking to their story. (#12)
  • Portfolio data layer: typed collections (portfolio/positions, projects, talks, oss, education) with introspectable schemas, portfolio.yaml profile, cv: curation blocks, and domain structs (Position, Project, Talk, OpenSource, Education, Profile, CV, Link) materialised via Cherry.Portfolio.from_build/1; entries are data-only (no page routes). (#11)
  • cherrybomb.dev deploy: Pages workflow building example/ on push to main with CNAME; reserved /install.sh and /install.ps1 installer paths (ADR 0007). (#10)
  • cherry gen.action: generates the GitHub Pages deploy workflow (build → upload → deploy, .nojekyll, CNAME for custom domains); example/ dogfood site (the future cherrybomb.dev) now builds in every CI run. (#9)
  • Default-on SEO and machine surface: canonical/Open Graph/JSON-LD head block on every page, Atom feed.xml, sitemap.xml, robots.txt; base_path support so every emitted URL works under GitHub project pages, proven by a base-path matrix test. (#8)
  • Authoring loop: cherry gen.post, cherry publish, and cherry serve (Bandit + watcher + SSE live reload, drafts included). (#7)
  • Default theme design: dual light/dark token palettes, class-linked syntax highlighting colored by theme tokens, no-flash theme toggle island (TS), token-discipline tests. (#6)
  • Theme contract v1: theme.exs manifest, three-level lookup, theme.list/theme.which/theme.eject with provenance; default theme renders posts, blog index, tag pages, 404. (#5)
  • Typed collections: posts + pages with introspectable schemas (mix cherry.schema), YAML frontmatter, MDEx GFM rendering, drafts/future filtering. (#4)
  • Build pipeline (load→validate→transform→layout→emit), cherry.exs site config, mix cherry.build, golden-fixture harness, determinism gate. (#3)
  • Cherry.CLI.run/1 seam with verb registry, --json envelopes, stable exit codes; mix cherry.version tracer. (#2)
  • Library skeleton with mix precommit gate, TS toolchain, and CI on ubuntu + windows. (#1)
  • Project constitution (AGENTS.md), design doc (DESIGN.md), ADRs 0001–0006, backlog.
  • Dual MIT / Apache-2.0 license.
  • End-state README with the Cherrybomb identity.
  • Devcontainer reference environment (Elixir 1.20.2 / OTP 28 / Node 22, pinned).
  • Minimal 0.0.1 package stub to claim cherry on Hex (published).
  • Portfolio dual-view design: Careers-style timeline + /cv/ web CV, both projections of one dataset (DESIGN.md §4).

Fixed

  • Date ordering everywhere dates entered sort keys: %Date{} structs term-compare field-alphabetically (day before year), so feeds, the blog index, tag pages, the timeline, and skills could order Jan 15 above Feb 1; all sort keys now go through Date.to_erl/1. (#19)
  • Byte-determinism of emitted JSON: atom-keyed map iteration follows atom-creation order and varies between VM runs, so JSON.encode! output was not reproducible; feed.json and cv.json now encode via Cherry.StableJSON with sorted object keys (ADR 0005). (#19)