Next
Queue
Work on this site that is agreed but not started: ten flows of the app to add, in the order agreed on 7 October 2026, one flow that waits for the app, and the pages about the project. One item at a time, each in a session of its own. This page holds everything a session needs to take an item without re-reading the whole site: the rules, the mechanics, the demo data, and for each flow its screens one by one.
An item is done when its page is on the site, npm run demo:build has rewritten the navigation, the screens were checked in a browser, the row below says Done, and anything the flow shows that the app does not have is an item on App changes.
The flows were read from the app's routes (web/packages/crm/src/routes.ts) and pages on 2026-10-07; the labels quoted below are the app's own on that date. Already on the site as flows: Sign in, Reset password and the Hatch Farm walkthrough. The Not found page and the Illustrations cover the error and empty states, so those are not flows.
Before you start
The rules of this site and the mechanics of a flow. Everything here was settled while the first flows were built; a new flow follows it without asking again.
- The site's rules. Everything on the site is in English, American spelling, file names in camelCase. The app wins over the design boards: a screen shows the app as its code draws it, and a difference is listed in a "Compared with the design board" or "Design only" note. Nothing is an image or a screenshot; a page is hand-built or generated from the site's components. Nothing may jerk on load or on a click: navigation is static markup written by
build.mjs, and anything that changes weight when active reserves its width. Do not write the claude.ai document links into the site. - For show only. A flow's screens are pictures: no form submits, no field edits, no list opens. The one control that leads to the next screen is ringed (
hf-go) and carriesdata-go="<next screen id>". Everything else is inert. A reader who wants the working thing uses the app. - A flow page. One file in
flows/, listed inpages.mjsunder Flows. Its<main>holds a hero (what the flow is, who is in it, where the data comes from), then<div class="board" id="<flow>">with one<section class="board__item board__item--wide" id="…" data-step="n">per screen: aboard__caption(a<b>title, then a<span>with who signs in, what to do and what to watch for), a.frame(.frame__chromewith the address and "Signed in as …", then.frame__screen.hf-screenwith the page), and an optionalboard__moreline naming the way on when no control on the screen does. The skeleton needs<div class="sc-crumb" data-crumb></div>in the top bar and a plain<main class="sc-main">;build.mjsinserts the menu, the breadcrumbs and the pager. Loadassets/showcase.jsandassets/demo.jsat the end of the body. - What
demo.jsgives for free. The Demo / All screens bar; the arrows named after the screens they lead to; the screen list as the site's dropdown; a step rail under the bar, built from the screens, or the page's own<ol class="steprail" data-for="<board id>">with a[data-step]button per step (the walkthrough's, with role chips); Full screen (a button in every frame's bar, a layer with ← → and Esc,?fullin the address opens it at once);#<screen id>opens the demo at that screen. Group screens into steps withdata-step; without it every screen is a step. - The page builders.
tools/genHatchFarm.mjsbuildsflows/hatchFarm.htmland holds, as functions, the app shell with the signed-in user (shell), the Lead page in every state the walkthrough needs (leadPage), the Opportunity page with its header and tabs (oppHead,oppOverview,buyersTab,contractorsTab,keyContactsTab,legalsTab), both dashboards (individualDashboard,executiveDashboard), the Morning Meeting Board (morningBoard) and a buyer's Account page (accountPage), plus the parts (chipFn,chipStage,chipDeal,health,pill,stall,fact,card,stagebar,field,btn,tiles,actionRows). Copy or extend them for a new flow; a builder is written once per flow intools/and run withnode initialDemo/tools/<name>.mjs, thennpm run demo:build. The HTML it writes is what the site serves; a hand edit to that HTML is lost on the next run, so change the builder. The other three builders (genLogos,genNotFound,genIllustrations) work the same way. - The styles to use. The app's components are in
components.css(card,table,chip,pill,health,stagebar,tabs,field/input/select-wrap,switch-row,clock,breakdown,tile,btn,locked-hint,shell,userblock,avatar). The page layouts of the walkthrough are thehf-block at the end ofassets/showcase.css:hf-head,hf-facts,hf-stage,hf-grid,hf-tiles,hf-form,hf-timeline,hf-list,hf-funnel,hf-popover. Three of them exist because the first screens broke:hf-actionrows (a Next Action in a narrow card; the app'sactions__rowgrid wraps the text to four lines there),hf-tighton the eight-column Buyers and Contractors tables withhf-cell-actionsfor their buttons (else they run out of the card), and atagplaced beside a tab rather than inside it (a tab keeps the width of its label). Status chips in tables getwhite-space: nowrap. - Checking a flow.
node --checkon any script touched. Serve the site (DEMO_NO_OPEN=1 DEMO_PORT=4312 npm run demo, or the one already running on 4310) and shoot it headless:"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless=new --disable-gpu --hide-scrollbars --window-size=1400,2600 --virtual-time-budget=3000 --screenshot=out.png "http://localhost:4310/flows/<flow>.html#<screen id>", thensips -c <height> 1400 --cropOffset <y> 0 out.png --out crop.png(an offset of 0 makes sips crop from the center; use 1). A screenshot taken at a#idshows a blank band at the top, because the page scrolled to the screen: crop below it, or shoot the page without the hash for the first screen.?fullshoots the Full screen layer. The Full size layer of the sign-in boards (sl-full) is a different, older mechanism insignInLayouts.js; leave it. - Decisions already taken. A Not found proposal carries no layout (2026-10-07). The twelve demo steps are read from the ADRs and feature specs because the scope doc is not readable from this account; step 10 is the Morning Meeting Board by assumption, and the order of steps 5 and 6 follows the ADRs. The Sign in flow keeps one signed-in screen (the MD's landing is a sentence in its caption). The screen list in a flow's bar is the site's dropdown, built by
showcase.jsover the native select. Components and Sign-in layouts are one scrolling page each, with the menu following the scroll; a flow is a board, not sections. - The demo data.
api/src/assets/valere-demo.ts. Users: Beth Morgan (Back Office, BM), Liam Carter (Land, LC), Sophie Turner (Partnerships, ST), Dan Hughes (Construction, DH), Alex Reid (MD, AR), Demo Admin (admin). Leads, all Berkshire: Hatch Farm (New Lead, no activity, no action, owner Back Office, landowner R. & J. Whitmore, contact Robert Whitmore 07700 900101, 8.5 acres, 54,000 sq ft, 60 units of which 18 affordable, exchange in 47 days, six Legal tasks), Oak Lane Paddock (Contacted), Mill Field (Qualified), Rectory Meadow (Appraisal Requested), Brook End (Appraised), Willow Farm (Fee Discussion). Opportunities: Church Farm (Legals, Land & Build, the fully populated one), Station Road Yard (Buyer, Turnkey), Kings Mead (Heads of Terms, Land Only, Thames Valley Homes Agreed), Ashcombe (Contractor, Land & Build, Kennet Agreed, Northgate and Brackenridge Tender received at £7,650,000 and £7,800,000, Fairfield Invited), Elm Park (Instructed, S106), Saw Mill Close and Elm Road (Exchange / Completion), Hollow Lane (Closed / Lost, North Yorkshire) and Orchard Rise (Closed / Lost). Buyers / RPs: Thames Valley Homes (Lucy Grant, Head of Land, 07700 900201), Kennet Housing Association (Omar Siddiqui), Loddon Living (Kate Rowe), Greenway Residential, Wessex Affordable Homes, Chiltern Rise Homes, Northern Counties Housing. Contractors: Brackenridge Construction, Northgate Build, Fairfield Contracting. Other: Harcourt & Pell Solicitors, Stanton Valuers, Lime Studio Architects, Valere (demo). Dates in the seed are offsets from the day it runs; the walkthrough fixes today as 7 October 2026 (TODAYin the builder). - Made up for the walkthrough, not in the seed. The appraisal £1,850,000 and the expectation £2,100,000 (variance 13.5 %); the email note; the action "Call Mr Whitmore to arrange a site visit", due Thu 9 Oct; Thames Valley Homes' offer £2,050,000, quality 4 / 5, next step "Board paper 14 Oct"; the tenders £5,800,000 (Northgate, £107.41 / sq ft, rank 1) and £6,100,000 (Brackenridge, £112.96, rank 2), due 3 Oct; Lucy Grant's new number 07700 900251; the dashboards' tiles and lists, the board's rows and the Executive figures (£448,350 is the Live app sign-in board's figure); Station Road Yard's landowner "Loddon Estates", probability 40 % and fee £95,000; Hatch Farm's Site details "Agent introduction", "Pre-application" and "Residential", which were not checked against
LEAD_SOURCE_LABELS,PLANNING_STATUS_LABELSandPROPOSED_USE_LABELS; the dashboard greeting "Good morning, Beth" (the app'sgreeting()was not read). A new flow that touches any of these keeps them, and a session with time fixes the labels against the code.
Flows to add
In the order to build them. The first five earn their place first: the daily routine, where records come from, the Partnerships features nothing on the site shows yet, how a colleague gets an account, and a meeting logged once. Each flow below lists its screens in order; a screen id is a suggestion, the data-step is the step the rail shows.
| # | Flow | Who signs in | Screens | Status |
|---|---|---|---|---|
| 1 | Morning meeting, the daily routine | Alex Reid (MD), then Dan Hughes | 6 | Queued |
| 2 | A new lead from nothing | Beth Morgan | 7–8 | Queued |
| 3 | Partnerships, from a recommendation to an offer | Sophie Turner | 6–7 | Queued |
| 4 | A colleague gets an account | Demo Admin, then the new user | 6 | Queued |
| 5 | Log meeting | Liam Carter | 5 | Queued |
| 6 | Construction, a tender and the contractor's record | Dan Hughes | 4–5 | Queued |
| 7 | Who sees what | All six | 9 | Queued |
| 8 | Find a deal | Any role | 7 | Queued |
| 9 | Why a deal is red | Liam Carter | 5 | Queued |
| 10 | My account and the session | Any role | 5 | Queued |
| 11 | Closing a deal | Land, Partnerships | 3 | Waits for the app |
1. Morning meeting, the daily routine
What it proves: the board has no data of its own. Every row is a live slice of a deal; a change on the deal moves the row at once (FR-20, goal 3).
Screens
mm-board(step 1) ·/morning-meeting· Alex Reid, MD. The Primary view: the segmented Primary | Legals, tiles Overdue, Due this week, On track, No next action; the table Deal, Next deadline, Open actions, Health. Rows sorted by next deadline: Ashcombe (Contractor; Overdue; Construction "Appoint contractor · 3 Oct"; Stalled: no contact 15 days; 55), Church Farm (Legals), Kings Mead (Heads of Terms; Due this week), Station Road Yard (Buyer; On track), Elm Park (Instructed; On track), Saw Mill Close (Exchange / Completion; No next action). Ring: the Ashcombe name →mm-deal. Watch for: the overdue row at the top.mm-deal(step 2) ·/opportunities/ashcombe· Dan Hughes, Construction. The Overview with the Next actions card: "Appoint contractor", Construction, due 3 Oct, overdue in red; the header's Stalled badge and 55. Ring: Complete on that action →mm-done.mm-done(step 2) · the same page · the action struck through with Done; the Health score up (no overdue action); the badge stays (no contact 15 days is another rule). Then:board__more"Back to the board" →mm-board-after.mm-board-after(step 3) ·/morning-meeting· Alex Reid. Watch for: Ashcombe's row dropped to No next action and the tiles changed (Overdue 0, No next action 2); nothing was typed on the board. Ring: Legals in the segmented →mm-legals.mm-legals(step 4) ·/morning-meeting?view=legals· tiles Legal blockers ("A critical task past its deadline"), Critical items open, Exchanged ("Awaiting completion"); rows Church Farm (Legals: exchange and completion countdowns, the Oldest outstanding item, the open critical tasks), Saw Mill Close and Elm Road (Exchange / Completion, awaiting completion). Ring: Church Farm →mm-church-legals.mm-church-legals(step 4) ·/opportunities/church-farm/legals· the deal clock and the Legal progress table with Church Farm's own tasks. End: "Start again from the board".
Data
The seed's Ashcombe (Contractor stage, Kennet Agreed, three contractors) and Church Farm (Legals, the fully populated Opportunity: read its Legal tasks, dates and actions from valere-demo.ts rather than reusing Hatch Farm's). The other rows as the walkthrough's board draws them (morningBoard in tools/genHatchFarm.mjs).
Confirm in the code first
pages/morning-meeting/PrimaryBoard.tsx,LegalsBoard.tsx,board-parts.tsx: the exact columns, whether the deal name links to the Opportunity, how the Legals row shows the countdowns and the oldest outstanding item.pages/lead-detail/CompleteActionButton.tsx: the button's label and where it sits in the Next actions card (the walkthrough never drew a completed action;actionRowshas adonestate ready).- The MD may complete an action: Activities and Next Actions are open to every Valere role (ADR-0028); if the story reads better with Dan, switch the signed-in user between screens as the walkthrough does.
2. A new lead from nothing
What it proves: where the records come from. The walkthrough starts from a seeded Lead; this flow makes one, with its landowner, and shows the three other New forms.
Screens
nl-overview(step 1) ·/leads· Beth Morgan, Back Office. The Leads Overview with its tiles (Total sites, Instructed, Appraised, Not contacted), filters (Region, Owner, Stage) and the six seeded Leads. Ring: New lead (the primary button by the title) →nl-empty.nl-empty(step 2) ·/leads/new· the empty form. Cards: Details (Site name *, placeholder "For example Hatch Farm"; Address *, "Street, town, postcode"; Region, hint "Used by the Leads Overview's region filter"; Area (acres), "0.0"; Estimated units, "0"; Planning status, "Select"; Proposed use), Landowner (Landowner account *, or New landowner name *), Source and ownership (Lead source *, Relationship owner *, First contact date, Notes). Aside: What happens next; Required ("Fields marked * are required."). Ring: Create lead (read the button's label) →nl-errors.nl-errors(step 2) · sent empty: the banner "The lead was not created. Fix the fields below and try again." and the field errors "Enter the site name.", "Enter the address.", "Pick a landowner account.", "Select a lead source.", "Select a relationship owner." (also known: "Enter acres, up to 2 decimals.", "Enter the new landowner's name.", "The first contact cannot be in the future."). Ring: the Site name field →nl-filled.nl-filled(step 3) · filled: Site name "Barn Close", address "Barn Close, Arborfield, RG2", Region Berkshire, 6.2 acres, 40 units; Landowner: New landowner name "Arborfield Farms Ltd" (the branch that creates the Account on the way); Lead source and Relationship owner Beth Morgan; First contact date today; a note. Ring: Create lead →nl-lead.nl-lead(step 3) ·/leads/barn-close· the new Lead page: New Lead, Days open 0, the facts from the form, the Landowner card naming Arborfield Farms Ltd, no activity, no action. Watch for: the lead is on the Leads Overview and the landowner on the Accounts Overview at once.nl-account(step 4) ·/accounts/new· the New account form, empty: Account name * ("For example Hatch Farm Estate Ltd"), Account type *, Sub-type * (for Other), Status, Address *; What happens next: "The account opens on its own page, using the template for its type." "It appears on the Accounts Overview straight away."nl-contact(step 4) ·/contacts/new· the New contact form: Person (First name *, Last name *, Job title "For example Head of Land"), Account (Account *, or New account name * with Account type * and Sub-type *), Contact details (Phone, Email); What happens next: "The contact opens on its own page." "It is listed on the Contacts Overview and on its Account's page straight away." "Log a first activity from a deal the contact works on."nl-opportunity(step 5) ·/opportunities/new· "New opportunity. Create a deal for a site that is already instructed, without a lead." Details (Site name * "For example Barn Close", Address *, Units *, Planning status, Region, hint "Used by the Opportunities Overview's region filter"), Deal (Landowner account *, Deal type * with the hint "Decides whether the Contractors tab appears", Land value (£), Expected fee (£), Opportunity owner *, Instructed on); the aside "Already have a lead?". End: "Start again from the Leads Overview".
Data
Invented for the story: Barn Close and Arborfield Farms Ltd (no such names in the seed). The selects' options come from the label maps in the code; do not guess them.
Confirm in the code first
pages/NewLead.tsx,NewOpportunity.tsx,NewAccount.tsx,NewContact.tsx(POC-6.0a, b, d, e): the submit buttons' labels, the layout (form left, the aside cards right), the "What happens next" sentences of New lead and New opportunity, and the option labels (LEAD_SOURCE_LABELS,PLANNING_STATUS_LABELS,PROPOSED_USE_LABELS, the account types and sub-types).- Where the New lead button sits on the Leads Overview, and the Add account / Add contact buttons on the other Overviews (their labels are "Add account" and "Add contact").
3. Partnerships, from a recommendation to an offer
What it proves: the POC-6 Partnerships features (S-1 recommended buyers, the Interest and Offers registers, the Buyer Health score), which no page on this site shows yet.
Screens
pa-recommended(step 1) ·/opportunities/elm-park/buyers· Sophie Turner, Partnerships. Elm Park (Instructed, S106) with no buyers yet, and the Recommended buyers card: the Buyer / RP accounts scored against the site (the Buyer match: active regions, unit range, affordable homes), the Minimum score filter, an Add button per row. Ring: Add on the best match →pa-added.pa-added(step 1) · the buyer table now holds the account at Coverage; its recommendation row reads "Added at Coverage". Then:board__more"See it on the register" →pa-interest.pa-interest(step 2) ·/registers/interest· the Interest Register: tiles Live interests, Opportunities covered, Buyers interested, Stale over 14 days; columns Opportunity, Buyer, Buyer stage, Registered, Last contact, Next step; the new row at the top and a Stale tag on an old one; the filters. Ring: the Elm Park row →pa-offer.pa-offer(step 3) · back on the Buyers tab: the buyer moved to Interested, then Offer received, with the row's edit form open: Offer (£), Quality ("Not rated"), Next step; "A figure can be changed but not cleared once set." Ring: Save →pa-offers.pa-offers(step 3) ·/registers/offers· Offers Received: tiles Offers received, Under review, Accepted, Average vs GDV; columns Opportunity, Buyer and next step, Offer, vs GDV, Status, Quality; the new offer under review. Ring: the buyer's name →pa-account.pa-account(step 4) ·/accounts/<the buyer>· the Account page with the Buyer health score card (its factors: reply dates, completed or closed agreed deals, offer against GDV; "Every factor is at full points." when nothing counts against it) and the Buying criteria card (active regions, unit range, affordable homes). End.
Data
Elm Park and the seed's Buyer / RP accounts with their criteria (read valere-demo.ts for each buyer's regions and unit range, so the recommendation order is the one the app would give). The offer figure is invented; pick one under the site's GDV if the seed has one.
Confirm in the code first
pages/opportunity-detail/RecommendedBuyers.tsxandweb/shared/components/BuyerMatch.tsx: the columns of the recommendation table, how the score is shown, the Minimum score options.pages/registers/InterestRegister.tsx,OffersRegister.tsx: the filters (register.hasFilterexists), what "Stale" and "vs GDV" read; where a Site's GDV lives (the Offers register needs it: "No offer on a site with a GDV recorded yet.").web/shared/components/BuyerHealth.tsx,pages/account-detail/BuyingCriteriaCard.tsx: the factor names and points.- Where the registers sit in the menu:
/registersredirects to the Interest Register; the walkthrough's shell has the Registers item since POC-UI-1 (the Pipeline group, as the app'sAppShell.tsxlists it).
4. A colleague gets an account
What it proves: there is no sign-up; an admin creates each user with one Valere role, and the role decides the homepage and the edit rights (POC-5.7, ADR-0029).
Screens
st-signin(step 1) ·/auth/sign-in· the Sign in screen with admin.demo@example.com typed (reuse the Sign in flow's screen in the Live app layout). Ring: Sign in →st-executive.st-executive(step 1) ·/dashboard· Demo Admin. The Executive dashboard, read only: the menu has one more item, Create staff user; every write is locked "Read only for Admin." Ring: Create staff user in the menu →st-form.st-form(step 2) ·/staff/new· "Create staff user. Add a colleague who can sign in to Valere CRM." The card User details: Role ("Select a role": Back Office, Land, Partnerships, Construction, MD), Email, Password with the Password rules checklist (8 to 72 bytes, "At least one number"); the aside cards Roles (what each role does) and Who can do this ("Only an admin can create staff users. There is no public sign-up."). Ring: Create →st-taken.st-taken(step 2) · sent with land.demo@example.com: the banner "The user was not created. Fix the email below and try again." and the field's "A staff user with this email already exists". Ring: the Email field →st-created.st-created(step 3) · sent with priya.nair@example.com, role Land: the green bar "Staff user created. priya.nair@example.com"; the form is empty again for the next colleague. Then:board__more"Priya signs in" →st-priya-signin.st-priya-signin(step 4) · the Sign in screen with the new email, thenst-priya-dashboard(step 4) ·/dashboard· the Individual dashboard · Land, with the Land role card Overdue follow-ups. Watch for: a new user has no name until they set it in My account; the top bar and the greeting show what the code shows for an empty name. End.
Branch, from st-form's caption: a Valere role opening /staff/new gets the red bar "Only an admin can create staff users."
Data
The admin is in the seed (Demo Admin, admin.demo@example.com). Priya Nair is invented; the seed's Valere-side Key Contact is a different fictional person, so check the seed for name clashes before using one.
Confirm in the code first
pages/CreateStaffUser.tsx: the submit button's label, the Roles card's text, the checklist's rules, the password field's own error.components/AppShell.tsx: the menu with the admin's extra item (iconi-staffexists on the site), and what the sidebar's user card shows when the profile has no name.components/StaffOnly.tsxandsession/edit-rights.ts: which controls the admin sees locked, with "Read only for Admin." (also on the Components page under Text field).
5. Log meeting
What it proves: a meeting is recorded once and appears on every related record, and its follow-up is a Next Action with an owning function (POC-6.0c, FR-12, FR-13).
Screens
lm-lead(step 1) ·/leads/mill-field· Liam Carter, Land. Mill Field (Qualified) with its timeline and tracker as the seed has them. Ring: Log meeting (find where the page offers it) →lm-form.lm-form(step 2) ·/leads/mill-field/log-meeting· "Log meeting. Record a meeting once. It appears on every related record." Cards: Details (Activity type "Meeting", hint "A meeting is logged as an activity"; Related to, the site, hint "It appears on this timeline and on each attendee's records"; Date *; Start time *), Attendees (Add attendee *, the contacts picker), Notes (What was discussed *), Follow-up (Action *, Owning function * "Choose a function", Due *); the aside What happens next ("The follow-up becomes a Next Action with its owning function." "The deal's days since last contact reset."). Ring: Save →lm-errors.lm-errors(step 2) · sent empty: "The meeting was not saved. Fix the fields below and try again." with "Choose the date.", "Enter the start time.", "Add at least one attendee.", "Enter what was discussed.", "Enter the action.", "Choose the owning function.", "Choose the due date." (also known: "A meeting cannot be in the future."). Ring: Add attendee →lm-filled.lm-filled(step 3) · filled: today 10:00, the attendee Mill Field's landowner contact (from the seed), "Walked the boundary; the owner wants a decision before Christmas.", the follow-up "Send the appraisal request", Owning function Back Office, due in a week. Ring: Save →lm-lead-after.lm-lead-after(step 3) · the Lead page: the timeline's new Meeting entry "With <attendee>", the tracker's new action with the Back Office chip, Last contact today, Health's Recent contact at 0. Then:board__more"The same meeting on the contact" → the attendee's Contact page (lm-contact, step 4) with the entry under its activity. End.
Confirm in the code first
pages/LogMeeting.tsx: the entry points on the Lead and Opportunity pages, the Save button's label, how attendees are picked and listed, whether the Opportunity variant differs.- Mill Field's landowner and contact in
valere-demo.ts.
6. Construction, a tender and the contractor's record
What it proves: Construction's own day, and that a contractor's bids live on its Account page (POC-6.4), the way a buyer's deals live on its.
Screens
ct-dashboard(step 1) ·/dashboard· Dan Hughes, Construction. The Individual dashboard · Construction with the role card Tenders in progress: Ashcombe, "Awaiting contractor". Ring: Ashcombe →ct-tenders.ct-tenders(step 2) ·/opportunities/ashcombe/contractors· the seed's three tenders: Northgate Build £7,650,000 Tender received, Brackenridge Construction £7,800,000 Tender received, Fairfield Contracting Invited; £ per sq ft and Rank if Ashcombe has a floor area. Ring: Move to Shortlisted on Northgate →ct-appointed.ct-appointed(step 2) · Northgate at Appointed; the header's Move to Heads of Terms now live (Kennet is Agreed, Land terms agreed if the seed says so). Then:board__more"The contractor's own page" →ct-account.ct-account(step 3) ·/accounts/northgate-build· the Contractor Account page: the Bid history card (its Bid outcome filter; one row per tender with the site, the price and the outcome; "No bids with this outcome." when the filter finds none), the deals panel, Contacts, Activity. End.
Confirm in the code first
pages/account-detail/contractor/BidHistoryCard.tsxand the folder beside it: the columns, the outcome values, the "No bids yet" title.pages/dashboard/role-cards.tsx: what the Construction card lists and the "Awaiting contractor" pill.- Ashcombe's floor area, Land terms and tender due dates in the seed.
7. Who sees what
What it proves: the same data, six homepages, and on one deal what each role may change (FR-25, goal 2).
Screens
ws-backoffice…ws-construction(step 1) ·/dashboard· the four Individual dashboards: Beth (role card Leads awaiting contact or appraisal: New Lead "Not contacted", Appraisal Requested "Awaiting appraisal"), Liam (Overdue follow-ups), Sophie (Live pitches and offers, with the "No buyer coverage" pill on a deal with no live buyer), Dan (Tenders in progress, "Awaiting contractor"). Each with its own tiles and My actions.ws-md,ws-admin(step 2) · the Executive dashboard for Alex Reid and for Demo Admin (read only, the extra menu item).ws-church-land,ws-church-partnerships,ws-church-construction(step 3) ·/opportunities/church-farm· the same Overview as Liam (Land terms his, the stage moves his), as Sophie (Land terms "Set by Land. Read only for Partnerships."; the Buyers tab hers) and as Dan (the Contractors tab his, everything else locked with its owner named). The ring moves from one locked control to the next.
Build it as a table of nine screens rather than a story; the rail groups them in three steps.
Confirm in the code first
pages/dashboard/role-cards.tsx: the four cards' rows and pills;IndividualDashboard.tsx: the eyebrow "Dashboard · <role>" and the greeting.session/edit-rights.ts: the exact hint sentences per field and per move;SITE_EDIT_FIELD_FUNCTIONSfor who owns what.individualDashboardin the builder is written for Beth only; it needs the user, the role card and the figures as arguments.
8. Find a deal
What it proves: the Overviews filter and search live, the filters live in the address, and an empty result says why (FR-14, FR-16).
Screens
fd-leads(step 1) ·/leads· any role. Tiles Total sites 6, Instructed 0, Appraised 1, Not contacted 1; the filters Region, Owner, Stage ("Region: All" …); the table Site, Units, Stage, Last contact, Owner, Health, Next action with the six Leads; the pager "Showing 1–6 of 6 sites". Ring: the Stage filter →fd-stage.fd-stage(step 1) ·/leads?stage=APPRAISED· one row, Brook End; the tiles count the filtered set. Ring: the Owner filter →fd-none.fd-none(step 2) ·/leads?stage=APPRAISED&owner=<Sophie>· "No leads match these filters." with Clear filters. Ring: Clear filters →fd-cleared.fd-cleared(step 2) · back to six rows. Ring: Brook End →fd-lead(step 2) · its Lead page.fd-accounts(step 3) ·/accounts· tiles Total accounts, Landowners, Buyers / RPs, Contractors, Other professionals; the type tabs; the Account name search; columns Account, Type, Contacts, Live deals, Health, Last contact. Ring: the search with "Greenfield" →fd-accounts-none· "No accounts match these filters."fd-contacts(step 3) ·/contacts· "Every person at an account, with how to reach them."; the Account type filter and the Name or account search; columns Name, Job title, Account, Phone, Email, Last contact. End.
Confirm in the code first
pages/list-params.ts: the query parameter names (region, owner, stage, page, search, type) so the frames' addresses are right.pages/LeadsOverview.tsx,AccountsOverview.tsx,ContactsOverview.tsx,OpportunitiesOverview.tsx: the Health column on Accounts (the Buyer Health score), the Add buttons, the pager's sentence.
9. Why a deal is red
What it proves: every score and stall flag is explainable and curable; the score runs on read, so the cure shows at once (FR-23, FR-24, goal 3).
Screens
rd-overview(step 1) ·/opportunities· Liam Carter. The Opportunities Overview (tiles Total opportunities, Active, Weighted pipeline fee; filters Region, Deal type, Status; columns Opportunity, Units, Deal type, Status, Fee, Prob., Health, Last activity) with one deal red and badged. Ring: its Health chip →rd-breakdown.rd-breakdown(step 1) · the breakdown open: each component with its points and the fact behind it, the stall rules fired. Ring: the deal's name →rd-deal.rd-deal(step 2) · the Opportunity Overview with the overdue actions and the old last contact. Ring: Log activity →rd-logged.rd-logged(step 2) · the call logged today: Recent contact back to 0 points off, the no-activity rule gone; the score up but still amber. Ring: Complete on the overdue actions →rd-green.rd-green(step 3) · the score green, no badge; then the Overview row again with the green chip. End.
Data
Pick the deal by computing the components from api/src/site/site-health.ts (Recent contact −25 from 14 days, Overdue actions −10 each up to 3, Stage duration −20 past the stage's limit, Next action −10, Legals −15 per critical task past deadline up to 2, only in Legals): a deal with no contact for 20 days and three overdue actions is at 45; with a stage over its limit, 25 and red. Ashcombe in the walkthrough is at 55 with "no contact 15 days"; give it two more overdue actions, or use a deal of your own in the story, and say in the caption what was assumed.
10. My account and the session
What it proves: a user keeps their own profile and password, and a password change ends the session on purpose (KAN-13, ADR-0012).
Screens
ma-account(step 1) ·/account· any role (Liam). "My account. Your profile and password." The Profile card: First name, Last name, Email, Role (the role read only), Save; the Change password card: Current password, New password (hint "Counted in bytes: some symbols count as more than one."), Confirm new password. Ring: Save on the profile →ma-saved· "Profile saved." in the green bar.ma-password-errors(step 2) · the password form sent wrong: "Enter your current password.", "The passwords do not match.", "The current password is incorrect." Ring: Change password →ma-changed.ma-changed(step 2) · "Password changed. Sign in with your new password." Watch for: the session ends on purpose (the refresh token could not be renewed later), so the next screen is Sign in. Then →ma-signin(step 3) · the Sign in screen.ma-signout(step 3) · the user block in the top bar is one link to My account; Sign out is on that page. The screen shows it ringed and the caption ends on Sign in. End.
Confirm in the code first
pages/Account.tsx: the buttons' labels, where Sign out sits, whether the email is editable.- The Sign in flow's "session ended" screen is Design only; this flow's return to Sign in is the app's real behaviour and shows the plain Sign in screen.
11. Closing a deal
Waits for the app. The stage table has a close to Closed / Lost with a reason (ADR-0017, moveSiteStage takes closedLostReason), but the pages do not offer it: pages/lead-detail/StageMoves.tsx (line 42 on 2026-10-07) keeps only the forward and back rows, row.kind !== 'CLOSE'. The Opportunity page shows "Closed / lost: <reason>" for a deal the seed closed (Hollow Lane, Orchard Rise), and a lost Lead opens on the Lead page.
- When the app has the close move, the flow is three screens: the Opportunity with Close as lost ringed, the reason asked, and the closed deal with its moves gone, its fields fixed and its row out of the Active tile and the board; with Exchange / Completion as the deal won beside it.
- Until then, it would be a Design only flow and an App changes item ("Close a deal as lost from the page"). Not started, because nothing on the site should propose app behaviour without the developers' conversation first.
Not flows, on purpose
- Lead qualification. The Lead page says "Not qualified yet." and nothing in the app writes it.
- Already in the walkthrough. The contact edited once and shown everywhere, the appraisal and the variance, the Deal type before Instruction, a locked field with its owner, the Turnkey deal without a Contractor stage, the Legals tab, the Health breakdown.
- States, not flows. Not found, empty lists and panels, errors and done states: the Not found page and the Illustrations.
Pages about the project
Listed in the menu as planned. On the same tokens and components, after the flows or between them.
| Page | What it holds | Source | Status |
|---|---|---|---|
| About the project | What the Valere CRM PoC is, its ranked goals, what is deliberately out of scope. | docs/BUSINESS_MODEL.md | Planned |
| Roles and functions | The five Valere roles and the admin, who owns which stage, locked fields. | docs/DOMAIN_GLOSSARY.md, ADR-0019, ADR-0028, site-stage.config.ts | Planned |
| Deal lifecycle | The stages from New Lead to Exchange / Completion, the moves and their conditions. | site-stage.config.ts, ADR-0017, ADR-0019; the Handoff line sign-in board draws it | Planned |
| App screens | The CRM pages: overviews, Lead, Opportunity with its tabs, the board, the dashboards. | docs/ARCHITECTURE.md; the walkthrough's screens can be reused one per page | Planned |
| Data model | Site, Company, Contact, the buyer and contractor links, the computed values. | api/prisma/models/, docs/DOMAIN_GLOSSARY.md ("Computed on read") | Planned |