Skip to content

Consolidate open PRs and feature branches into a single mergeable branch#119

Merged
RJK134 merged 28 commits into
mainfrom
copilot/automate-pr-resolution-and-branch-rationalisation
May 8, 2026
Merged

Consolidate open PRs and feature branches into a single mergeable branch#119
RJK134 merged 28 commits into
mainfrom
copilot/automate-pr-resolution-and-branch-rationalisation

Conversation

Copilot AI commented May 8, 2026

Copy link
Copy Markdown
Contributor

The repo had 4 open PRs (#85, #116, #117, #118) and 6 active feature branches diverging from main, with PR #118 declaring conflicts and PR #85 effectively superseded by a utility that landed on main independently. This branch merges all relevant work into one, resolves conflicts against current main, and de-duplicates the overlapping refactor.

Merges

Conflict resolutions of note

  • auth.ts trustHost — combined main's AUTH_URL || VERCEL with PR feat(demo): LIVE_MAPS override, UAT feedback form, AUTH fixes, demo runbook updates #118's DEMO_MODE requirement:

    trustHost:
      Boolean(process.env.AUTH_URL) ||
      Boolean(process.env.VERCEL) ||
      process.env.DEMO_MODE === 'true',

    Preserves Vercel-preview posture; only loosens host trust under DEMO_MODE.

  • lib/demo/demo-mode.ts — kept case-insensitive EQUISMILE_LIVE_MAPS?.toLowerCase() === 'true'.

  • docs/DEMO_RUNBOOK.md — kept main's accurate field references (checks.googleMaps.status, Pierre Rochat, full route-run yard names) and folded in PR feat(demo): LIVE_MAPS override, UAT feedback form, AUTH fixes, demo runbook updates #118's additive §4a (service-worker cache caveat) + §4b (AUTH_SECRET 500 troubleshooting) and two new failure-mode rows.

  • app/[locale]/triage/page.tsx — removed dead taskLabelKey != null ternaries left over from the refactor merge; the canonical taskTypeLabel returns string.

Branches with no PR

Risk

  • Medium: auth.ts adds DEMO_MODE to the trustHost OR. Production (DEMO_MODE unset) is unchanged.
  • Low: EQUISMILE_LIVE_MAPS=true consumes real Google API quota; documented in .env.example and runbook §7.

Follow-ups for the maintainer

RJK134 and others added 24 commits April 29, 2026 21:09
- Add AbortSignal.timeout(8000) to fetchStatus so the loading
  spinner cannot hang indefinitely when the demo DB is unreachable.
- Fix Data Counts grid: replace grid-cols-3 sm:grid-cols-7 with
  grid-cols-2 xs:grid-cols-3 sm:grid-cols-4 — the 7-column row
  overflowed at 375 px (iPhone SE / 14) and clipped text.
- Shorten full-day workflow button label on xs viewports via
  a hidden/inline-block swap so the button fits a single line.
- Remove stale /api/setup POST reference from runAction; the
  endpoint now returns 410 Gone (Phase 16 slice 7) — calling it
  would show an unhandled 410 in the results panel.
- Create HANDOFF.md summarising iPhone QA status and caveats.
Tomorrow's client demo runs on an iPhone via a Pinggy HTTPS tunnel.
The demo brief asks for live Google Maps (geocoding + Route
Optimization) but simulated WhatsApp + email so a stray click can't
text a real customer mid-walkthrough.

Today DEMO_MODE is a single global flag — flipping it true to enable
the auth bypass and the messaging simulators *also* simulates Google.
There's no way to mix.

This PR adds a per-integration override for Maps only, and ships a
copy-pasteable iPhone-via-Pinggy runbook that uses it.

- lib/demo/demo-mode.ts:
  - isLiveMapsForced() — reads EQUISMILE_LIVE_MAPS=true. Defaults to
    false. Only the Maps client should consult this; WhatsApp +
    email stay gated by isDemoMode() alone.
  - setLiveMapsForced() — test override matching setDemoMode() shape.
- lib/integrations/google-maps.client.ts:
  - getMode() now returns 'live' when DEMO_MODE=true AND
    EQUISMILE_LIVE_MAPS=true (and credentials are present).
    Defensive fallback to 'demo' if credentials are missing —
    EQUISMILE_LIVE_MAPS=true with no API key still simulates rather
    than failing the demo with a runtime error.
  - Function name uses `isLiveMapsForced` rather than `useLiveMaps`
    to avoid eslint-react-hooks/rules-of-hooks misclassifying the
    `use` prefix as a React hook.
- __tests__/unit/integrations/google-maps-optimize.test.ts:
  - 3 new cases — DEMO_MODE+override calls live API, DEMO_MODE
    without override simulates, override-with-no-creds still
    simulates. Existing 8 tests untouched.
- .env.example: EQUISMILE_LIVE_MAPS documented next to DEMO_MODE
  with the explicit "real Google billing applies" warning.
- docs/DEMO_RUNBOOK.md (new): operator runbook covering pre-flight,
  the .env.local block (DEMO_MODE + EQUISMILE_LIVE_MAPS + the
  Pinggy-aware AUTH_URL / NEXT_PUBLIC_APP_URL), reset-and-rehearse
  loop, sign-in shortcut, eight-page walkthrough script keyed off
  the seed-demo fixtures (Mistral urgent / Sarah Mitchell routine /
  Villeneuve→Aigle route), mobile sanity checklist at 390 px, and
  a failure-modes table for the obvious gotchas.

Drive-by: __tests__/unit/offline/queue-replay.test.ts had two
TS errors after PR #65 made `sequence?: number` optional but didn't
update the call sites that pushed `req.sequence` into a
`number[]`. Loosened those two arrays to `Array<number | undefined>`
to match the type. Already passing CI on main, but the local
typecheck flagged it; fixing here as a tiny drive-by.

Verification (local, on a fresh checkout against this branch):
- npm run lint ✓
- npm run typecheck ✓
- npx prisma validate ✓ (with stub DATABASE_URL — same shape as CI)
- npm run test ✓ — 1000/1000 (was 997 baseline + 3 new)
- npm run build ✓

Mobile rehearsal at 390 px on the Pinggy tunnel is the next step
(D3 in the plan); operator runs it. Anything broken on that pass
goes into the same PR.

https://claude.ai/code/session_01WGFmhpjAsc2nq4uyw8YBvS
…form

Three demo-readiness deliverables for tomorrow's iPhone-via-Pinggy
client session, all within the existing PR #66 scope (no new code,
no schema, no tests change).

- scripts/windows/DEMO.bat: prints an "Integration status" block
  after loading .env so the operator can confirm at a glance whether
  the demo will run with live Google Maps + simulated messaging
  (the safe shape) or all-simulated. Specifically warns when
  EQUISMILE_LIVE_MAPS=true is set but GOOGLE_MAPS_API_KEY or
  GCP_PROJECT_ID is missing — the client falls back to the
  simulator silently in that case, which we want surfaced before
  showtime, not during. The footer also now points at
  docs/DEMO_RUNBOOK.md and the new UAT files.

- docs/uat/UAT_VET_PERSONA.md (new): Dr. Rachel Kemp profile,
  matching the seeded demo-staff-rachel (founder + lead vet,
  rachel@equismile.example, brand-colour maroon). Covers her
  typical Tuesday, what she will and won't tolerate, what's in
  scope vs out of scope for the session, and which seeded fixtures
  map to her real coverage area (Mistral / Sophie Dupuis FR
  urgent / Sarah Mitchell routine / Villeneuve→Aigle route).
  Designed so the test reads as one continuous narrative rather
  than "I'm a generic admin pretending to be a vet."

- docs/uat/UAT_FEEDBACK_REPORT.md (new): browser-readable
  fillable form covering all 12 demo surfaces (sign-in, dashboard,
  enquiries, triage, planning, route generation with live Google,
  route review, appointments, completed, bilingual, mobile polish
  at 390px, offline). Each section has tick-box pass/fail/partial
  + structured checks + free-form notes. Bottom-of-form has a
  numbered findings log with severity, a separate
  "lack-of-function" list (enhancements vs bugs), and an overall
  verdict with the "would you use this Tuesday?" gate.

  Form opens in any browser via GitHub markdown render or a
  local markdown preview. The vet fills it in during/after the
  session and either pastes into a GitHub PR comment, emails it,
  or hands it over in person.

- scripts/windows/README.md: new "Client-demo flow" section
  documenting the .env additions for live Maps + Pinggy, with
  cross-references to DEMO_RUNBOOK and the two UAT files.

Verification: lint, typecheck, prisma validate, test (1000/1000),
build all green locally — same green state as the previous commit
on this branch.

https://claude.ai/code/session_01WGFmhpjAsc2nq4uyw8YBvS
Companion to docs/uat/UAT_VET_PERSONA.md and
docs/uat/UAT_FEEDBACK_REPORT.md (markdown). The vet wanted to fill
the report in the browser instead of editing markdown — this is the
direct delivery on that ask.

- public/uat-feedback.html — single self-contained static file:
  - Mobile-first at 390 px (iPhone Safari is the target). 44 px
    minimum tap targets, safe-area-inset-aware, brand-maroon header.
  - 12 sections matching the markdown report 1:1: sign-in,
    dashboard, enquiries, triage, planning, route generation (live
    Google), route review, appointments, completed, bilingual,
    mobile polish, offline. Each has Pass / Partial / Fail radios,
    structured pass/fail checklist, and a free-form notes textarea.
  - Findings log table (5 rows pre-rendered + "+ Add row" button)
    with severity dropdown and repro selector.
  - Lack-of-function + enhancement-ideas free-form textareas.
  - Overall verdict block — "Would you use this Tuesday?" three-way,
    biggest-fix, biggest-cheer, free-form summary.

- Persistence: localStorage auto-save on every input. Survives a
  page refresh or accidental tab close. "Clear" button at the
  bottom for a fresh start. Save status shown live ("Saved locally
  ✓ HH:MM:SS").

- Export: "Copy report" opens a modal with the full report rendered
  as markdown (matches docs/uat/UAT_FEEDBACK_REPORT.md shape) ready
  to paste into a GitHub PR comment, email, or Slack DM.
  "Download .md" saves it as a file.

- No build step. No JS framework. No external dependencies. Static
  HTML+CSS+inline JS. Next.js serves anything in public/ directly,
  so it's reachable at:
    http://localhost:3000/uat-feedback.html  (laptop)
    https://<tunnel>.pinggy.io/uat-feedback.html  (iPhone via Pinggy)
  The page does not require auth — it's a feedback form, not part
  of the operational app, so the vet can hit it without going
  through demo sign-in first.

- No code/server/test changes. Static asset only.

Verification: lint + typecheck still green.

https://claude.ai/code/session_01WGFmhpjAsc2nq4uyw8YBvS
Two papercuts caught during the live rehearsal of tomorrow's iPhone
demo. Both are demo-only (production unaffected).

- scripts/windows/DEMO.bat:
  - Sets a deterministic demo-only AUTH_SECRET if .env doesn't
    provide one. Without this, Auth.js v5 returns 500 on every
    /api/auth/session call and the dashboard never loads after
    sign-in. The default is intentionally non-secret and the value
    starts with "demo-" so the integration-status block at startup
    can flag it ("AUTH_SECRET = demo fallback") versus a real .env
    value ("AUTH_SECRET = from .env"), versus the failure mode
    where neither is set ("[WARNING] not set - Auth.js will 500").
  - Same shape as the existing DATABASE_URL fallback above it. .env
    still wins because it's loaded after these defaults.

- docs/DEMO_RUNBOOK.md:
  - New §4a "First-time browser setup" — explains the symptom an
    operator hits when the browser has previously cached an older
    EquiSmile build (PWA service worker replays the old login page
    with the production CSP, which includes upgrade-insecure-
    requests, which silently rewrites the form POST to HTTPS,
    which then doesn't match the HTTP-only allow-list in the
    current CSP). Step-by-step fix for both Chrome/Edge DevTools
    and iPhone Safari → Website Data.
  - New §4b "Auth.js 500 on /api/auth/session" — the AUTH_SECRET
    diagnostic, with the integration-status string the operator
    will see in DEMO.bat at startup.
  - Two new rows in §8 "Failure modes" table, cross-referencing
    the new sections, so the operator can find the fix from
    either entry point.

Verification: lint + typecheck green. No code change, no test
change. Bat-file change is local to DEMO.bat only; LAUNCH.bat,
production deploys, and CI all unchanged.

https://claude.ai/code/session_01WGFmhpjAsc2nq4uyw8YBvS
A fresh demo run without AUTH_URL set in .env hits this every time:

  [auth][error] UntrustedHost: Host must be trusted.
  URL was: http://localhost:3000/api/auth/session
  [auth][error] UntrustedHost: Host must be trusted.
  URL was: http://0.0.0.0:3000/api/auth/session

Two distinct host strings show up because DEMO.bat binds the Next
server with HOSTNAME=0.0.0.0 (correct, the Pinggy tunnel needs it),
so Next's own SSR fetches use 0.0.0.0:3000 while the browser hits
localhost:3000. Auth.js v5 rejects both unless trustHost is on.

Today the gate was `trustHost: Boolean(process.env.AUTH_URL)` —
correct for production behind a known proxy, but breaks the demo
shape where neither AUTH_URL nor a known proxy host is set, AND
where the bind hostname differs from the browser hostname.

- auth.ts: trustHost now ORs with `DEMO_MODE === 'true'`. Demo mode
  unconditionally trusts whatever Host header arrives. The trust
  boundary is already loosened by DEMO_MODE elsewhere (CSP relaxed
  in lib/security/headers.ts, form-action allow-list, /api/demo/
  sign-in bypass), so this is consistent. Production / non-demo
  deploys still require explicit AUTH_URL — unchanged behaviour.

- scripts/windows/DEMO.bat: belt-and-braces — sets a fallback
  AUTH_URL=http://localhost:3000 alongside the existing AUTH_SECRET
  fallback. .env still wins because it's loaded after these defaults.
  Auth.js callbacks construct absolute redirect URLs from AUTH_URL,
  so providing it explicitly avoids any guesswork even with the
  trustHost loosening above.

Verification: lint, typecheck, test (1000/1000) all green. No
production code path changed — the OR with `DEMO_MODE === 'true'`
short-circuits to the existing `Boolean(process.env.AUTH_URL)` when
DEMO_MODE is unset.

Closes the second blocker hit during today's iPhone-via-Pinggy
rehearsal. First blocker (CSP form-action via stale SW cache) was
addressed in 19836d6.

https://claude.ai/code/session_01WGFmhpjAsc2nq4uyw8YBvS
The dashboard's "Triage Tasks" panel was rendering raw enum values
(URGENT_REVIEW, CLARIFY_SYMPTOMS, ASK_FOR_POSTCODE, ASK_HORSE_COUNT,
MANUAL_CLASSIFICATION) where the triage page itself maps them through
i18n via taskTypeLabel + t().

Mirrors the pattern at app/[locale]/triage/page.tsx:184–193 and 321:
- Adds useTranslations('triage') as tTriage.
- Adds the same taskTypeLabel(taskType) mapping.
- Replaces {task.taskType} with {tTriage(taskTypeLabel(task.taskType))}.

All five enum values are already keyed in messages/{en,fr}.json under
the triage namespace (urgentReview, askPostcode, askHorseCount,
clarifySymptoms, manualClassification). Fallback returns the raw
value if the map ever drifts out of sync.

Closes Finding 7 of docs/UAT_v1_1_TRIAGE_round2.md.

https://claude.ai/code/session_01PF4MU4P4Ytt9UcuVsbAzT5
Both dashboard and triage pages had an identical taskTypeLabel mapping
copy-pasted between them. Move it to lib/utils/task-type-label.ts so
future enum or translation key changes only need to be made in one
place.
…-001

Fixes Vercel build failure (PrismaClientKnownRequestError P2003 on Invoice_visitOutcomeId_fkey).

The demo seed for invoice demo-inv-001 referenced visitOutcomeId 'demo-outcome-completed', which is never created. Actual VisitOutcome rows use IDs of the form demo-outcome-001..008. This commit retargets demo-inv-001 to the first valid outcome id.
demo-outcome-001 belongs to Marie's first visit; use demo-outcome-005
for customer demo-cust-jeanluc to keep invoice–outcome customer alignment.

Applied via @cursor push command
Co-authored-by: Richard Knapp <RJK134@users.noreply.github.com>
Agent-Logs-Url: https://github.com/RJK134/EquiSmile/sessions/80cffda9-167f-49c6-85a9-cd617d839dcc

Co-authored-by: RJK134 <167345619+RJK134@users.noreply.github.com>
Agent-Logs-Url: https://github.com/RJK134/EquiSmile/sessions/80cffda9-167f-49c6-85a9-cd617d839dcc

Co-authored-by: RJK134 <167345619+RJK134@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/RJK134/EquiSmile/sessions/80cffda9-167f-49c6-85a9-cd617d839dcc

Co-authored-by: RJK134 <167345619+RJK134@users.noreply.github.com>
Co-authored-by: Richard Knapp <RJK134@users.noreply.github.com>
… outcome

Co-authored-by: RJK134 <167345619+RJK134@users.noreply.github.com>
…UTH fixes, demo runbook

# Conflicts:
#	auth.ts
#	docs/DEMO_RUNBOOK.md
#	lib/demo/demo-mode.ts
#	lib/integrations/google-maps.client.ts

Co-authored-by: RJK134 <167345619+RJK134@users.noreply.github.com>
…n-link audit

Co-authored-by: RJK134 <167345619+RJK134@users.noreply.github.com>
…ility

# Conflicts:
#	app/[locale]/dashboard/page.tsx
#	app/[locale]/triage/page.tsx

Co-authored-by: RJK134 <167345619+RJK134@users.noreply.github.com>
…view feedback)

Agent-Logs-Url: https://github.com/RJK134/EquiSmile/sessions/9679dd49-fcf3-47e2-8f8f-23b647c3c28b

Co-authored-by: RJK134 <167345619+RJK134@users.noreply.github.com>
@vercel

vercel Bot commented May 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
equismile Error Error May 8, 2026 11:41am

Request Review

Copilot AI added a commit that referenced this pull request May 8, 2026
…onical utility

- Re-point dashboard and triage page imports from lib/utils/task-type-label
  to the canonical lib/utils/triage-task-type (landed via PR #119)
- Simplify call sites to direct t(taskTypeLabel(...)) — safe because
  TRIAGE_TASK_TYPE_LABEL_MAP is typed against the full TriageTaskType enum,
  so all known values are covered; unknown values fall back to the raw string
- Remove superseded lib/utils/task-type-label.ts
- Rename __tests__/unit/utils/task-type-label.test.ts →
  __tests__/unit/utils/triage-task-type.test.ts; update import and
  fallback assertions to match the canonical utility's string-return contract

Co-authored-by: RJK134 <167345619+RJK134@users.noreply.github.com>
@RJK134 RJK134 marked this pull request as ready for review May 8, 2026 11:23
@RJK134 RJK134 self-requested a review as a code owner May 8, 2026 11:23
Copilot AI review requested due to automatic review settings May 8, 2026 11:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates multiple open PRs/feature branches into a single mergeable branch, resolving conflicts against current main, de-duplicating an overlapping task-label refactor, and improving the demo/UAT workflow (auth stability, live-Google-maps override, mobile demo UX, and operator/UAT documentation/assets).

Changes:

  • Demo reliability/UX: add fetch timeouts, mobile layout fixes, and improved demo workflow scripting (including an integration-status readout).
  • Demo configuration/auth: case-insensitive EQUISMILE_LIVE_MAPS override and demo-mode trustHost behavior in Auth.js config.
  • UAT collateral + seed correction: add UAT persona/report templates and a browser-fillable feedback form; fix demo seed invoice → visitOutcome linkage and add tests.

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
scripts/windows/README.md Documents the Windows demo flow (Pinggy + live Google Maps) and links to runbook/UAT docs.
scripts/windows/DEMO.bat Adds demo-safe Auth.js defaults, prints integration status at startup, and improves operator guidance for mobile tunnel usage.
public/uat-feedback.html Adds a standalone, locally-persisted UAT feedback form with markdown export.
prisma/seed-demo.ts Fixes demo-inv-001 to reference a valid, customer-aligned VisitOutcome.
lib/demo/demo-mode.ts Makes EQUISMILE_LIVE_MAPS parsing case-insensitive.
HANDOFF.md Adds an iPhone demo QA handoff note and known caveats.
docs/uat/UAT_VET_PERSONA.md Adds the vet persona used during UAT, aligned to seeded demo fixtures.
docs/uat/UAT_FEEDBACK_REPORT.md Adds a markdown-based UAT feedback template (tick-box walkthrough + findings log).
docs/DEMO_RUNBOOK.md Expands the runbook with service-worker cache troubleshooting and Auth.js AUTH_SECRET troubleshooting.
auth.ts Extends Auth.js trustHost behavior to include DEMO_MODE=true.
app/[locale]/triage/page.tsx Removes leftover whitespace from the refactor merge.
app/[locale]/demo/page.tsx Adds abort/timeouts for demo status/actions, refines mobile layout, and improves action-result rendering on small screens.
tests/unit/utils/task-type-label.test.ts Adds unit coverage for taskTypeLabel’s mapping + fallback behavior.
tests/unit/seed.test.ts Adds a guard test verifying the corrected invoice → outcome linkage in the demo seed file.
tests/integration/demo-mode.integration.test.ts Adds integration coverage for case-insensitive EQUISMILE_LIVE_MAPS handling and ensures env/test cleanup.

Comment thread scripts/windows/DEMO.bat Outdated
Comment thread HANDOFF.md Outdated
Comment thread HANDOFF.md Outdated
Comment thread public/uat-feedback.html
if (sec.checks && sec.checks.length) {
html += '<ul class="checks">';
sec.checks.forEach((c, i) => {
html += `<li><input type="checkbox" id="${sec.id}_chk${i}" data-key="${sec.id}.checks.${i}" /><span>${c}</span></li>`;
Comment thread public/uat-feedback.html
Comment on lines +636 to +641
document.getElementById('btn-export').addEventListener('click', () => {
exportText.value = buildMarkdown();
if (typeof dlg.showModal === 'function') dlg.showModal();
else dlg.setAttribute('open', '');
});
document.getElementById('dlg-close').addEventListener('click', () => dlg.close());
Comment thread public/uat-feedback.html
Comment on lines +572 to +580
const m = state.meta || {};
lines.push('| Field | Value |');
lines.push('|---|---|');
lines.push(`| Tester | ${m.tester || ''} |`);
lines.push(`| Date | ${m.date || ''} |`);
lines.push(`| Build | ${m.build || ''} |`);
lines.push(`| Device | ${m.device || ''} |`);
lines.push(`| Network | ${m.network || ''} |`);
lines.push(`| Tunnel URL | ${m.tunnel || ''} |`);
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@RJK134 RJK134 enabled auto-merge (squash) May 8, 2026 11:41
@RJK134 RJK134 disabled auto-merge May 8, 2026 11:41
@RJK134 RJK134 merged commit d8b4dd4 into main May 8, 2026
4 of 5 checks passed
@RJK134 RJK134 deleted the copilot/automate-pr-resolution-and-branch-rationalisation branch May 8, 2026 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants