ElmsPark · verification report
Private. For Don, from Kenn. Not for publication.
SoundCheck (codename) · follow-up to round 2 · the demo, run for real

The demo you promised is the demo that runs.

Your repo at commit c0f48b1, the same state round 2 audited · fresh clone on a Mac that had no Postgres at all · README followed exactly, every step in a real browser · 15 July 2026 · machine-readable copy for your Claude: soundcheck-demo-report.md

90-second version: round 2 left one row unproven, "the live demo already exists", because the audit machine had no Postgres. It now has: the full tour runs end to end and lands on the RED "Venue: problem to fix" with the no-live-drums Venue Pack. Your "about 3 minutes" is really about 14 seconds. One landmine: a fresh clone dies at the Book button because .env.example ships placeholder Stripe keys, a one-line fix that should go in before anyone else clones it. And your F7 finding stopped being theoretical: the failed booking leaked a HELD date, first attempt.

Verdict

It survives a cold machine, a fresh database and a stranger driving it. The one thing that broke is a config file, not your product.

This was the adversarial version of your quickstart: brand-new Postgres 16.14, fresh clone at the reviewed commit, no help from you, and a scripted probe pass on top of the manual run. The search filtering, all-in pricing, mock deposit, auto contract, dual e-sign, tokenised venue intake and the readiness engine all behaved exactly as the README says. Nothing in the product code was changed to make it pass.

~14sclone to running app, measured (your claim: "about 3 minutes")
13/13scripted text probes found: RED chip, limiter flag, readout, contract states
16inputs on the venue form, no login needed, matching round 2's count
1demo-breaking bug, config-level, one-line fix

Timing, measured

"Getting started (for Kenn)" followed exactly, wall clock per step, M5 MacBook Pro.

StepDetailTime
Prerequisitebrew install postgresql@16 + service start, machine had no Postgres7s
Clonefresh git clone3s
Env + databasecp .env.example .env, set DATABASE_URL, createdb (your keg-only path hint in the README is right, and needed)2s
Install + schema + seednpm install (warm cache), db:migrate, db:seed → "Seeded 6 acts, 4 reviews, 2 venues"7s
Dev servernpm run dev → "Ready in 1772ms", homepage 200~3s

Call it 14 seconds of real work. A first-ever npm cache would add a minute or two; the 3-minute claim still holds with room to spare. You can safely keep it, or tighten it and enjoy the reaction.

What held

The 2-minute tour, in order, with the proof.

Search and pricing HELD

Saturday 1 August 2026 + SW1A 1AA returned "4 bands available", which is the right number: the seed books two of the six out on that date and both were hidden. Every card carries a true all-in price. The Velvet Kings priced £1,450 + £1.80 travel (4 miles) + £290.36 VAT = £1,742.16, deposit £348.43, and the same figures flowed unchanged into the booking, the contract text and the act's payout view. Nothing recalculated behind the client's back.

Booking, contract, both signatures HELD

With the mock provider active the booking confirmed instantly, deposit "payment protected", contract auto-generated with the right parties, venue, date and figures. Signed as Chloe, then as the act: sent → client signed → fully signed, booking advanced to CONTRACT_SIGNED. The act dashboard shows budget, venue and the £1,306.80 payout with your £145 commission disclosed up front, which is the honesty the pitch leans on.

Act's view of the booking with the contract fully signed
The act's view after both signatures: fully signed, Client ✓, Act ✓.

The differentiator: venue intake to RED HELD, VERBATIM

The tokenised link on the booking page opened in a cookie-less browser with no login, greeted the venue by name and said who it is helping and when. Exactly 16 inputs. Submitted a 6×4m stage, 3 sockets, 23:30 curfew and a 85 dB A-weighted limiter with a named reset authority.

The venue's own thank-you page already showed the plain-English readout and "Readiness: RED (1 note flagged to the act)". Chloe's dashboard flipped the booking to a red "Venue: problem to fix" chip while her past bookings stayed green. The booking page carried the specific flag, "Limiter at 85 dB will not allow this band's live drums. Electronic-kit line-up or a different venue needed.", plus the ULEZ note for the SW1A postcode. The Venue Pack printed the readout word for word: "85 dB, A-weighted. Live drums not advisable. Electronic kit or cajon line-ups recommended. Reset authority: Sarah (duty manager), 07700 900123."

Booking page: red Venue problem to fix chip, limiter flag, contract fully signed
The money shot: contract signed, venue RED, the limiter flag in red, ULEZ in amber.
The 16-input tokenised venue form, filled
The venue form as the venue sees it: two minutes, no account.
Venue Pack with flags, stage fit, limiter readout, contacts
Venue Pack: flags, stage fit (6×4m vs 5×3m needed), readout, on-the-night contacts.

What broke

One blocker, one of your own findings caught happening, two small frictions. I changed no code; everything below reproduces on a clean clone.

BLOCKER  A fresh clone dies at the Book button

The README's only .env instruction is to set DATABASE_URL, and it promises the mock provider needs nothing. But .env.example ships STRIPE_SECRET_KEY="sk_test_..." as a literal placeholder, and src/server/payments/index.ts switches on any non-empty key. So the copied file sends the placeholder to the real Stripe API and the first booking attempt 500s:

POST /book/cmrlokxz4... 500 in 112ms
⨯ Internal error: Error: Invalid API Key provided: sk_test_...
    at ...StripeAPIError... (node_modules/stripe/esm/RequestSender.js:100:31)

Blanking the three STRIPE_* lines and restarting brings the mock provider up and the whole tour then runs first time. Fix is one line either way: ship those three values empty in .env.example, or treat the sk_test_... placeholder as unset in paymentsProvider(). Worth doing before anyone else clones, because it fires at the exact moment a first-time viewer clicks Book.

YOUR F7, OBSERVED LIVE  The failed booking leaked a HELD date

The booking transaction commits before the payment call, so that crash left the booking stuck at AWAITING_PAYMENT and The Velvet Kings' 1 August slot HELD, on the very first attempt. The date became unbookable for everyone and nothing cleans it up; I had to reseed to continue. That is round 2's F7 moving from "will happen in theory" to "happened on attempt one". Any abandoned real Stripe checkout does the same, so I would pull it up the queue.

       name        |    date    |  status
-------------------+------------+-----------
 The Velvet Kings  | 2026-08-01 | HELD     ← leaked by the failed submit

FRICTION  Booking details typed before sign-in are discarded

Fill the booking form signed out and the banner says "Your details and price are kept exactly as shown". They are not: the sign-in round-trip only preserves act and date, so venue name, postcode and guest count come back blank and the price drops its travel component until retyped. Either carry the fields through the redirect or soften the banner.

COSMETIC  Raw genre enums on the act page

The act header shows SOUL_MOTOWN, FUNK_DISCO where the search cards show "Soul / Motown". Ten minutes, worth it before screenshots start circulating.

Fix list, in order

  1. Blank or guard the Stripe placeholders. One line in .env.example or one guard in paymentsProvider(). Until then the README's happy path fails on every fresh clone.
  2. Release HELD slots when a checkout fails or is abandoned. F7, now observed. An expiry on HELD (or cleanup on payment failure) protects both demos and the eventual real Stripe flow.
  3. Carry booking-form details through the sign-in redirect, or reword the "kept exactly as shown" banner until they are.
  4. Prettify the genre enums on the act page.

Demo-script notes, for whoever is driving

Use the seeded Saturday (1 August 2026) so the availability filtering is visible. Give the booking a venue postcode that is not SW1A 1AA or GL54 1AA: those two match seeded venues with confirmed setups, so the app reuses the cached Venue Database entry (a feature, but it skips the tokenised outreach link the tour wants to show). Keep the intake to one problem, the 85 dB limiter, so the RED flag reads clean. And blank those Stripe lines first.

Evidence

So you do not have to take my word for any of it.

Database ground truth after the tour (Postgres, straight query):

  reference   |     status      | venueReadiness |   contract   | client_signed | act_signed
--------------+-----------------+----------------+--------------+---------------+------------
 SC-2026-0005 | CONTRACT_SIGNED | RED            | FULLY_SIGNED | t             | t

          name          | confirmation | confirmedBy  | thresholdDb |       verdict
------------------------+--------------+--------------+-------------+---------------------
 The Verification Rooms | CONFIRMED    | Vera Manager |          85 | ELECTRONIC_KIT_ONLY

Method: manual run in Chrome first, then a Playwright pass that re-drove the state-changing steps and asserted 13 exact strings (the RED chips, the limiter flag, the readout, the contract states), all 13 found. Screenshots in this report are from that pass, untouched, at /assets/soundcheck-verify-x7k4/.

Machine-readable version of this whole report for your Claude, with repro steps and the raw outputs: soundcheck-demo-report.md. Round 2 itself: the code audit.