Spillway CMSStructured content like Drupal. Page building like Puck. Setup like Ghost. AI that drafts, people who approve.
Menu

Open decisions and debatable changes

A running list of things that need Kevin's call or a second opinion. Newest at the top of each section. Items move to the roadmap or the spec once decided.

Decisions needed

  • OPEN QUESTION: a dedicated import format for design systems made in Claude. Brand → Import brand reads a design system exported as W3C design tokens (DTCG JSON) or CSS custom properties through its Design tokens source, and Figma variables with their modes (theming.md). The export format of a design system made in Claude is not confirmed, so no reader guesses one. Needed: the format (or a sample export), whether it carries component styles that should become block styles, and whether it should be a fourth source or detected inside Design tokens.
  • Shared brands: locking a part resets the children's overrides of it. When a parent locks a part a child had overridden, the child's override is dropped (published and drafted) and the part follows the parent; unlocking it later does not bring the child's old values back. Keeping the override dormant would need the child's own values stored apart from its effective brand. Also open: a site created later starts with an empty brand profile (or its parent's when it inherits); should a new site copy the default site's voice instead?
  • Brand import: logo rules have no field. Logo rules from a PDF guide (clear space, minimum size, do and don't) are listed as notes because the brand has no field for them; add one?
  • Brand import: voice rows drafted. Decided 2026-11-07: the brand profile is per site (migration 0041 copied the instance-wide one to every site) and has a draft like the rest of the brand, so applied voice rows land in the brand draft and reach the AI tasks only when a person publishes. PUT /api/brand still writes the published profile directly and keeps a draft's voice in step.
  • Brand import: unapplied imports kept 24 hours. An import waits for review in brand_import (logo uploads under <dataDir>/brand-imports/) and is pruned after BRAND_IMPORT_TTL_HOURS (24) on the next import. Long enough?
  • Page builder: a keyboard path for adding and moving blocks. Puck 0.24 drags blocks with the pointer only (it registers no keyboard sensor), so from the keyboard a block can be selected in the Outline, edited, duplicated and deleted, but not added or moved (upstream report, finding 4). Options: wait for Puck; or add our own controls through the overrides: an Add block menu in the header that inserts after the selected block, and Move up and Move down on the block's action bar. Neither is built.
  • Registry four-eyes review: second reviewer. Decided 2026-11-07: turn REGISTRY_FOUR_EYES=1 on for spillwaycms.com once a second reviewer account exists (approve refuses the version's publisher and its namespace maintainers with 403 four_eyes). Still open: who the second reviewer is.
  • Hosting plans. Decided on 2026-11-05 in the "Spillway hosting plans on Accorbis" document: three plans (Site $49, Campus $249, Enterprise $1,499), one $0.10/GB overage rule, Auto with a $49 floor and a hard cap, one price worldwide, R2 as the default store, one Accorbis-owned Cloudflare zone, email support included with priority support as a paid add-on, HA Postgres as a recommended choice at install on Campus, uptime credits on Pantheon's Elite schedule, 30% education and nonprofit discount, pay 10 months get 12, migration fee by size, AI on Accorbis keys with fair use then credits. Per site for Site and Campus; Enterprise is one multisite instance with 10 sites included and more by quote. Decided 2026-11-07: declining HA does not lower the Campus price; one Campus price includes it.

Debatable changes already made (revert if disagreed)

  • Kevin's decisions of 2026-11-07 (the "Spillway decisions for Kevin" document, all agreed):
    • Security reports go through GitHub's private vulnerability reporting, with security@spillwaycms.com as the email alternative; conduct reports go to conduct@spillwaycms.com, forwarding to two people at KWALL. v0.1.0 is tagged after those addresses and the brand-token fix landed.
    • Release tags are pushed by Kevin (this session's git access cannot push tags); merges happen when Kevin says so in the conversation.
    • Trusted modules on spillwaycms.com are reviewed by KWALL maintainers within 5 business days; no paid fast-track until there is a queue worth paying to skip.
    • Registry sign-in adds email magic links for publishers who are not on GitHub; Google sign-in is skipped for now. Not built yet.
    • Organizations keep both rules as built (the namespace wins over package ownership; a plain member publishes only when named a maintainer) and stay free until organizations ask for more; billing is revisited after launch.
    • The importer keeps landing in a workstream by default, and keeps AI on by default with the estimate shown before it is spent (built in #18).
    • The public /brand page is off by default and noindex when on (built in #14).
    • The page builder's accessibility is fixed on our side, with the issue text for Puck in docs/dev/upstream/puck-accessibility.md (built in #19).
  • Page builder on phones: the real builder, not a notice. At 390 px the builder now works in Puck's phone layout (the panels open from the rail at the bottom); the header wraps to two rows and its actions become icons with their names kept for screen readers. The alternative was a "The page builder needs a wider screen" notice with a link back to the editor below a set width. Chosen because the phone layout passes the accessibility crawl and quick fixes (a typo, a style) are worth having on a phone; dragging blocks there is still awkward.
  • Importer AI usage: decided, on by default with an estimate. Decided 2026-11-07 (Kevin): AI stays on by default when a provider is configured, and the estimated AI calls and tokens, with the share of the monthly token cap, are shown before the crawl starts (wizard, POST /api/imports/estimate, spillway import) and again, measured, before the classification spends them. Each job can turn it off (Use AI for type proposals, --no-ai, options.useAi: false); above the remaining cap the wizard asks for an explicit confirmation and the cap enforcement is unchanged. Reading the code corrected the premise: the type proposals are one call per classification (two with the retry) carrying every cluster, not one call per cluster, and no other importer step uses AI. Pricing is not shown because the AI layer meters tokens only (importer.md).
  • First release: v0.1.0. Decided 2026-11-06 (Kevin left it to us): nothing was ever published as 0.1.0, so the first public release is v0.1.0 with everything folded in. The Unreleased notes are folded into one [0.1.0] entry; the tag is pushed by hand as docs/dev/releases.md describes, since pnpm release refuses a version equal to the current one and is for every release after.
  • Repository and image names: kwall-llc. Decided 2026-11-06: the code stays at kwall-llc/spillway-cms, so the release image is ghcr.io/kwall-llc/spillway-cms (the release workflow names it after the repository) and the docs, the fleet's default image and the links say so.
  • npm: the @spillway-cms scope. Decided 2026-11-06: Kevin created the spillway-cms organization on npmjs.com, so the packages are @spillway-cms/shared, blocks, sdk, client, astro and next (and the private workspace packages follow the same scope). Add an NPM_TOKEN repository secret (an automation token with publish rights on the org) and the release workflow publishes the MIT packages with provenance. core and fleet (AGPL) ship as the image, not on npm.
  • Auto plan: Redis step. Decided 2026-11-06: Auto may delete the Redis add-on after the site has run cleanly on the in-process cache for the step's dwell time, and recreates it with the same plan and region on restore. The cache is disposable, so no data is lost; every other deletion stays forbidden.
  • Cost model object store. Decided 2026-11-06: R2 is the default and the cost model uses its rates ($0.015/GB-month, zero egress). It is cheaper and allowed through Cloudflare's CDN under its terms; Tigris stays a per-site option for customers who want one Fly invoice.
  • Backups and key files. Decided 2026-11-06: backups must restore completely, so they carry the instance's key files, form uploads and installed modules. Without BACKUP_ENCRYPTION_KEY the run is flagged and the System tab and /status.json warn; the fleet sets the key on provisioned sites.
  • Importer off-site redirects: decided, drop by default. (G-18) Source redirects whose target is another host are left out of the landing, counted in summary.redirectsDropped and noted; offsiteRedirects: 'keep' (wizard checkbox, --keep-offsite-redirects) lands them. Alternative was keeping them with only the audit row.
  • Registry smoke load in the worker. (G-19) Done: the sandbox worker compiles the Wasm and checks the ABI; the main thread only reads the memory limits from the header. Nothing to decide; listed so the review item has a home.
  • Shared saved views: managers share, a person's own default wins. Personal views stay user preferences (views:<table>); shared views are rows of their own (shared_view, migration 0036) for everyone on a site or chosen roles, written with the new views.share permission (manager and admin). A person's own default beats a shared default; a shared view for named roles beats one for everyone. Alternatives: let editors share too, or let a shared default override personal ones.
  • Kit dependencies: built as proposed. requires: { kits, modules, core } in kit.json, resolved recursively at install from the bundled kits and the registry (kits.md). The bundled kits declare none; spillwaycms.com installs its website kit (in kwall-llc/spillwaycms.com) and the bundled docs kit as two steps rather than one depending on the other.
  • Importer home page. The imported site's / lands at /home because the setup wizard's home page already holds /. Alternative: replace the starter home page when the owner asks. Current: /home plus a note in the report.
  • Importer menus. Crawled navigation lands as import-main and import-footer menus so nothing overwrites the site's menus; the owner swaps them in. Alternative: replace main when the site is empty.
  • Importer created types. Only the title field is required on types the importer creates (an import cannot promise every field is filled). Fine, or require the body too?
  • Page cap semantics. The cap counts fetched plus failed pages; assets and redirects do not count.
  • registry.publish is granted to every role including viewer so anyone with an account on spillwaycms.com can publish; review and namespaces limit abuse. Alternative: a separate "publisher" role.
  • Registry pages on the public site are cached for at most 60 seconds and evicted on every registry mutation.
  • Theme = a kit with kind: "theme" and at least one screenshot, not a separate package format.
  • Registry client fetches now pass the SSRF guard; a registry on a private network needs SPILLWAY_ALLOW_PRIVATE_URLS=1.
  • Module tarballs from the registry are capped at 20 MB.
  • Catch-all pages: a page whose last path segment is any (for example /registry/any) renders every unmatched path below its parent. Used for package and publisher pages; a reserved word in paths as a result.
  • The docs site body is generated from the repository Markdown at build time; there is no Markdown field on the doc type.

Not verified against real services

Fly.io provisioning, Okta and Entra SAML and OIDC, GitHub OAuth (fake only), HubSpot, SMTP, the four AI providers, Meilisearch, Tigris and R2. Each needs credentials and a short live run; see docs/dev/roadmap.md.

Spillway 0.1.0 · Edit this page on GitHub

Was this page helpful?