The reset-demo-data CronJob in groombook-uat intermittently failed with
FK 23503 on invoice_tip_splits because two pods could run the seed
concurrently: the new pod's TRUNCATE deleted rows the old pod was still
inserting.
Acquire a session-level advisory lock for the full duration of the seed.
CRITICAL: with postgres-js connection pooling, a pg_advisory_lock
acquired on one pooled connection and released on a different one is a
no-op (the lock is bound to the pg-backend that took it). We therefore
reserve a dedicated connection for the lock, take pg_advisory_lock(KEY)
on it, run the seed on the pooled connections, and release the lock +
reserved connection in a try/finally so a thrown seed error cannot leak
the lock or the connection.
Defence-in-depth with the infra PR that switches
concurrencyPolicy: Replace → Forbid on the reset-demo-data CronJob.
- Adds withSeedAdvisoryLock helper and runSeedBody extracted function
- Wraps seed() body in the helper; client.end() runs after the lock
releases so a reserved connection is not returned to a closed pool
- SEED_ADVISORY_LOCK_KEY = 0x47524f4f ("GROO" in ASCII) — arbitrary
stable 32-bit key, referenced in runbooks
- UAT_PLAYBOOK.md §3.29 documents the regression check
cc @cpfarhood
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds a defense-in-depth audit row to impersonationAuditLogs when the
staff-side owner-bypass path fires. Mirrors the failure-isolation
pattern in src/middleware/portalAudit.ts: insert failures are logged
and swallowed so a working read can never turn into a 500.
- New writeOwnerBypassAudit helper called only when isOwner === true.
- No DB migration; petId + actorStaffId go inside metadata jsonb.
- resolveImpersonationClientId stays pure (no audit side effects).
- Positive + negative tests + a cross-tenant regression test.
- UAT_PLAYBOOK.md §3.19d: TC-API-3.19d documents the audit assertion.
Parent tracking: GRO-2062 (Paperclip).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(rbac): port Better-Auth user auto-provision into legacy ./src tree (GRO-2052)
Ports the Better-Auth user-table auto-provision branch from canonical apps/api into the deployed ./src/middleware/rbac.ts so the owner-bypass in pets.ts is reachable for Better-Auth email/password customers. OIDC account branch retained as backward-compat fallback. Adds 5 rbac.test.ts cases and UAT_PLAYBOOK pre-condition docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Flea Flicker <flea@groombook.dev>
Co-committed-by: Flea Flicker <flea@groombook.dev>
Adds an owner-bypass in the profile-summary handler for customers signed in via Better Auth, using the existing X-Impersonation-Session-Id portal session header. When a groomer-role staff row carries a valid impersonation session whose clientId matches the pet's clientId, skip groomerLinkageCheck and serve the summary. Otherwise fall through to the existing linkage check.
Resolves a 403 Forbidden where the customer (auto-provisioned by resolveStaffMiddleware as a 'groomer' staff row with no appointment linkage) could not read their own pet's profile.
Scope: GRO-2013 profile-summary endpoint only — no rbac.ts/schema/Dockerfile changes.
Tests: 6 new cases (owner-bypass, no-header, cross-tenant, expired, manager regression, linked-groomer regression); 294/294 pass.
UAT_PLAYBOOK.md: TC-API-3.19a/b/c.
Closes GRO-2013.
Co-authored-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
Co-committed-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
Updated UAT_PLAYBOOK.md §4.3 — new seed data verification tests.
GRO-1898: After populating extended profile fields in the UAT seed, add
test cases to verify the data is actually present and shaped correctly.
Test cases cover:
- /api/clients returns seed data
- /api/pets/{id} returns all 5 extended fields for UAT test pets
- medicalAlerts shape is correct ({type, description, severity})
- Deterministic UAT pets (Charlie = behavioral alert, Delta = skin alert)
are verifiably populated
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(gro-1866): add session-from-auth portal endpoint + role scope (#93)
Bridges Better Auth SSO sessions to portal sessions for real customers.
Adds role to genericOAuth scopes for Authentik role propagation.
Closes GRO-1866
feat(GRO-1177): add pet profile summary endpoint (#30)
Adds GET /api/pets/:id/profile-summary with aggregated pet profile,
grooming history, visit count, and upcoming appointment.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Returns aggregated pet profile with:
- All pet fields (basic + extended)
- recentGroomingHistory: last 10 entries from groomingVisitLogs with staff name join
- lastVisitDate: most recent groomedAt timestamp
- visitCount: count of completed appointments
- upcomingAppointment: next scheduled/confirmed appointment with service/staff name
Enforces same groomer RBAC as GET /:id. Returns 404 for non-existent pets.
Adds PetProfileSummary, GroomingHistoryEntry, and UpcomingAppointment types.
Adds unit tests covering: 404, 403, aggregated profile, empty history, no upcoming appt.
Updates UAT_PLAYBOOK.md §3 with TC-API-3.8 and TC-API-3.9.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Added TC-API-0.1 for GET /api/health (unauthenticated).
Corrected path from /health to /api/health (GRO-1544).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add TC-API-1.11 through TC-API-1.15 covering existing staff unaffected by
OIDC login, auto-provisioned role/superUser flags, and name fallback
variants (name present, no name+email present, no name+no email).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
1. Fix vi.mock factory: importOriginal -> db.and/eq/exists/or stubs
(removes ReferenceError from undeclared imports in test)
2. Remove MedicalAlert.id — not in schema/migration/DB, only in types
3. Replace z.string().max(100) coatType with z.enum for CoatType union
4. Fix test expecting coatType "smooth" (invalid) -> "double" (valid)
5. Add TC-API-3.8 through TC-API-3.15 to UAT_PLAYBOOK.md §4.3
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(GRO-1272): auto-provision staff record on first OIDC login (#19)
Fixes HTTP 403 on all authenticated routes for new OIDC users by auto-creating
a minimal groomer staff record on first login when a Better-Auth user exists
but no staff record is found.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Address QA review findings on PR #12:
- Add coatType and petSizeCategory to buildPet defaults in packages/db/src/factories.ts
to fix TypeScript typecheck failure
- Restore UAT_PLAYBOOK.md (was deleted during monorepo extraction) and add
§4.15 Buffer Rules test cases
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add user table mock and db.insert returning chain to rbac.test.ts
- Add three new tests: happy-path auto-provision, email-prefix fallback,
and miss-path (no user → 403)
- Add TC-API-1.4 to UAT_PLAYBOOK.md §4.1 for first-login auto-provision
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a seeding step after UAT staff creation that:
- Creates Better-Auth user records (emailVerified: true) for 4 UAT accounts
- Creates account records with providerId="credential" and scrypt-hashed passwords
- Links staff.userId for accounts with existing staff records (super, groomer, tester)
- Reads passwords from SEED_UAT_*_PASSWORD env vars (guard clause skips if unset)
- Is fully idempotent (upsert-safe)
Bypasses Authentik SSO for UAT login; Shedward can authenticate via
POST /api/auth/sign-in/email using the same UAT password secrets.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Created comprehensive UAT playbook covering all 13 route groups with test cases for authentication, client management, pet management, appointment scheduling, services, staff management, invoicing & payments, customer portal, waitlist, search, reports, impersonation, and settings & setup.
Co-Authored-By: Paperclip <noreply@paperclip.ing>