Gap analysis: KWALL Drupal starter vs. Spillway
Read-only comparison of the higher-education Drupal starter at kwall-drupal-starter (Drupal 11 on Pantheon, custom upstream) against Spillway on the phase-2b branch, as of 2026-11-05. Spillway status was taken from the code, not the roadmap: "Shipped" means a route, table and admin screen exist; "In progress (2b)" means the branch has a dependency, contract or scaffold but no working feature yet; "Planned" cites the roadmap phase; "Missing" means neither.
What is actually on phase-2b beyond the committed v0.2: a 0008_api_tokens migration, a packages/fleet scaffold (types, errors, http), pg and @extism/extism added to core/package.json, jose and node-saml added in the last commit, and a Phase 3 contract block in packages/shared/src/index.ts for API tokens and the MCP server. No SSO, menu, personalization, analytics, Postgres dialect, MCP endpoint or module runtime code exists yet.
1. Inventory of the Drupal starter
The starter is Pantheon's drupal-composer-managed upstream with KWALL's modules, theme and config. config/ and recipes/ are empty; the real site configuration (1,634 files) is shipped inside web/modules/custom/kwall_starter_helper/site-configs/full-feature-edu, with variants city (adds a Services type and a water-leak webform), portal (adds an Audience vocabulary, permissions_by_term, masquerade and a login-walled site) and news-site-fixes. core.extension.yml enables 264 modules on the standard profile. Thirty-one composer patches are carried against core and contrib.
Contrib modules by purpose
| Purpose | Modules (composer require; those not enabled in the edu config are marked with an asterisk) |
|---|---|
| Content modeling | paragraphs, paragraphs_library, layout_paragraphs, entity_reference_revisions, field_group, field_permissions, auto_entitylabel, name, address, telephone_*, time_field, date_recur, date_recur_modular, color_field, tablefield, block_field, viewsreference, inline_entity_form, entity_reference_tree, entity_type_clone, entity_clone\, quick_node_clone\, replicate(+ui, actions), single_content_sync, default_content, storage, year, maxlength, node_title_help_text, allowed_formats\* |
| Editorial workflow and scheduling | content_moderation, workflows, moderation_sidebar, moderation_dashboard\, scheduler, scheduler_content_moderation_integration, content_lock (+timeout), diff, workbench_email, node_revision_delete, views_cm_current_state, override_node_options\, entity_usage, paragraphs_report\, checklistapi\ |
| Media | media, media_library, media_directories (+ui, editor), media_entity_browser\, entity_browser, entity_embed, embed, focal_point, crop, image_effects\, imagemagick, exif_orientation\, file_mdm, filefield_paths, media_entity_file_replace, media_file_delete, media_library_edit\, media_link_enhancements\, media_pexels, svg_image, webp, blazy, slick (+paragraphs), file_download_link, imce\, responsive_image, stage_file_proxy\* |
| WYSIWYG | ckeditor5, linkit, anchor_link, ckeditor_bs_grid, ckeditor_iframe\, ckeditor5_icons\, ckeditor5_paste_filter\*, token_filter, responsive_tables_filter, openai_ckeditor |
| SEO | metatag (+open_graph, twitter_cards, async_widget), schema_metatag (article, event, person, place), simple_sitemap, pathauto, token, rabbit_hole, easy_breadcrumb |
| Redirects | redirect, path_redirect_import\, views_url_alias\ |
| Menus | menu_block, menu_item_extras, menu_link_attributes, fontawesome_menu_icons, group_content_menu |
| Search | search_api, search_api_solr, search_api_pantheon, search_api_autocomplete, facets, facets_pretty_paths, better_exposed_filters, selective_better_exposed_filters, select2 |
| Forms | webform (+ui, submission_log, export_import), captcha, recaptcha, honeypot |
| Layout | layout_builder (+restrictions, modal, iframe_modal, styles, admin_theme), layout_paragraphs, bootstrap5 theme, views_bootstrap, fences, block_class, collapsiblock\*, inline_block_title_automatic |
| Accessibility | editoria11y, extlink, accessible-slick library, dubbot\* |
| Performance and caching | redis, memcache\, pantheon_advanced_page_cache\, cache_control_override (portal only), minifyhtml, variationcache, blazy lazy-load |
| Security | seckit, password_policy, username_enumeration_prevention, userprotect, autologout\, shield\, flexible_permissions, permissions_by_term (portal), masquerade (portal), reroute_email, mail_login |
| Authentication | samlauth\, simplesamlphp_auth\, cas\*, externalauth |
| Analytics and marketing | google_tag, google_analytics, addtoany, social_media_links, eu_cookie_compliance\*, smart_content (+block, browser) for personalization, gtranslate |
| AI | ai, ai_provider_anthropic, ai_provider_openai, gemini_provider (not enabled); openai (+chatgpt, ckeditor, content, prompt) enabled |
| Higher-ed and site features | calendar, calendar_link, views_ical, date_recur (events); geolocation, geofield, geocoder (campus map); group, gnode, gcontent_moderation (department delegation); feeds\, migrate_plus, migrate_tools, migrate_source_csv (imports); taxonomy_manager, taxonomy_import, taxonomy_unique, tvi; entityqueue\; views_bulk_operations, views_bulk_edit\* |
| Admin and dev | admin_toolbar, module_filter, environment_indicator\, config_ignore, config_log, config_partial_export, config_rewrite, config_filter, devel\, upgrade_status, responsive_preview, theme_switcher, twig_tweak, asset_injector, jsonapi_extras\* |
Custom modules
| Module | What it does |
|---|---|
kwall_starter_helper | Ships the full site config sets, Google-font catalog endpoints, and a Drush command that pulls brand colors, fonts and logo from KWALL's customer portal (Accorbis) on Pantheon. |
kwall_academic_programs | Program content type, Degree Type and Interest Area vocabularies, program grid with AJAX program finder. |
kwall_calendar | Month/week/day/year calendar views over recurring events, iCal feed, mini calendar, current-week and list/calendar toggle blocks. |
kwall_directory | Person content type, Department and Person Type vocabularies, filterable faculty and staff directory. |
kwall_news | Article form and image styles, Newsroom and related/featured article views, News Category and Article Type vocabularies. |
kwall_alerts | Alert content type with level, expiry and dismissability; alert block fed by a REST export with a dismissed-cookie. |
kwall_header_banner | Header Banner paragraph and block (image, color filter, style, hide-title) per page. |
kwall_map | Location content type with address and geolocation and a Google Maps campus map with custom overlay. |
kwall_group | Blocks that render a Group's menu (department sub-navigation) for the site_section group type. |
kwall_breadcrumbs | Three breadcrumb builders (node, views, path). |
kwall_share_bar | Social share block. |
kwall_components | Preprocess and templates for document list, heading, CTA, news list and glossary paragraphs. |
kwall_custom_admin | Admin theme CSS and JS tweaks, paragraph widget adjustments. |
kwall_media_directories_extender | Folder-style media browser tweaks. |
kwall_city | City variant: Services content type and grid (depends on webform). |
kwall_portal | Login-walled site: anonymous redirect to login, permissions_by_term integration. |
kwall_saml | Domain-aware SAML redirect and attribute mapping on top of simplesamlphp_auth. |
curator | Imports Curator.io social posts as Social Post nodes on cron. |
custom_module | Site glue: entity access, AJAX program-finder and calendar subscribers, year exposed-filter helper, token alters. |
Content types and fields
| Type | Fields (short) | Path pattern |
|---|---|---|
| Basic Page | body, hero_content (header_banner), paragraphs (21 component types), below_content_paragraphs (full-width, 28 types), page_summary, thumbnail, hide_sidebar, meta_tags | [menu parents]/[title] |
| Landing Page | body, banner_content (header_banner, hero_slideshow, hero_video), paragraphs, below_content_paragraphs, page_summary, thumbnail, legacy_url, migration_status/notes, meta_tags | same |
| Homepage | hero_slideshow (slideshow or video), below_content_paragraphs, meta_tags | front |
| Layout Builder | layout_builder__layout, hero_slideshow, meta_tags | same |
| Article | body (required), article_type, categories, featured, publish_date (required), thumbnail, paragraphs, sidebar components, redirect, legacy_url, migration fields | article/[title] |
| Event | recurring_dates (date_recur, required), body, event_type, topics, event_location (term) plus location name/address/geo/link, featured, featured_image, paragraphs | events/[title] |
| Person | first/last name (required), title, department, person_type (required), email, phone, photo (person_image), biography, hero_content | directory/[title] |
| Program | banner image and thumbnail (required), description, page_summary, degree_type, academic_interest, related_programs, program components, sidebar components, hero_content | programs/[title] |
| Location | address, geolocation, description, images, images block, more link, related links, link_from_map, section_title | campus-map/[title] |
| Alert | alert_body, alert_level (required), expiration_date, not_dismissable | none |
| Testimonial | body, image, person_info, link_to_page, link_to_other_page | testimonials/[title] |
| Social Post | curator id/feed, network, body, date, image, url, author fields | none |
| Services (city only) | description, phone, thumbnail, services_area, paragraphs | services/[title] |
Paragraph (component) types: 58
Layout: layout (margins, padding, background color or media), from_library, drupal_block, view_reference, webform_reference. Hero and banners: header_banner, hero_slideshow and hero_slide, hero_video, banner_with_* (three), announcement_banner. Content: wysiwyg, heading, image, gallery, media_with_text, video_text, embedded_video, uploaded_video, accordion and accordion_item, tabs and tab_item, glossary, document_list and item, label_with_text, countdown. Calls to action: cta_block, buttons, buttons_cta, button_with_label, full_width_image_and_cta, featured_image_text_cta, icon_link, icon_links_grid, colored_icons_grid, sidebar_links_with_icons, menu_quick_links. Data-driven: news_list, featured_news, featured_events, featured_news_and_events, testimonials, person_spotlight, stat_row and stat_item, colored_stat_bar_item, card_item, card_with_image_item, contact_information, hours_of_operation and item, social_media_icons, footer_links_column, footer_contact_link.
Block content types: basic, component_block, paragraphs_block, header_banner, layout_block, links_with_heading, footer_block (site name, logo, address, phone, copyright, links, social links).
Taxonomies (15)
article_type, categories, tags, news_category, degree_type, interest_area (with icon), department, person_department, person_type, event_type, event_topic, event_location (links to a campus-map Location), document_category, glossary_section, media_directory (folders for the media browser). Hierarchy is Drupal-native; tvi lets a vocabulary pick its term-page view.
Roles, workflow, moderation
Roles: administrator, content_editor (create and edit all content types, draft and review transitions, schedule, view scheduled, editoria11y, media browsers), content_publisher (editor plus publish and archived_published transitions, homepage), group_member (media and toolbar only; real rights come from group roles), plus anonymous and authenticated. Editorial workflow: Draft, Needs review, Published, Archived, applied only to article, homepage and page bundles. Group type site_section (departments) with group roles group_administrator, group_publisher, group_editor, member, outsider; group content is page only, group menus via group_content_menu. Content lock on nodes with a 180-minute timeout; diff on revisions; workbench_email enabled with no templates in config; scheduler enabled for alert, article, event and page (publish and unpublish), with "view scheduled content" and three scheduled-content views.
Views (58)
Public: newsroom (keyword, category, publish year, 10 per page), directory (keyword, department), programs_grid (keyword, AJAX finder), calendar_main_page (day, week, month, year, list; keyword, date, type), calendar_calendar_view, mini_calendar, ical_events_feed, campus_map, documents (keyword, department, category), glossary, search (Solr full text with excerpt), archive, taxonomy_term, frontpage. Blocks and attachments: featured news and events, related articles/events/programs, news_lists, filtered listings for articles and events, alerts (block and REST export), social_grid, connect_with_us. Admin: content, moderated_content, moderated_group_content, scheduled content/media/terms, locked_content, redirect, media and media_library, paragraphs_library, config_log, editoria11y results, webform_submissions, group members and nodes, social_post_dashboard.
Menus, webforms, SEO, theme
Menus: main, mega-menu (menu links carry a Layout Builder layout), footer, header-action-navigation, utility-navigation left and right, plus account, admin, tools. Webforms: contact (two email handlers, captcha), email_signup, review_webform (fieldsets, flexbox, radios, checkboxes, datetime, terms of service). Metatag defaults for global, front, node (title, description from page summary, OG image from thumbnail), taxonomy term, user, 403 and 404; schema.org via schema_metatag for article, event, person, place; simple_sitemap per bundle with priorities; pathauto patterns per type; redirect with auto-redirect on alias change and query passthrough; rabbit_hole per type. Theme kwall_starter on bootstrap5: twelve regions (alerts, nav branding, nav main, nav additional, hero, breadcrumb, two sidebars, content, content bottom, footer, invisible), theme settings for eleven colors (three primary, secondary, three alert levels, four neutrals), three fonts from a Google-font catalog, logo; Webpack and SCSS build; 67 image styles and 6 responsive image sets on bootstrap5 breakpoints; Twig overrides for 27 paragraphs, menus, views, nodes.
Conventions from CLAUDE.md and README
Pantheon Integrated Composer; composer code-sniff and code-fix with Drupal and DrupalPractice standards; frontend assets built per module with Gulp or Webpack and committed; configuration shipped in module config/install and the helper's site-config sets rather than config/sync; Quicksilver scripts run drush site-install and notify a Firebase function on deploy_product; branding is pulled from the KWALL customer portal by site slug.
2. Comparison table
| Capability | Starter | Spillway status | Note |
|---|---|---|---|
| Scheduled publish and unpublish | scheduler + scheduler_content_moderation_integration, per-type, cron, "Scheduled content" views | Missing | No publishAt/unpublishAt on Entity, no scheduler, no cron. Roadmap does not mention it. |
| Content moderation states | content_moderation: draft, review, published, archived with per-role transitions; only 3 bundles | Shipped v0.1/v0.2 | draft, review, published; submit, publish, unpublish, reject (v0.2). archived is defined but no endpoint sets it. Applies to every type. |
| Revision history and diff | core revisions + diff module (inline, split, unified) | Shipped v0.1 (list, restore); diff Missing | Revisions are full snapshots; no comparison view. |
| Revision pruning | node_revision_delete per type | Missing | Revisions grow unbounded. |
| Redirects | redirect module, auto-redirect on alias change, query passthrough, admin view, CSV import | Shipped v0.2 | 301/302, enabled flag, hit counter, auto-redirect on slug change, query passthrough, 404 log with counts. No bulk import, no wildcard. |
| 404 handling | metatag 404 defaults, fast_404 | Shipped v0.2 | Rendered 404 page in site shell, path_miss table. No custom 404 page content. |
| Menu management | core menus (9), menu_block, menu_item_extras, mega-menu with Layout Builder, group menus | In progress (2b), nothing in code | No menu table, API or admin; blocks take raw hrefs. |
| Taxonomy | 15 vocabularies, hierarchy, term pages via tvi, taxonomy_manager | Missing | Only select options and reference fields. No vocabulary entity, no hierarchy, no term pages. |
| Media library | media types (8), media_directories folders, entity browsers, Pexels, file replace, usage | Shipped v0.1 (basic) | Flat library, upload, alt, title, search, MIME sniffing, 25 MB cap. No folders, no media types, no replace, no usage. |
| Image styles, responsive, focal point | 67 image styles, responsive_image, focal_point, crop, imagemagick, webp | Missing | Files are served as uploaded; Image block sets loading="lazy" only. |
| WYSIWYG | CKEditor 5: headings, styles, alignment, tables, Bootstrap grid, anchors, linkit to nodes and files, media embed, video embed, OpenAI | Shipped v0.1 (Tiptap richtext) | Internal link picker, media embed, tables and embeds in rich text are not present; Text block has an HTML mode. |
| Components vs blocks | 58 paragraph types, layout_paragraphs, paragraphs library, Layout Builder | Shipped v0.1 (13 Puck blocks) | Hero, Heading, Text, Image, Columns, CallToAction, Cards, Spacer, Divider, Embed, Button, Form, Collection. No reusable (library) blocks, no accordion, tabs, gallery, stats, video, person spotlight, news list. |
| Listings with exposed filters | Views with BEF, selective filters, pagers, AJAX | Shipped v0.2 (partial) | Collections: admin-defined filters, sort, limit, paginated items API, Collection block. No visitor-facing filters or keyword search on a listing. |
| Site search with facets | search_api_solr on Pantheon, facets (5 sources), autocomplete | Shipped v0.2 (basic) | SQLite FTS5 over title and text fields, /search page, type filter. No facets, no autocomplete; Meilisearch planned (Phase 3). |
| Webforms | webform: 40+ element types, wizard pages, conditional logic, file uploads, email handlers, submission views, CSV export, captcha, honeypot | Shipped (Phase 4) | 9 field types, required, conditional logic (visibleWhen, server and browser), multi-step forms with server-side step validation, file uploads (private store, type from bytes, download for submission viewers), Turnstile or hCaptcha by site setting, submissions list, CSV, redeliver, webhook/HubSpot/email connectors, honeypot, timing, rate limit, UTM capture. Salesforce, Slate, Mailchimp still planned. |
| Meta tags per page | metatag field on page types, defaults per bundle, OG and Twitter | Shipped v0.1/v0.2 (partial) | seoTitle, seoDescription on Page; site-level suffix, description, OG image, robots. No per-entity OG image or Twitter card fields on other types. |
| Schema.org | schema_metatag (article, event, person, place) | Shipped v0.2 (partial) | WebSite, Organization, WebPage, Article, BreadcrumbList generated automatically. No Event, Person, Place, Course. |
| XML sitemap | simple_sitemap with per-bundle priority, cron generation | Shipped v0.2 | /sitemap.xml, lastmod; no priority, no exclusion per type. Also /robots.txt and /llms.txt. |
| Google Tag Manager and GA | google_tag, google_analytics with path and role exclusions | Missing | Nothing in the shell; "personalization and analytics" is in progress for 2b but no code. |
| Accessibility tooling | editoria11y (in-page checker and reports), extlink, accessible-slick | Shipped v0.2 (partial) | Pre-publish checks: alt text, heading order, one H1, SEO length, banned words, dead internal links; publish blocked on errors. No site-wide report or in-page checker. |
| Alerts and emergency banner | kwall_alerts type, level, expiry, dismissable, REST feed, block | Missing | No site-wide region, no expiry. |
| Events with recurrence and calendar | date_recur with modular widget, calendar views, iCal feed, Add-to-calendar links | Missing | date field only; no ranges, recurrence, calendar or iCal. |
| People and faculty directory | Person type, department and type vocabularies, filterable directory view | Missing (kit possible) | A person type can be modeled; no directory listing with filters. |
| Programs and degrees | Program type, degree and interest vocabularies, AJAX program finder | Missing (kit possible) | Same: fields can be modeled; finder needs filters and taxonomy. |
| Course catalog | Not in the starter (programs only) | Missing | Neither product has one. |
| News and press | Article type, categories, newsroom with filters, featured and related views, press release article_type | Shipped v0.1 (Post type), listing partial | Post has summary, body, hero, date. Collections give latest and filtered lists; no related-content or category filters for visitors. |
| Global header and footer | Theme regions, footer_block content block, branding block, utility menus, share bar, social links | Missing | The shell renders site name and theme CSS only; no header or footer configuration. |
| Multi-site and department delegation | group + gnode + gcontent_moderation: site sections with their own roles, menu, moderated content | Planned (Phase 3 multisite); delegation Missing | Spillway roles are global; no ownership scope smaller than the site. |
| Roles | content_editor, content_publisher, group roles, administrator; 50+ granular permissions | Shipped v0.1 | viewer, author, editor, manager, admin with 16 permissions. Authors edit own only. No per-type permissions. |
| Workflow notifications | workbench_email (enabled, no templates), user mail | Missing | Review queue exists; no email or in-app notification on submit, reject or publish. |
| Content locking | content_lock on all node types, 180-minute timeout, break-lock | Missing | Concurrent saves last-write-wins. |
| URL aliases and patterns | pathauto patterns per type including menu-parent paths; bulk regenerate | Shipped v0.1 | pathPattern per type with slug, id, type, yyyy, mm tokens; no menu-parent token, no bulk regenerate. |
| Translation and multilingual | language module enabled (en only), gtranslate widget, content_translation settings present | Planned (Phase 3) | locale column and locales setting exist; one locale rendered. AI translate task exists. |
| Caching | Pantheon AGCDN, redis, page cache 24h, cache tags via pantheon_advanced_page_cache | Shipped v0.1 | In-process LRU with tag invalidation, Cache-Tag and s-maxage headers for a CDN; no purge call; Redis planned. |
| Environment configuration | pantheon.yml, settings.php per environment, config_ignore, config_log, environment_indicator, reroute_email | Shipped v0.1 (partial) | Env vars for port, data dir, DB path, secret, base URL, log level. No environment indicator or mail reroute. |
| Backups | Pantheon automated backups and multidev | Shipped v0.1 (documented) | SQLite online backup plus the media folder; spillway export and import CLI. |
| Config as code | Drupal config export, module config/install, site-config sets | Missing | Types, collections and forms are only in the database; no export or import of structure separate from content. |
| Clone content | quick_node_clone, replicate, entity_clone | Missing | No duplicate action. |
| Bulk operations | views_bulk_operations, bulk edit, pathauto bulk | Missing | Lists have no multi-select actions. |
| Audit and config log | config_log, dblog, webform_submission_log | Shipped v0.1 (table only) | audit_log written on every write; no UI or API. |
| SSO | samlauth, simplesamlphp_auth, cas (required, not enabled), kwall_saml, externalauth | In progress (2b): jose and node-saml deps only | Roadmap Phase 2: OIDC, SAML, SCIM. |
| API for integrations | JSON:API, REST (alerts), jsonapi_extras | In progress (2b): API token contract and table | Full JSON API shipped with cookie auth; bearer tokens and MCP are contract-only. |
| AI assistance | openai (CKEditor, content, prompt), ai module with Anthropic, OpenAI, Gemini providers | Shipped v0.2 | Brand profile, page from brief, assist, type from description, alt text, usage report; multi-provider. |
| Personalization | smart_content with browser segments | Missing (2b in name only) | Roadmap Phase 3. |
| Spam protection | captcha, recaptcha, honeypot | Shipped (Phase 4) | Honeypot, timing and rate limit on form submit; Cloudflare Turnstile or hCaptcha verified server-side (Site settings → Forms); reCAPTCHA is not offered. |
| Campus map | kwall_map with geolocation and overlay | Missing | Not in the spec. |
| Social feed import | curator | Missing | Not in the spec. |
| Glossary, documents library, hours of operation, countdown, testimonials, share bar | Paragraphs and views | Missing | Not in the spec; see gap list. |
3. Ranked gap list
Critical
- Scheduled publish and unpublish. The starter enables scheduler on pages, articles, events and alerts, with a "Scheduled content" view and moderation integration, and a campus communications team relies on it for embargoed news, term-start pages and alert expiry. Spillway has no date on the entity and no clock. Implement in core, Phase 2b: add
publishAtandunpublishAtcolumns toentity, acontent.schedulepermission (editor and up),POST /api/entities/:id/scheduleandunschedule, and a lightweight in-process ticker (every 60 seconds, plus aspillway run-scheduledCLI for cron on multi-instance setups) that publishes throughpublishEntityso checks, cache tags and audit all apply. Show a "Scheduled" filter in the content list and a banner on the entity. Unpublish-at should move the entity toarchived, which also gives that status its first endpoint.
- Menu management. Every site in the starter has a main menu, a mega menu, two utility menus, a footer menu and department sub-menus; pages pick their menu parent on the edit form and
pathautoderives the path from it. Spillway has no menu at all and the shell has no navigation. Implement in core, Phase 2b (already listed as in progress): amenuandmenu_itemtable (label, link to entity or URL, parent, weight, new tab, optional description and icon),GET/PUT /api/menus/:key, a drag-and-drop tree editor, a "Menu placement" rail on the entity editor, a{menu}path token, and aNavigationblock plus header and footer slots in the shell that render named menus.
- Global header and footer. The starter configures branding, utility navigation, search, social links, address and copyright through theme regions and a footer block. Spillway renders a bare shell. Implement in core alongside menus: a per-site "layout" document with header and footer zones edited in Puck (logo from media, menus, search toggle, social links, address, copyright) and rendered around every page; cache-tag it
site:<id>so one save invalidates the whole site.
- Taxonomy. Fifteen vocabularies drive every listing filter (department, category, degree type, event type) and several term pages. Spillway's
selectoptions are flat strings owned by one type. Implement in core, Phase 2b or early 3: avocabularyandtermmodel (hierarchy via parent, slug, description, optional path pattern for term pages), ataxonomyfield type that references a vocabulary (single or multiple), term pages rendered as a Collection filtered by term, and term filters exposed to Collections and search.
- Image styles and responsive images. The starter has 67 styles, focal-point cropping and WebP. Spillway serves originals, so a 4 MB hero is sent to a phone. Implement in core, Phase 2b: on-demand resizing with
sharpbehind/media/:id/:filename?w=&h=&fit=&format=, a focal point stored onMediaItem, named presets in site settings,srcsetandsizesemitted by the Image, Hero and Cards blocks, and cached derivatives underDATA_DIR/media/derived.
High
- Visitor-facing listing filters and pagination. The newsroom, directory, calendar and program finder all have keyword plus taxonomy and date filters with pagination. Spillway Collections are fixed queries. Extend the Collection block (core, Phase 2b):
filters: 'none' | 'inline', anexposedlist of fields (keyword over searchable fields, taxonomy, date range, select), server-rendered filter form with query-string state, page size and pager, and anitems.jsonendpoint for progressive enhancement. This unlocks the directory, newsroom and program finder as kits rather than code.
- Events with recurrence and calendar. Events are the highest-traffic listing on most campus sites. Implement as a module and kit, Phase 3 once modules exist (or as core field types sooner): a
daterangefield type with timezone, anrecurrencefield (RRULE, exclusions, occurrence expansion stored in a side table so Collections can sort by next occurrence), aneventtype in a higher-ed kit, aCalendarblock (month, week, list), an/events.icsfeed and per-event add-to-calendar links.
- Higher-ed content kit (people, programs, news, alerts). The starter ships Person, Program, Article, Event, Alert, Location and Testimonial with their vocabularies, listings and components. Spillway should ship an installable "Higher education" kit (Phase 2b, after taxonomy): content types, vocabularies with seed terms, collections and block presets, plus sample pages. Alerts need a small core hook: a site-level
alertssetting rendered above the header with level, expiry and a dismissed cookie.
- Rich text internal links and media embed. Editors link to pages by searching for them (linkit) and embed images, documents and videos inline. Implement in core, Phase 2b: a link dialog in the Tiptap editor that searches entities and media (storing
entity:<id>so links survive slug changes, resolved at render), image and document embeds that reference media ids, and tables. Keep the HTML sanitized with an allow-list rather than the current trust-the-editor policy.
- Reusable component library.
paragraphs_libraryandfrom_librarylet one promo or contact card be placed on many pages and edited once. Implement in core, Phase 2b: ashared_blockentity (a saved Puck component tree with a label), a "Save to library" action on any block, aSharedblock that references it and is resolved at render, taggedshared:<id>for invalidation.
- Department delegation. The
site_sectiongroup type gives a department its own editors, publishers, pages and menu while the central team keeps the shell. Spillway roles are site-wide. Implement in core, Phase 3 alongside multisite: asectionentity (name, path prefix, menu),sectionIdon entities, and section-scoped role assignments (editorof section X). Permissions checks already go throughrequireEditAccess, which is the seam.
- Workflow notifications. workbench_email is enabled for submit, approve and reject emails. Implement in core, Phase 2b: an outbox table plus the existing SMTP connector; events
entity.submitted,entity.rejected,entity.published,comment.addedwith templates in site settings; a per-user digest preference; and the same events on a webhook for Slack or Teams.
Medium
- Content locking. Two editors on one landing page is common on a marketing team. Implement in core, Phase 2b:
entity_lock(entity, user, expires) acquired on open, refreshed by heartbeat, released on close, breakable bycontent.edit_any, with a 409 on save when another user holds it.
- Revision diff and pruning. Add a side-by-side comparison of two revisions (field by field, block by block for layouts) in the admin and
GET /api/entities/:id/revisions/:a/diff/:b; add a per-type "keep N revisions" setting and a prune job. Core, Phase 2b.
- Webform parity: conditions, multi-step, file uploads, captcha. Done in Phase 4:
visibleWhenon fields,steps, afilefield type stored underDATA_DIR/form-uploads, Turnstile or hCaptcha by site setting (docs/api.md "Forms"). Salesforce, Slate and Mailchimp connectors stay on the roadmap.
- Google Tag Manager and analytics settings. A
trackingsection in site settings (GTM container id, GA4 id, consent mode, path exclusions), emitted by the shell, with adataLayerevent on form submit. Core, Phase 2b; small.
- Per-entity SEO fields on every type and richer schema.org. Promote
seoTitle,seoDescription,ogImage,noindexandcanonicalto built-in fields on any type with a path (hidden behind an "SEO" tab), and generate Event, Person and Course JSON-LD from the kit's types. Core, Phase 2b.
- Search facets and autocomplete. Add type and taxonomy facets with counts to
/searchand/api/search, and a suggest endpoint; this stays on FTS5 until Meilisearch (Phase 3).
- Config as code. The starter's whole structure is YAML that ships with the upstream, which is how one team runs dozens of sites. Add
spillway export --structureandimport --structurefor types, vocabularies, collections, forms, menus and theme, with a kit manifest format; this is also what the fleet API will push. Core, Phase 3 with fleet.
- Clone and bulk actions. Duplicate entity, and multi-select publish, unpublish, archive, delete and change-author on the content list. Core, Phase 2b; small.
Low
- Media folders, media types and replace-in-place. Folders (or tags) in the media library, a document type with a download block, replacing a file while keeping its id, and "where is this used" from the existing
referencetable. - Campus map and locations. A
locationtype with address and coordinates and aMapblock; a kit item once a map provider setting exists. - Glossary, documents library, hours of operation, countdown, stats, testimonials, share bar, social feed. Blocks for the higher-ed kit; the social feed importer is a module candidate.
- Environment indicator and mail reroute. An
ENVIRONMENTvariable shown in the admin header and aMAIL_REROUTE_TOthat catches all outbound mail outside production. - Translation widget. gtranslate-style machine translation is a stopgap; proper localization is Phase 3 and already has the
localecolumn and AI translate task.
Items the starter has that the Spillway spec never mentions: scheduling, taxonomy, menus, image styles and focal point, alerts, events and calendar, content locking, revision diff and pruning, department delegation via groups, GTM, campus map, glossary, documents library, social feed import, config as code, clone and bulk actions, environment indicator, mail reroute, captcha.
4. Already better in Spillway
- Review workflow in one piece. Draft, review, publish, reject with a note, a review queue, field-level comments and pre-publish checks that block publishing on errors are core, where the starter needs content_moderation, workflows, moderation_sidebar, moderation_dashboard, workbench_email and editoria11y and still has no comments.
- Redirects with a 404 log. The starter needs redirect plus a custom view and path_redirect_import; Spillway logs top misses next to the redirect screen and creates redirects on slug changes automatically.
- Forms with CRM delivery and UTM capture. Webhook with HMAC signature, HubSpot, email, redelivery, CSV, UTM and referrer capture, honeypot and rate limiting are built in; the starter needs webform, webform_submission_log, captcha, honeypot and a third-party HubSpot bridge.
- Collections for marketers. A saved query with a preview and a block, instead of Views UI, viewsreference and the news_list paragraph's hand-built curation fields.
- SEO files and structured data. sitemap.xml, robots.txt, llms.txt and JSON-LD (WebSite, Organization, WebPage, Article, BreadcrumbList) without metatag, schema_metatag, simple_sitemap or easy_breadcrumb.
- AI with governance. Brand profile with banned words, page from brief, assist, alt text, type design, per-task provider routing across Anthropic, OpenAI, Google and xAI, a token cap, a usage report and an audit row per call; the AI never publishes. The starter has openai_* and the ai module with no brand layer or cap.
- Audit log on every write in the same transaction, versus config_log for configuration and dblog for events.
- Cache tags on every page from one process, with no Pantheon-specific module, and headers a CDN can purge by.
- Security defaults without seckit, password_policy or username_enumeration_prevention: scrypt, HMAC-stored sessions, CSRF header, login rate limit with constant-time email handling, MIME sniffing and sandboxed SVG.
- One process, one file. Install in five minutes with Docker and SQLite, backups as a file copy, versus Composer, Solr, Redis, MariaDB and a Pantheon workflow.
- Same JSON API for everything the admin does, with the API tokens and MCP contract on this branch extending it to agents, where the starter adds jsonapi_extras and a bespoke REST export for alerts.
Spillway 0.1.0 · Edit this page on GitHub