A prototype of Run breast screening in England (Rubie), an NHS service for running screening clinics and reading mammograms. Built on the NHS prototype kit: Node, Express, Nunjucks and NHS.UK Frontend. It is heavily data driven: realistic seed data is generated daily and populates almost every page.
Read docs/domain.md before working on views or content. The docs index at the end of this file says what else to read and when.
This is an advanced prototype, so some things differ from a plain prototype kit project and from the general kit guidance in .github/instructions/:
- Aiming for outward realism; internals can be faked. It may be hacky in places, and that is fine, but the codebase must stay maintainable because it is large and long-lived.
- There is a lint step and a small smoke suite (
npm test: eslint, a route sweep, Playwright journeys). Keep them passing. Run them before opening a PR and after refactors that move code between modules. See docs/testing.md. - Layouts live in
app/views/_templates/, not the kit's singlelayout.html. Routes are split by area underapp/routes/. - Seed data is shared and read only. Writes go through working copies and update helpers. See docs/data-conventions.md.
- No backwards compatibility needed for old seed data. Data regenerates daily, from
/settings, and when the tests run, so never rely on a specific generated record or id persisting. - Assume the app is already running. No need to start it.
app/views/- pages, organised by area (clinics, participants, appointments, reading, episodes)app/views/_templates/- layouts.app/views/_components/andapp/views/_includes/- reusable Nunjucksapp/views/style-guide/- live style guide at/style-guide/showing every custom componentapp/routes.jsandapp/routes/- middleware and the routes that do real processingapp/lib/utils/- helpers, all auto-registered as Nunjucks filters and globals.app/filters/- Nunjucks-only filtersapp/lib/generators/- seed data generation.app/data/- reference data and generated seed dataapp/assets/- Sass and client-side JavaScriptdocs/- the documentation this file points toscripts/andtests/- the smoke suite and the docs generator
These are the mistakes that happen most. docs/routing-and-forms.md has the detail.
- Form data saves itself. Every POST or GET field is merged into
req.session.databy itsnamebefore any route runs. A form can post straight to the next page. Never write a route whose only job is to receive a form. - Read submitted values from
req.session.data, notreq.body. The two exceptions are in routing-and-forms.md. - Pages under
/participants/:id/,/clinics/:id/appointments/:id/and the reading workflow are served by dynamic routing. Add a template, not a route. The middleware has already loadedparticipant,appointment,clinic,episodeand URL locals such asappointmentUrl. - Write a route only for branching, saving, validation or real processing, in the area's file under
app/routes/, posting to a separate URL and finishing withres.redirect(). - Use the referrer filters for "return to where I came from":
urlWithReferrerto go deeper,getReturnUrlto come back,appendReferrerto extend the chain. Never build?returnTo=or?referrerChain=by hand. See docs/referrers.md. - Check for an existing helper before writing logic. docs/utils-filter-reference.md lists every filter and helper, with a table of contents from line 14. Prefer generic composable filters over one-offs; new ones go in
app/lib/utils/and need a JSDoc comment (the reference is generated from them withnpm run docs). - Validation is rare. Only where missing data would break what follows. See docs/validation.md.
docs/data-conventions.md has the detail.
- No new top-level keys on
data, and nothing onreq.sessionoutsidedata. Name fields for where they belong (appointment[...],participant[...]). Unsaved form state goes in one namespaced temp store, such asappointment[somethingTemp][...]ordata.imageReadingTemp. - Store ids, not display strings. Save a user id and turn it into a name when displaying it.
- Use real data. Read users, permissions and reference data from
dataand the helpers; never type names or options into a template. - Every field a page reads needs seed data. Update the generator when adding a field, and remove or update generators that no longer fit.
- Follow a data change everywhere it goes. When adding, renaming or changing a key, field or status, search the whole repo for it and check every page, helper, generator and test that reads it.
- One name per concept across variables, data keys, field names, routes, files and copy. Finish renames everywhere.
- Modern JavaScript:
const, arrow functions, template strings. No semicolons except in Sass. 2-space indentation. Allman style, with conditions on their own lines - Descriptive names in plain English:
indexnoti,buttonnotbtn - Double quotes for strings in Nunjucks and HTML attributes; single quotes in JavaScript
- First line of every JavaScript, Nunjucks and Sass file is a comment with its path, for example
// app/routes/clinics.jsor{# app/views/clinics/index.html #} - Comments explain purpose and non-obvious constraints. They do not record history or past bugs
- Nunjucks:
elseifnotelif, no trailing commas, object keys on separate lines, HTML insetorcallblocks rather than inline - Prefer NHS.UK Frontend macros over hand-written HTML, and look up parameter names in the component reference rather than guessing
- Set
small: trueon everyradiosandcheckboxesmacro. Buttons are already a medium size by default (set in Sass), so only addsmall: trueto a button when you want the small size main.scsssets a tighter spacing scale than NHS Frontend's default, so spacing points 4 to 9 are smaller than the NHS Frontend docs say, and the grid gutter is 24px- Sass: BEM with an
app-namespace, full class names written out (no&__nesting), one file per block, NHS Frontend tokens not hard-coded values, no inline styles - Server-side rendering first. Client-side JavaScript is progressive enhancement and everything must work without it
- Reuse before writing: an include, component, layout, helper or filter probably exists
- UK English spelling. Sentence case for headings, titles and buttons, never title case or all caps
- Smart quotes in user-facing text (‘ ’ and “ ”)
- Follow the NHS design system for patterns and content
- Lean copy: do not restate the heading in the sentence below it
- Implement only what is asked. Suggest improvements rather than making them
- If a request is ambiguous or too broad, ask or propose a narrower reading before starting
- Number questions and decisions you put to the user, so they can answer by number. Give your recommendation for each
- Say what changed and what remains after each unit of work
- Do not claim something works until it has been checked in the running app. When fixing a bug, verify the fix
- One task per chat. Long chats lose track of earlier instructions and of these conventions. When a task is done, or the chat has become long, suggest starting a new chat
When a piece of work is complete, and before suggesting a commit or a pull request, use the review-changes skill (/review-changes) without being asked. It reviews the change against docs/review-checklist.md in a subagent, fixes what it finds, runs npm test and writes review notes for the pull request. The authors are designers whose background is not coding, so this step is how code problems get caught.
To deal with review comments on a pull request, from Copilot's code review or a person, use the pr-comments skill (/pr-comments). Reply, then resolve: reply to every thread with what changed and the commit, and resolve only the fixed ones. Leave a thread open, with a reply saying why, when a comment does not apply. Replies post under the user's GitHub account, so write them as short, plain notes from that person.
Copilot applies the guides in .github/instructions/ automatically when editing matching files. If your tool does not, read them:
.github/instructions/nhs-frontend-guide.instructions.md- NHS Frontend components, Sass and content conventions.github/instructions/nhs-prototype-kit-guide.instructions.md- general prototype kit patterns (this repo goes beyond it, see above)- docs/nhs-frontend-component-reference.md - every macro with parameters. Table of contents near the top, or search for
## Component name - docs/nhs-frontend-sass-reference.md - every mixin, function and variable. Table of contents near the top
Read the doc before working in its area. docs/README.md has the same list with more detail.
| Doc | Read when |
|---|---|
| domain.md | Any view or content work; anything touching screening concepts |
| routing-and-forms.md | Adding a page, form or route |
| referrers.md | Any link or form that returns the user somewhere |
| data-conventions.md | Reading or changing participants, clinics, appointments, episodes |
| utils-filter-reference.md | Before writing any data or display logic |
| validation.md | Adding form validation or error messages |
| modal-system.md | Forms that open in a modal |
| filtering.md | Index pages with filter panels |
| image-reading.md | Anything in the reading workflow or reading data |
| pacs-viewer.md | The mammogram viewer window and image sets |
| data-generator-reference.md | Changing how seed data is generated |
| medical-information-generator.md | Medical history, symptoms and other medical information data |
| issues.md | Raising, showing or resolving issues, or the Review pages |
| review-checklist.md | Reviewing a change before it is committed or merged |
| testing.md | Running or adding to the smoke suite |