Guidance for AI coding agents working in this repository — a Jekyll/GitHub Pages template where site behavior is driven by _data/*.yml, not hardcoded in templates or scripts.
Which repo are you in? This file is for work on the template itself. If you are in a copy — a repository created from this template that you were asked to configure for an organization — read AGENTS.md instead: it is the setup runbook, and this file's "keep it organization-agnostic" rule does not apply to a copy.
A GitHub-Pages-hosted catalog template, shipped configured as a generic AI Use Case Catalog (the ai-use-cases preset, also served as the showcase example at /examples/ai-use-cases/). Keep it organization-agnostic: the health-coalition deployment it grew out of now lives in its own repository, crypticpy/bchc-ai-use-case-catalog, and no organization-specific names belong here. Content is authored through GitHub issues/PRs (see docs/admin-guide.md); there is no server and no database. Full docs: README.md, docs/configuration.md, docs/content-model.md, docs/admin-guide.md; the map of the repository family (this template, the BCHC deployment, deploy variables) is docs/ecosystem.md — keep it current when a repository is renamed, transferred or retired.
_data/schema.yml defines every entry field. Never hardcode a field name in a layout, include, script, or workflow — read it from the schema instead. Everything downstream already does this: _layouts/entry.html, _includes/entry-card.html, _includes/facet-filters.html, _includes/field-value.html, _plugins/search_index.rb, submit/index.md, scripts/new_entry_from_issue.mjs, scripts/check_front_matter.rb, and .github/ISSUE_TEMPLATE/new-entry.yml (generated, don't hand-edit) all iterate schema.fields rather than naming specific keys. If you add a feature that needs a new per-entry attribute, add it as a schema field, don't bolt it on as a special case.
After editing _data/schema.yml, run npm run generate to regenerate .github/ISSUE_TEMPLATE/new-entry.yml and resync _config.yml's title/description. CI runs this too, but a stale committed issue template is confusing — regenerate and commit it in the same change.
Before finishing any change that touches _data/*.yml or catalog/**/index.md front matter, run npm run validate (parses all _data/*.yml, then runs scripts/check_front_matter.rb and scripts/check_file_sizes.rb, the same gate validate.yml runs on every PR).
_data/site.yml— branding, module toggles, home/footer/submit copy._data/theme.yml— colors/fonts/radius._data/navigation.yml— header links._data/schema.yml— entry content model (see above)._data/events.yml,_data/cohorts/<year>.yml,_data/resources.yml— feature data for theevents,cohorts,resourcesmodules._plugins/modules.rb— drops pages under a disabled module's path atpost_read._plugins/events.rb— merges site + cohort events intosite.data.events_all._plugins/search_index.rb— builds/search.jsonfrom schema fields markedsearch/facet._plugins/theme_filters.rb— Liquid filters (hex_to_rgb,facet_values,slugify_list,link_host,query_encode).assets/js/configurator/core.js— shared logic behind both configurators (/setup/in-browser andnpm run setupCLI):renderFiles()produces_data/site.yml,_data/theme.yml,_data/schema.yml,_data/navigation.yml,_config.yml,.github/ISSUE_TEMPLATE/new-entry.yml.presets.jsholds the four starting presets. Edit shared behavior incore.jsonce, not in both wizards.scripts/*.mjsandscripts/*.rb— the issue→PR automation (new_entry_from_issue.mjs,new_event_from_issue.mjs,scaffold_year.rb,update_schedule_from_issue.rb,update_event_attachments_from_issue.mjs, etc.). Each is invoked by a matching.github/workflows/*.ymland readsISSUE_BODY/ISSUE_TITLEenv vars, not GitHub API calls.
- Assigns inside an included template leak into the parent scope. Jekyll's
{% include %}shares the caller's Liquid variable scope (unlike Shopify's sandboxedrender). Reusing a common variable name (v,entries,f) inside an include can silently clobber a variable of the same name in whatever page/layout included it. Prefer distinctive names inside_includes/*.html. x.first(notx.sizeor a type check) is the idiom used to detect "is this an array" for a field whose value could be a scalar (text/select) or a list (list/multiselect), e.g.{% if v.first %}{% for x in v %}…{% else %}{{ v }}{% endif %}in_includes/facet-filters.html,index.md,_layouts/entry.html. A bare string's.firstisnil(falsy), an array's.firstis truthy unless the array is empty. Follow this pattern rather than introducing a different array check.- An empty string is truthy in Liquid, so
{% if include.cta_url %}is true for a caller that passedcta_url=''to switch the CTA off. Optional include params that a caller may blank out are tested with!= ''(see_includes/empty-state.html), never for bare truthiness. {% include %}parameters cannot use bracket/dynamic access directly —{% include x.html field=site.data.cohorts[page.year] %}does not work. Assign to a variable first, then pass the variable:{% assign year_key = page.year | append: '' %}{% assign data = site.data.cohorts[year_key] %}{% include timeline.html events=data.events %}(see_layouts/cohort.html,_layouts/event.html). Theappend: ''is also load-bearing — it coercespage.yearto a string so the hash lookup matches YAML string keys.
- Match the schema's
facet/card/search/group/placementsemantics exactly as documented indocs/content-model.md— they have precise meanings consumed by multiple templates, not just descriptive labels. - Front matter files are validated structurally (
check_front_matter.rb):slugmust equal the folder name,publishedmust beYYYY-MM-DD, required fields must be non-blank,select/multiselectvalues must be inoptions. Keep generated/edited entries consistent with this or CI will fail the PR. - Don't hand-edit
.github/ISSUE_TEMPLATE/new-entry.yml— it's regenerated from the schema and marked as such at the top of the file.