ROADMAP-v3 decisions 21-24: Cross Events moves into crosscode-platform
(2.2.0); one crosscode script deploys Accounts plus the products named;
the Hub tracks releases per product with requirements; the Hub gets the
contracts as a committed file, not from the package registry. Cross Events
versions in the build order move up by one. HANDOVER updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One install of each product per customer; House of Prayer booking
defaults; email and WhatsApp only (no push); Accounts hidden from
members. Pricing: Website R99 with 5 GB plus storage add-on, App from
R99, address once per church (own domain, crosscode.co.za address R10,
domain included R25). Authenticator app 2FA as Cross Events 2.2.0.
Handover now points at ROADMAP-v3; nothing left open in ROADMAP-v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plan for selling three products from one Hub: shared users through a
per-church Accounts service, event sync in any direction on one shared
format (crosscode.event/1), PM2 installs per product per church.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Send now and Remind answer straight away with who they're going to and
queue the feedback-send task; the Feedback page refreshes to show progress.
- One-off background tasks (enqueueTask in src/jobs/index.js): pg-boss with
retries, or in-process after the response when the queue is off.
- Reminders are claimed with the 24-hour rule, so two tasks at once never
remind the same person twice.
- Tests: tests/tasks.test.js, tests/feedbackPage.test.ts, e2e/feedback.spec.ts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Supervisor -> Feedback (supervisors and admins, campus-scoped): every
event's form with answered of asked, response rate, average rating and
last reminder; per event the results per question (with the AI summary),
each response, and on named forms who was asked and who has answered.
- Remind those who haven't answered (or one person on a named form): a
fresh link, plus the form to anyone eligible who hasn't had it; nobody gets
the form or a reminder twice within 24 hours. The 3-day job is unchanged.
- Anonymous forms show no names and no list of who has answered.
- FeedbackInvite.reminderCount (migration 20261103120000_feedback_reminders);
access log action feedback_responses; help page; dashboard and Ctrl+K links.
- Roadmap: the AI helpers on the privacy pages are dropped; 2.1.0 is the
feedback page.
- Tests: tests/feedbackPage.test.ts, e2e/feedback.spec.ts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Heartbeat income bands widened from "over R100 000" to 14 bands up to
"over R10 000 000"; every licence-tier edge is a band edge (tested).
- New income.eventIncome12m: last 12 months' event income without donations,
sponsorships or donation legs, which the licence tier is billed on.
- Fix: donation legs were counted twice in the heartbeat income bands.
- Protocol doc, roadmap pricing decisions, changelog, what's new, handover.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The eight reports that list people get the AI summary too, sent as
anonymised totals built on the screen (aiReportData.ts): no names,
contact details, notes or comments; consent and medical only as counts.
The server refuses personal columns on those reports and logs each
summary in the access log (ai_report; sensitive for consent and medical).
Roadmap §18: 2.0.1, then crosscode.co.za, then 2.1.0 (AI helpers on the
Consent log and Access log).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crosscode.co.za as Cross Code's main site with a Cross Events page;
church website and app as "coming later"; repo joshua/crosscode-site;
own server; no prices yet; contact form to info@crosscode.co.za;
privacy notice and terms of use; no cookies. Content from Joshua later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AI helpers, API v1 removed, Argon2id passwords, React Compiler lint
rules back to errors, migration guide. Docs (HANDOVER, roadmap) mark
2.0.0 as released.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The 192 places the rules flagged are fixed and the rules are errors
again (npx eslint src: nothing). lib/hooks.ts: useStateSync (state
that follows other state, set while rendering), useExternalEffect
(API loads and browser reads), useNow; lib/navigation.ts hardNavigate
for full reloads after sign-out or identity switches; other pages use
the router. Resets moved into the handlers that cause them.
- The banner shows and hides at its times on open pages.
- HANDOVER, roadmap, README, FEATURES, SETTINGS, CHANGELOG updated;
2.0.0 section drafted in WHATS-NEW.
e2e 102/102 (PayFast sandbox skipped); production build passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /api (v1) answers 410 API_V1_REMOVED, except provider webhooks
(/api/webhooks/*, /api/whatsapp/webhook) and links in sent messages
(308s, one-click POST). 43 v1 route files and deprecation.ts removed;
/docs redirects to /docs/v2; API_DOCUMENTATION.md points there.
- Read-only and impersonation lists name v2 paths only. The campus
scope route test now runs over v2 routes (v2CampusScope.test.ts).
- Passwords hashed with Argon2id (@node-rs/argon2); bcrypt hashes still
sign in and are re-hashed at the next sign-in.
- e2e on /api/v2 only (v2() helper); Playwright waits on /api/v2.
- MIGRATING-TO-2.md with the full v1 -> v2 table.
- Two documented response shapes corrected.
Backend 690 tests and e2e 102/102 (PayFast sandbox skipped) pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A setup wizard run on one server added files to backend/src that the
backend build picked up, so the deploy failed part-way and rolled back.
deploy.sh now lists files git reports as changed or added (ignored files
and the PM2 config don't count) and stops before backing up or checking
out. --check runs only that; ALLOW_LOCAL_CHANGES=1 deploys anyway; the
script also runs from a copy outside the install (the folder you're in).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Most routes answered by a v1 handler showed an empty body and
`data: unknown` in /docs/v2. Each now documents what its handler reads
and answers, and every path and query parameter is described.
- `documented: { body?, response? }` on v1Route/defineRoute, with shared
shapes in src/v2/docs.ts. Documentation only: not enforced, so nothing
that works can start failing; outside production a body or answer that
doesn't fit is logged ("doesn't match its documented schema").
- QUERY_HELP / PATH_HELP describe common parameters; empty bodies say
"No body needed".
- Declared the funnel's eventIds and webhook deliveries' status/before.
- tests/v2Docs.test.ts keeps the document complete; frontend types
regenerated, three call sites' types tightened (no payload change).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On events with a required attendee form, tickets waited for the forms, but
At the door and Manual registration never asked for them: payment was
recorded and nobody could be checked in.
- The form pops up at the door and in Manual registration; "Fill in later"
(any staff) issues the tickets once paid and sends the payer a link
(Registration.formWaivedAt, POST /api/v2/registrations/{id}/form-later).
- One form per person on picked-session bookings; a required form with no
questions no longer holds tickets back; every caller asks the ticket
generator (formsOutstanding).
- Kiosk asks for the forms the server says are missing; headings show as text.
- Admin Corrections page and /api/v2/corrections: issue missing tickets,
pay later (ticketsReleasedAt), make it free (complimentaryDiscount),
check in or undo after the event.
- Migration 20261102120000_corrections; unit and e2e tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The site calls AI through the Hub (protocol 3b, Hub 1.6.0): src/ai
works out whether the AI buttons show (licence token first, then the
Hub, cached a minute), runs a helper call with per-helper switches and
a per-person daily limit, logs AiCall rows, and emails admins at 80%.
v2 /ai/status, /ai/usage, /ai/settings; useAiStatus, AiButton, AiTab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HANDOVER: version table in order with 1.51.4/1.51.5 added; Next up
renumbered with the 2.0 clean-ups (lint rules, node-fetch, password
hashing); event picker points at fetchManagedEvents; stale lines fixed.
ROADMAP: status rewritten as a dated summary; section 14 marks what's
done and that CI was removed; Haiku prices flagged to check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /api/v2/webhooks/{yoco,payfast,whatsapp}; new provider set-ups use them, the v1 addresses
keep working with the same handlers.
- Calendar and one-click unsubscribe links point at v2; old links answer 308 (a one-click POST
to the old address is still handled there).
- /api/webhooks/whatsapp runs the real WhatsApp handler (was an empty stub).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: my privacy, unsubscribe links, the privacy admin, audit log, jobs, impersonation,
settings and first-run set-up, the Hub, outbound webhooks, backups, Yoco/PayFast settings.
- Every call the app makes is now on /api/v2 (incoming webhooks and message links: 1.51.16).
- v1Route handles res.download/sendFile; impersonation end/log allowed on v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: tickets (mine, one, send, issue, transfers, scans), scan locks, the attendance register
and its export, certificates, allocations.
- Frontend: apiV2Raw() for string-path helpers; every ticket/attendance screen on v2.
- v1Route can run a v1 router by URL; an unanswered chain is a 404.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: payments, refunds, donations, payment links and the pay page, providers, cash-ups,
provider transactions, waitlist, sponsorship, consent, form responses, feedback.
- src/v2/viaV1.ts: v1Route() answers a v2 route with the v1 handler in-process.
- /docs/v2 opens collapsed with a filter (Swagger UI stopped drawing past ~130 endpoints).
- Frontend: every money screen on v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: staff list (paged), event registrations, registration in full (+ /me), status, manual
registration, bulk, items, invoice, coupon, attendees and form links, form drafts and responses.
- src/services/registrationAdmin.ts holds the rest of registrationController.js (now v1 wrappers).
- Frontend: fetchAllRegistrations(); every registration screen on v2.
- Roadmap: money gets its own release (1.51.11); webhooks and links move to 1.51.16.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: series (+ page, compare, follow), season passes (+ buy, pay, mine), bundle deals
(+ quote), coupon codes (+ check, redemptions), campuses (+ admin, enabled, order, audience).
- src/services/series.ts, deals.ts, campuses.ts; the five controllers are thin v1 wrappers.
- v2 errors carry a service's own code as `reason`.
- Frontend: apiV2Data() for screens with their own record types; these screens on v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: /events/manage, create, PATCH, deactivate, copy-from, notify-recipients.
- src/services/events.ts (from eventController.js, with the schedule helpers); v1 uses it.
- fetchManagedEvents() for every picker; the event wizard writes through v2.
- pg-boss 11 in its own schema (pgboss_v11); old pgboss schema left in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- Next 16 (Turbopack dev and build), React 19.3; tsconfig jsx react-jsx; images remotePatterns.
- ESLint 10 with eslint-config-next 16 flat configs; npm run lint = eslint src. React
Compiler-era react-hooks rules set to warn (181 existing places) until screens move to v2.
- Removed the empty pass-through middleware.ts.
- e2e: wait for the palette to be ready before Ctrl+K on the staff dashboard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- Express 5 (qs query parser and req.body = {} kept as in Express 4).
- Scanner and at-the-door check-in use /api/v2/checkin; look-up gives attendee, bookedBy,
ticketOptionId.
- Shared middleware errors on /api/v2 rewritten to { error: { code, message, reason } };
apiFetch treats reason like v1's code.
- Deprecation + Link: rel="successor-version" on v1 endpoints that have a v2 replacement.
- Roadmap: open question on how much of v1 to retire in 2.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: auth/signup, auth/login (+ code step, rate limited), PATCH /me; check-in look-up and
scan returning an outcome for every normal result. Campus rule as v1.
- Sign up/login/2FA/profile moved to src/services/auth.ts; scan preview and scan to
src/services/checkin.ts. v1 answers unchanged (ServiceError v1Status).
- Login, 2FA step, sign up and profile/password pages use v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: register, get, cancel and checkout a registration (owner/admin only).
- Registration create/cancel moved to src/services/registrations.ts and online checkout to
src/services/payments.ts; v1 controllers call the same services (ServiceError carries status).
- Typed frontend calls: npm run api:types writes openapi.json and schema.d.ts; apiV2() helper.
Register page, Pay now and My Events cancel use v2; apiFetch reads v2 errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- tsconfig (allowJs, strict .ts), npm run build -> dist/, typecheck, dev via tsx, ts-jest.
- src/index.js is now a launcher (dist build when it matches, else tsx); server moved to app.js.
- /api/v2: defineRoute with zod -> request validation, response stripping, OpenAPI 3.1 at
/docs/v2; one error shape and one paged-list shape. First endpoints: events, event, me,
my registrations.
- deploy.sh/install.sh build the backend; rollback to older releases skips the build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- EventPicker replaces the plain event dropdowns on staff/supervisor pages and the kiosk:
upcoming first, past behind "Show past events", search across all, keyboard support.
- Reports events list can be searched ("Select these N").
- Site settings -> Webhooks: "How webhooks work" guide with headers, signature checking
(Node.js/PHP) and an example of every event type (EVENT_TYPES[].example, shape-tested).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
Audit log: search (person, target, IP), role filter, every action in the
filter, inclusive To date. Data requests: search and paging (was cut at
200). Webhook deliveries: status filter and Load older. Supervisor Payments
loads only what it needs (unpaid registrations, recent/today/donation
payments, a person's own for refunds) instead of everything.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
Public pages, auth, registration, payment, My Events and the kiosk: skip
link, focus ring, labels tied to fields, named quantity buttons, announced
errors, ModalOverlay popups (focus trap, Escape, focus return), contrast
and underlined in-text links, reduced motion. Branding warns about a primary
colour too light for white text and offers a darker shade. axe-core and
keyboard checks in e2e/accessibility.spec.ts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>