Compare commits

..

49 Commits

Author SHA1 Message Date
Flea Flicker ff85ed31ad feat(GRO-2359): add POST /api/portal/clients-from-auth for OOBE (web)
CI / Test (pull_request) Successful in 25s
CI / Lint & Typecheck (pull_request) Successful in 27s
CI / Build & Push Docker Images (pull_request) Successful in 1m26s
The OOBE flow on the web portal calls this endpoint to create a fresh
`clients` row bound to the Better Auth user's email when the SSO
bridge returns 404. Returns 201 on success, 409 if a client with that
email already exists (portal-selection case), 401/503 on auth issues,
400 on invalid body.

The OOBE success path navigates the user back to `/` and lets the
existing `session-from-auth` re-bridge; the new client is now
resolvable by email, so the bridge mints a real portal session.

Tests cover: 401 (no session), 400 (zod), 201 + persisted values
(name trimmed, optional fields normalized to null), 409 (existing
client or unique-constraint race), 503 (auth not configured).

Paired with the web PR on `feature/2357-p2-sso-to-oobe-routing`.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
(cherry picked from commit cdeebec021)
2026-06-11 18:32:38 +00:00
Flea Flicker 58305d7a89 uat→main (PROD): GRO-2342 portal waitlist service {id, name} (frozen @47e2021 + cherry-pick c737bfe) (#211)
CI / Test (push) Successful in 29s
CI / Lint & Typecheck (push) Successful in 32s
CI / Build & Push Docker Images (push) Failing after 57s
Merge pull request 'GRO-2342: portal/appointments — symmetric service {id, name} on both card paths' (#211) from release/main-GRO-2342-api into main

GRO-2342: GET /portal/appointments populates service: {id, name} on the synthetic waitlist card (was {id} only) and on the appointment card (consistent shape). TC-API-8.20 in UAT_PLAYBOOK.md.

Approved CTO. Squashed from release/main-GRO-2342-api @ c737bfe.

Refs: GRO-2342, GRO-2344, GRO-2345, GRO-2346, PR #211.
Co-authored-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
Co-committed-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
2026-06-11 08:33:52 +00:00
Flea Flicker 47e2021cf4 Promote uat → main (PROD): GRO-2319 portal waitlist surfacing + seed (#207)
CI / Test (push) Successful in 23s
CI / Lint & Typecheck (push) Successful in 26s
CI / Build & Push Docker Images (push) Successful in 41s
Co-authored-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
Co-committed-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
2026-06-10 08:58:26 +00:00
Flea Flicker 31404befee uat→main (PROD): GRO-2311 seed portal StatusBadge appointments (frozen @df5e768) (#206)
CI / Test (push) Successful in 26s
CI / Lint & Typecheck (push) Successful in 29s
CI / Build & Push Docker Images (push) Successful in 31s
uat→main (PROD): GRO-2311 seed portal StatusBadge appointments (squash)

Co-authored-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
Co-committed-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
2026-06-09 11:18:03 +00:00
Scrubs McBarkley a9db0ca9ac uat→main (PROD): GRO-2172 pet extended-field schema fix (frozen @c4385617) (#203)
CI / Test (push) Successful in 26s
CI / Lint & Typecheck (push) Successful in 29s
CI / Build & Push Docker Images (push) Successful in 1m7s
Phase-4 promotion. CTO-approved, CI 3/3 green. Single-file diff (src/routes/pets.ts +26/-2), frozen @c4385617. GRO-2311 excluded.
2026-06-09 10:52:37 +00:00
Flea Flicker 4bbb0c9fc5 uat→main (PROD): GRO-2172 pet extended-field schema fix (frozen @c4385617)
CI / Test (pull_request) Successful in 30s
CI / Lint & Typecheck (pull_request) Successful in 34s
CI / Build & Push Docker Images (pull_request) Successful in 1m21s
Promote GRO-2172 from uat to main. Pins src/routes/pets.ts to its exact
content at uat merge commit c4385617 (PR #200), adding the extended pet
profile fields to createPetSchema/updatePetSchema and wiring medicalAlerts
into POST/PATCH /pets:

- temperamentScore: int 1–5
- temperamentFlags: string[] (≤20, each ≤100 chars)
- medicalAlerts: {type,description,severity}[] (≤50)
- preferredCuts: string[] (≤20, each ≤200 chars)
- coatType already present on main; schema now references all 5 fields

Based on main HEAD (03f79a37) so the PR diff is limited to src/routes/pets.ts.
GRO-2311 (uat HEAD 807ccb45) is intentionally excluded.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-09 10:19:25 +00:00
Flea Flicker 03f79a3701 uat → main: GRO-2299 redact googleMapsApiKey from PATCH /api/admin/settings (#198)
CI / Test (push) Successful in 27s
CI / Lint & Typecheck (push) Successful in 30s
CI / Build & Push Docker Images (push) Successful in 30s
GRO-2299: redact googleMapsApiKey from PATCH /api/admin/settings response
Co-authored-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
Co-committed-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
2026-06-09 07:49:49 +00:00
Flea Flicker 2b92c2ab6c uat→main (PROD): GRO-2294 Route Optimization security hardening (frozen @2566fb8) (#197)
CI / Lint & Typecheck (push) Successful in 30s
CI / Test (push) Failing after 11m41s
CI / Build & Push Docker Images (push) Has been skipped
feat(security): GRO-2294 Route Optimization security hardening [squash]

Co-authored-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
Co-committed-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
2026-06-09 07:38:02 +00:00
Flea Flicker e9ad92de01 uat→main (PROD): GRO-2157 nav export + GRO-2225/2235 (frozen @4868f18) (#192)
CI / Test (push) Successful in 28s
CI / Lint & Typecheck (push) Successful in 31s
CI / Build & Push Docker Images (push) Successful in 28s
feat: nav export + conflict guard + UAT seed (GRO-2157, GRO-2225, GRO-2235)

Squash-merges PR #192: uat→main PROD promotion.
Freezes at validated SHA 4868f18 (UAT regression GRO-2261 11/11 PASS).
Bundles: GRO-2157 (nav export), GRO-2225 (UAT seed), GRO-2235 (conflict guard).
CTO-reviewed and approved (review #4542).

Co-authored-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
Co-committed-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
2026-06-09 01:23:06 +00:00
Flea Flicker bfe1a29c08 Merge pull request 'uat→main (PROD): GRO-2234 portal session fix + validated batch' (#191) from flea/uat-to-main-gro-2234-api into main
CI / Test (push) Successful in 26s
CI / Lint & Typecheck (push) Successful in 29s
CI / Build & Push Docker Images (push) Successful in 28s
2026-06-09 00:37:35 +00:00
Scrubs McBarkley 1ad43ce701 Merge pull request 'promote(uat→main FROZEN @6120b96): + GRO-2156 route buffer/reorder (supersedes #185)' (#186) from release/main-6120b96 into main
CI / Test (push) Successful in 25s
CI / Lint & Typecheck (push) Successful in 29s
CI / Build & Push Docker Images (push) Successful in 1m19s
promote(uat→main FROZEN @6120b96): GRO-2214+GRO-2211+GRO-2203+GRO-2155+GRO-2163+GRO-2156

CTO-reviewed, CEO-merged per SDLC Phase 4 governance.
Carries: GRO-2214 waitlist validation, GRO-2211, GRO-2203 pet PATCH, GRO-2155+GRO-2163 route optimization, GRO-2156 route buffer/reorder.
All gates passed: QA, Security, UAT 6/6 PASS.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-08 23:29:58 +00:00
Flea Flicker 96dbb8c41d Merge pull request 'Promote dev → uat: GRO-2155/2156/2203/2211/2163 + GRO-2234 (cumulative batch)' (#182) from flea/dev-to-uat-gro-2156 into uat
CI / Test (push) Successful in 25s
CI / Lint & Typecheck (push) Successful in 30s
CI / Build & Push Docker Images (push) Successful in 1m24s
CI / Test (pull_request) Successful in 27s
CI / Lint & Typecheck (pull_request) Successful in 30s
CI / Build & Push Docker Images (pull_request) Successful in 1m11s
2026-06-08 19:42:25 +00:00
Flea Flicker 636fa713e1 Merge dev into uat: add GRO-2234 portal session sliding TTL + re-mint to dev→uat batch
CI / Test (pull_request) Successful in 28s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Images (pull_request) Successful in 27s
2026-06-08 19:17:15 +00:00
Flea Flicker 6120b96c7c Merge dev into uat: promote GRO-2156 route travel buffer + reorder (Phase 2.2)
CI / Test (pull_request) Successful in 26s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Images (pull_request) Successful in 1m2s
Resolves UAT_PLAYBOOK.md conflict by unioning uat-only TC-UAT-2/3 (GRO-2100)
with dev's §4.16 update + new §4.17. Code files taken from dev (superset).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-08 18:11:05 +00:00
Flea Flicker eb92f99c4a dev → uat: GRO-2203 portal pet PATCH malformed-petId 500→404 (#178)
CI / Test (push) Successful in 27s
CI / Lint & Typecheck (push) Successful in 32s
CI / Build & Push Docker Images (push) Successful in 1m1s
CI / Test (pull_request) Successful in 27s
CI / Lint & Typecheck (pull_request) Successful in 31s
CI / Build & Push Docker Images (pull_request) Successful in 1m4s
2026-06-08 17:53:01 +00:00
Flea Flicker 587fd4ec95 dev → uat: GRO-2155 route optimization endpoints (carries GRO-2163) (#176)
CI / Test (push) Successful in 26s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Images (push) Successful in 25s
2026-06-08 16:45:44 +00:00
Flea Flicker 6e2e46daf8 Merge uat → main: portal pet PATCH + photoKey S3 key-hijack fix (GRO-2187) (#174)
CI / Test (push) Successful in 25s
CI / Lint & Typecheck (push) Successful in 28s
CI / Build & Push Docker Images (push) Successful in 40s
2026-06-08 13:25:46 +00:00
Flea Flicker 8cf72d926d dev → uat: portal photoKey S3 key-hijack fix (GRO-2187/GRO-2198) (#173)
CI / Test (push) Successful in 22s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Images (push) Successful in 43s
CI / Test (pull_request) Successful in 27s
CI / Lint & Typecheck (pull_request) Successful in 32s
CI / Build & Push Docker Images (pull_request) Successful in 39s
2026-06-08 12:39:52 +00:00
Flea Flicker 8721f0b63c dev → uat: GRO-2154 geocoding endpoints (Phase 1.3) (#171)
CI / Test (push) Successful in 24s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Images (push) Successful in 35s
2026-06-08 12:06:43 +00:00
Flea Flicker 027e012a58 Merge pull request 'dev → uat: GRO-2153 abstracted geocoding service' (#168) from dev-to-uat-gro-2153 into uat
CI / Test (push) Successful in 1m5s
CI / Lint & Typecheck (push) Successful in 43m29s
CI / Build & Push Docker Images (push) Successful in 1m7s
2026-06-08 10:51:17 +00:00
Flea Flicker b3db206588 Merge pull request 'dev → uat: GRO-2187 portal pet PATCH + GET enrichment (carries GRO-2152)' (#166) from dev-to-uat-gro-2187 into uat
CI / Test (push) Successful in 1m19s
CI / Lint & Typecheck (push) Successful in 1m25s
CI / Build & Push Docker Images (push) Successful in 3m58s
2026-06-08 10:02:17 +00:00
Flea Flicker fc072d51f4 Merge pull request 'promote(uat→main): GRO-2123 seed advisory lock + GRO-2100 uat-groomer linkage ordering' (#157) from uat into main
CI / Test (push) Successful in 14s
CI / Lint & Typecheck (push) Successful in 17s
CI / Build & Push Docker Images (push) Successful in 41s
2026-06-04 12:53:06 +00:00
Flea Flicker 6538406db2 Merge pull request 'chore: delete stale apps/api/src/db/seed.ts duplicate (GRO-2129)' (#158) from dev into uat
CI / Test (push) Successful in 12s
CI / Lint & Typecheck (push) Successful in 18s
CI / Build & Push Docker Images (push) Successful in 38s
CI / Test (pull_request) Successful in 22s
CI / Lint & Typecheck (pull_request) Successful in 25s
CI / Build & Push Docker Images (pull_request) Successful in 38s
2026-06-04 12:45:24 +00:00
Flea Flicker e2eacbc9fe Merge pull request 'dev → uat: GRO-2123 seed advisory lock' (#156) from dev-to-uat-gro-2123 into uat
CI / Test (push) Successful in 16s
CI / Lint & Typecheck (push) Successful in 16s
CI / Build & Push Docker Images (push) Successful in 40s
CI / Test (pull_request) Successful in 12s
CI / Lint & Typecheck (pull_request) Successful in 15s
CI / Build & Push Docker Images (pull_request) Successful in 39s
2026-06-04 11:32:06 +00:00
Flea Flicker e639cc82d1 chore(uat): GRO-2100 promote uat-groomer seed-linkage ordering fix to uat (#154)
CI / Test (push) Successful in 16s
CI / Lint & Typecheck (push) Successful in 19s
CI / Build & Push Docker Images (push) Successful in 27s
Co-authored-by: Flea Flicker <flea@groombook.dev>
Co-committed-by: Flea Flicker <flea@groombook.dev>
2026-06-02 20:23:54 +00:00
Flea Flicker f2931d7be2 Merge pull request 'Promote dev→uat: GRO-2100 uat-groomer ↔ UAT Pup Alpha linkage' (#152) from promote/dev-to-uat-gro-2100 into uat
CI / Test (push) Successful in 13s
CI / Lint & Typecheck (push) Successful in 18s
CI / Build & Push Docker Images (push) Successful in 26s
Merge pull request #152 from groombook/promote/dev-to-uat-gro-2100

Promote dev→uat: GRO-2100 uat-groomer ↔ UAT Pup Alpha linkage
2026-06-02 19:11:46 +00:00
Paperclip d4a4ddce37 ci: retrigger GRO-2100 PR #152 Build & Push Docker Images (Reset image build failed — docker registry flake)
CI / Test (pull_request) Successful in 13s
CI / Lint & Typecheck (pull_request) Successful in 17s
CI / Build & Push Docker Images (pull_request) Successful in 40s
2026-06-02 18:28:17 +00:00
Paperclip bd384bdf5c docs(UAT_PLAYBOOK): add TC-UAT-2/3 for uat-groomer linked/unlinked pet profile-summary (GRO-2100)
CI / Lint & Typecheck (pull_request) Successful in 16s
CI / Test (pull_request) Successful in 2m20s
CI / Build & Push Docker Images (pull_request) Failing after 36s
Lint Roller review on PR #152 flagged that the GRO-2100 seed change produces
new observable UAT API behavior that the playbook must reflect. Add two
deterministic rows pinning the contract GRO-1987 TC-UAT-2/3 will exercise:

- TC-UAT-2: uat-groomer + linked pet c0000001-...-002 (UAT Pup Alpha) → 200
- TC-UAT-3: uat-groomer + unlinked pet c0000001-...-003 (UAT Pup Beta) → 403

The 403-vs-404 note in TC-UAT-3 mirrors the verification note in the
GRO-2100 issue body so the QA runner knows where to file if the API
returns 404 (a separate RBAC defect, not against the seed).
2026-06-02 18:24:40 +00:00
Scrubs McBarkley c92fb2539d promote(uat→main): owner-bypass audit fix (GRO-2062) + services seed-idempotency fix (GRO-2064)
CI / Test (push) Successful in 14s
CI / Lint & Typecheck (push) Successful in 16s
CI / Build & Push Docker Images (push) Successful in 25s
2026-06-02 06:00:02 +00:00
The Dogfather 411c42b2c4 Merge pull request 'Promote dev→uat: GRO-2033 services_pkey seed fix (fc6c6ef7)' (#149) from dev into uat
CI / Test (push) Successful in 14s
CI / Lint & Typecheck (push) Successful in 16s
CI / Build & Push Docker Images (push) Successful in 39s
CI / Test (pull_request) Successful in 12s
CI / Lint & Typecheck (pull_request) Successful in 16s
CI / Build & Push Docker Images (pull_request) Successful in 38s
2026-06-02 05:06:34 +00:00
The Dogfather bf97849324 promote(dev→uat): owner-bypass read audit row (GRO-2063) (#147)
CI / Test (push) Successful in 12s
CI / Lint & Typecheck (push) Successful in 17s
CI / Build & Push Docker Images (push) Successful in 41s
Promote GRO-2063 defense-in-depth audit row to uat. CI green. QA + CTO approved on dev PR #146.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-02 04:21:43 +00:00
Scrubs McBarkley 2a6242d3de Merge pull request 'promote(main): GRO-2033 prod migration fix + GRO-2013/2014 + rbac auto-provision (uat→main)' (#145) from uat into main
CI / Test (push) Successful in 13s
CI / Lint & Typecheck (push) Successful in 18s
CI / Build & Push Docker Images (push) Successful in 30s
promote(main): GRO-2033 prod migration fix + GRO-2013/2014 + rbac auto-provision (uat→main)

CI green. UAT regression GRO-2035 PASS. Migrations 0039/0040 idempotent — signed off by CEO.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-02 03:22:21 +00:00
The Dogfather 7181d41b24 Merge pull request 'Promote dev→uat: rbac Better-Auth auto-provision (GRO-2052)' (#144) from dev into uat
CI / Test (push) Successful in 13s
CI / Lint & Typecheck (push) Successful in 15s
CI / Build & Push Docker Images (push) Failing after 13s
CI / Test (pull_request) Successful in 12s
CI / Lint & Typecheck (pull_request) Successful in 15s
CI / Build & Push Docker Images (pull_request) Successful in 41s
Promote dev→uat: rbac Better-Auth auto-provision (GRO-2052)

Makes the pets.ts owner-bypass reachable for Better-Auth email/password customers by auto-provisioning a groomer staff row keyed on user.id. Unblocks GRO-2050 and GRO-2035.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 02:42:19 +00:00
The Dogfather 4e9c4c5e08 Merge pull request 'promote(uat): GRO-2013 owner-bypass + GRO-2033 idempotent migrations (dev→uat)' (#142) from dogfather/gro-2013-promote-uat into uat
CI / Test (push) Successful in 13s
CI / Lint & Typecheck (push) Successful in 18s
CI / Build & Push Docker Images (push) Successful in 39s
2026-06-01 20:14:14 +00:00
The Dogfather 16c959434b promote(uat): GRO-2013 owner-bypass + GRO-2033 idempotent migrations (dev→uat)
CI / Test (pull_request) Successful in 11s
CI / Lint & Typecheck (pull_request) Successful in 16s
CI / Build & Push Docker Images (pull_request) Successful in 41s
Merge dev into uat. Resolves test-file/playbook conflicts created by PR #138's
squash merge by taking dev's superset versions (verified: all GRO-2014 tests +
TC ids preserved, plus GRO-2013 additions). No-ff merge so dev becomes an
ancestor of uat, preventing future squash-divergence conflicts.

Carries:
- GRO-2013 deployed-tree owner-bypass (src/routes/pets.ts, reconciled 20-test file)
- GRO-2033 idempotent migrations 0039/0040

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-01 20:10:51 +00:00
The Dogfather 23484dc90a promote(uat): GRO-2014 profile-summary error-handling fix (dev→uat) (#138)
CI / Test (push) Successful in 10s
CI / Lint & Typecheck (push) Successful in 16s
CI / Build & Push Docker Images (push) Successful in 39s
2026-06-01 18:27:42 +00:00
Scrubs McBarkley 766728865e Merge pull request 'promote: uat → main — pnpm-offline Docker hardening + accumulated UAT fixes (GRO-1985)' (#136) from uat into main
CI / Test (push) Successful in 12s
CI / Lint & Typecheck (push) Successful in 14s
CI / Build & Push Docker Images (push) Successful in 1m19s
promote: uat → main — pnpm-offline Docker hardening + accumulated UAT fixes (GRO-1985)

UAT PASS: GRO-2015
Security PASS: GRO-2024 (Barkley Trimsworth)
UAT CI: run #2313 — 3/3 jobs green (incl. offline pnpm smoke tests)
2026-06-01 18:07:30 +00:00
The Dogfather 6a81a52a50 Merge pull request 'Promote dev → uat: UAT seed-password source-of-truth playbook (GRO-2000)' (#134) from dev into uat
CI / Test (push) Successful in 12s
CI / Lint & Typecheck (push) Successful in 15s
CI / Build & Push Docker Images (push) Successful in 27s
CI / Test (pull_request) Successful in 11s
CI / Lint & Typecheck (pull_request) Successful in 13s
CI / Build & Push Docker Images (pull_request) Successful in 1m10s
2026-06-01 17:41:47 +00:00
The Dogfather 5a4b9a98bd Merge pull request 'promote(docker): bake pnpm via npm to remove Corepack runtime downloads (GRO-1981)' (#133) from dev into uat
CI / Test (push) Successful in 12s
CI / Lint & Typecheck (push) Successful in 14s
CI / Build & Push Docker Images (push) Successful in 40s
Promote GRO-1985 (parent GRO-1981) dev->uat. cc @cpfarhood
2026-06-01 16:30:54 +00:00
The Dogfather f7f88156e1 Merge pull request 'promote(db): register extra_large via migration 0038 to UAT (GRO-2004)' (#131) from dev into uat
CI / Test (push) Successful in 11s
CI / Lint & Typecheck (push) Successful in 15s
CI / Build & Push Docker Images (push) Successful in 35s
2026-06-01 14:52:13 +00:00
The Dogfather 8af5a49d14 Merge pull request 'Promote dev→uat: GRO-1982 pet_size_category extra_large enum migration' (#126) from dev into uat
CI / Test (push) Successful in 13s
CI / Lint & Typecheck (push) Successful in 16s
CI / Build & Push Docker Images (push) Successful in 37s
Promote dev→uat: GRO-1983 seed-job pnpm fix + GRO-1982 extra_large enum migration

Carries the accumulated dev state into uat (PR #125 docker pnpm fix + 0037 migration).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-01 12:44:20 +00:00
Scrubs McBarkley 403634eb96 Merge pull request 'promote: uat → main (GRO-1757 SSO auto-provision fix)' (#89) from uat into main
CI / Lint & Typecheck (push) Successful in 9s
CI / Test (push) Successful in 9s
CI / Build & Push Docker Images (push) Successful in 50s
2026-05-26 02:15:57 +00:00
The Dogfather 152abfc4d5 fix(ci): remove duplicate provenance keys causing YAML parse error
CI / Test (push) Successful in 9s
CI / Lint & Typecheck (push) Successful in 12s
CI / Build & Push Docker Images (push) Successful in 1m10s
Duplicate 'provenance: false' in each docker/build-push-action step caused
Gitea to reject the workflow file, breaking push CI and workflow_dispatch.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-26 01:26:05 +00:00
Flea Flicker c8bbb12edb Merge pull request 'promote(dev→main): GRO-1752 schema fix for UAT (CI trigger)' (#82) from dev into main 2026-05-25 23:28:27 +00:00
Flea Flicker ba95088653 Merge pull request 'chore: trigger CI from uat for GRO-1754' (#81) from fix/gro-1754-uat-ci into main 2026-05-25 23:23:15 +00:00
Flea Flicker dd83f29736 chore: trigger CI from uat for GRO-1754 2026-05-25 23:22:04 +00:00
Chris Farhood 185fce8e17 Add .mcp.json
CI / Lint & Typecheck (push) Successful in 15s
CI / Test (push) Successful in 13s
CI / Build & Push Docker Images (push) Successful in 2m44s
2026-05-24 18:14:57 +00:00
Scrubs McBarkley 081379c189 Merge pull request 'promote: uat → main (GRO-1509 OIDC accountLinking fix)' (#46) from uat into main
CI / Test (push) Successful in 9s
CI / Lint & Typecheck (push) Successful in 12s
CI / Build & Push Docker Images (push) Successful in 50s
Merge uat → main: GRO-1509 OIDC accountLinking fix

Sign-offs cleared:
- QA: GRO-1510 ✓
- UAT: GRO-1515 ✓
- Security: GRO-1516 ✓
- Infra: groombook/infra PR #413
2026-05-22 14:03:43 +00:00
The Dogfather e01c12a316 Merge pull request 'chore: migrate .github/workflows to .gitea/workflows' (#22) from gitea/migrate-workflows into main
CI / Lint & Typecheck (push) Successful in 15s
CI / Test (push) Successful in 20s
CI / Build & Push Docker Image (push) Failing after 1m47s
chore: migrate .github/workflows to .gitea/workflows

Migrate CI workflow from GitHub Actions to Gitea Actions.
- Registry: ghcr.io → git.farh.net
- Auth: secrets.GITHUB_TOKEN → gitea.token
- Cache: type=gha → type=registry

Part of GRO-1315.
2026-05-20 01:34:04 +00:00
3 changed files with 46 additions and 123 deletions
+1 -1
View File
@@ -21,7 +21,7 @@
"wait-for-db": "node ./scripts/wait-for-db.mjs",
"migrate": "node ./scripts/wait-for-db.mjs && drizzle-kit migrate",
"seed": "node ./scripts/wait-for-db.mjs && tsx src/seed.ts",
"reset": "node ./scripts/wait-for-db.mjs && tsx src/reset.ts",
"reset": "node ./scripts/wait-for-db.mjs && tsx src/reset.ts && drizzle-kit migrate && tsx src/seed.ts",
"studio": "drizzle-kit studio",
"typecheck": "tsc --noEmit"
},
+39 -114
View File
@@ -1,52 +1,13 @@
/**
* reset.ts — Drop all application tables, re-run migrations, and re-seed.
* reset.ts — Drop all application tables and re-run migrations + seed.
*
* Intended for local development only. Never run against production.
*
* Usage:
* DATABASE_URL=postgres://... npx tsx packages/db/src/reset.ts
*
* GRO-2139: the entire drop→migrate→seed chain runs inside a single
* Postgres advisory lock (SEED_ADVISORY_LOCK_KEY) so a concurrent
* `seed.ts` (e.g. the dev `seed-test-data-*` Job being recreated at
* the top of the hour) cannot interleave between `reset.ts` (DROP)
* and `seed.ts` (TRUNCATE+insert) and collide on `invoices_pkey`.
*
* Why this matters: `seed.ts` derives every primary key from a single
* shared Mulberry32 PRNG seeded with 42 (see `createPrng(42)` and
* `uuid()` in seed.ts). Two concurrent same-profile seeders therefore
* emit *identical* ids for the same logical row, and any moment
* between a concurrent `seed.ts` TRUNCATE and INSERT is exactly the
* window in which the second seeder's INSERT can hit a pkey already
* taken by the first. Pre-GRO-2123 this raced unconditionally;
* GRO-2123 added the advisory lock around `runSeedBody` but left
* `reset.ts` and `drizzle-kit migrate` outside the lock. This script
* now wraps the *whole* chain in the same lock: `withSeedAdvisoryLock`
* pins the lock to one reserved session and the DROP → migrate → seed
* work runs on the rest of the pool, so the lock guarantees mutual
* exclusion against any concurrent seeder for the entire chain.
*
* See: groombook/infra `apps/base/reset-cronjob.yaml` (CronJob) and
* `apps/base/seed-job.yaml` (one-shot Job) — both invoke the same
* `seed.ts` code path on the same database in `groombook-dev`.
*/
import postgres from "postgres";
import { drizzle } from "drizzle-orm/postgres-js";
import { migrate } from "drizzle-orm/postgres-js/migrator";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";
import * as schema from "./schema.js";
import {
SEED_ADVISORY_LOCK_KEY,
withSeedAdvisoryLock,
getProfile,
runSeedBody,
profiles,
} from "./seed.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const MIGRATIONS_FOLDER = resolve(__dirname, "../migrations");
import postgres from "postgres";
async function reset() {
const url = process.env.DATABASE_URL;
@@ -55,88 +16,52 @@ async function reset() {
process.exit(1);
}
if (
process.env.NODE_ENV === "production" &&
process.env.ALLOW_RESET !== "true"
) {
console.error(
"[FATAL] db:reset must not be run in production without ALLOW_RESET=true.",
);
if (process.env.NODE_ENV === "production" && process.env.ALLOW_RESET !== "true") {
console.error("[FATAL] db:reset must not be run in production without ALLOW_RESET=true.");
process.exit(1);
}
// Pool sizing is load-bearing here. `withSeedAdvisoryLock` does
// `pool.reserve()` to pin the advisory lock to one dedicated session
// (a session-level lock released on a *different* pooled connection is
// a no-op), and the DROP / migrate / seed work then runs on the
// *remaining* pooled connections. The lock provides mutual exclusion
// across processes regardless of how many connections the work uses —
// it does NOT require the work to share the lock's session.
//
// Therefore `max` must be ≥ 2: 1 reserved for the lock + ≥1 free for
// the work. `max: 1` would let `reserve()` consume the only connection
// and every query inside the callback would block forever waiting for
// a connection that never frees (connection-starvation deadlock). We
// use `max: 6` to match `seed()`'s headroom (1 reserved + 5 work).
const client = postgres(url, { max: 6 });
const db = drizzle(client, { schema });
const client = postgres(url, { max: 1 });
try {
await withSeedAdvisoryLock(client, async () => {
console.log("Dropping all application tables...\n");
console.log("Dropping all application tables...\n");
// Drop dependencies (tables) first
await client`
DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (
SELECT tablename FROM pg_tables
WHERE schemaname = 'public'
) LOOP
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(r.tablename) || ' CASCADE';
END LOOP;
END $$;
`;
// Drop in dependency order (children before parents)
await client`
DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (
SELECT tablename FROM pg_tables
WHERE schemaname = 'public'
) LOOP
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(r.tablename) || ' CASCADE';
END LOOP;
END $$;
`;
// Drop custom enums
await client`
DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (
SELECT typname FROM pg_type
WHERE typtype = 'e' AND typnamespace = (
SELECT oid FROM pg_namespace WHERE nspname = 'public'
)
) LOOP
EXECUTE 'DROP TYPE IF EXISTS ' || quote_ident(r.typname) || ' CASCADE';
END LOOP;
END $$;
`;
// Drop custom enums
await client`
DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (
SELECT typname FROM pg_type
WHERE typtype = 'e' AND typnamespace = (
SELECT oid FROM pg_namespace WHERE nspname = 'public'
)
) LOOP
EXECUTE 'DROP TYPE IF EXISTS ' || quote_ident(r.typname) || ' CASCADE';
END LOOP;
END $$;
`;
// Drop the drizzle migrations tracking table
await client`DROP TABLE IF EXISTS drizzle.__drizzle_migrations CASCADE`;
await client`DROP SCHEMA IF EXISTS drizzle CASCADE`;
// Drop the drizzle migrations tracking table
await client`DROP TABLE IF EXISTS drizzle.__drizzle_migrations CASCADE`;
await client`DROP SCHEMA IF EXISTS drizzle CASCADE`;
console.log("✓ All tables and enums dropped\n");
console.log("✓ All tables and enums dropped\n");
console.log("Running migrations...");
await migrate(db, { migrationsFolder: MIGRATIONS_FOLDER });
console.log("✓ Migrations applied\n");
console.log("Seeding database...");
const profile = getProfile();
const cfg = profiles[profile];
await runSeedBody(client, db, profile, cfg);
});
console.log(
`\n✓ Reset complete (advisory lock key=0x${SEED_ADVISORY_LOCK_KEY.toString(16)})`,
);
} finally {
await client.end();
}
await client.end();
}
reset().catch((err) => {
+6 -8
View File
@@ -24,9 +24,9 @@ import type { MedicalAlert } from "@groombook/types";
// ── Seed profile configuration ─────────────────────────────────────────────
export type SeedProfile = "dev" | "uat" | "demo";
type SeedProfile = "dev" | "uat" | "demo";
export interface ProfileConfig {
interface ProfileConfig {
staffCount: { manager: number; receptionist: number; groomer: number; bather: number };
clientCount: number;
appointmentsBackDays: number;
@@ -35,7 +35,7 @@ export interface ProfileConfig {
includeUatClients: boolean;
}
export const profiles: Record<SeedProfile, ProfileConfig> = {
const profiles: Record<SeedProfile, ProfileConfig> = {
dev: {
staffCount: { manager: 1, receptionist: 1, groomer: 2, bather: 0 },
clientCount: 100,
@@ -70,8 +70,6 @@ function getProfile(): SeedProfile {
return "uat";
}
export { getProfile };
// ── Deterministic PRNG (Mulberry32) ──────────────────────────────────────────
/**
@@ -1402,7 +1400,7 @@ async function seedKnownUsers() {
// from runbooks without ambiguity and binds to the single-argument
// `pg_advisory_lock(int)` form, which postgres-js serializes as a plain
// number (no bigint type plumbing required).
export const SEED_ADVISORY_LOCK_KEY = 0x47524f4f; // "GROO" in ASCII — arbitrary, stable
const SEED_ADVISORY_LOCK_KEY = 0x47524f4f; // "GROO" in ASCII — arbitrary, stable
/**
* Reserve a dedicated connection from `pool`, take the seed advisory lock
@@ -1415,7 +1413,7 @@ export const SEED_ADVISORY_LOCK_KEY = 0x47524f4f; // "GROO" in ASCII — arbitra
* for the lock and release it from the same reserved connection. The
* seed work itself still runs on the pooled connections.
*/
export async function withSeedAdvisoryLock<T>(
async function withSeedAdvisoryLock<T>(
pool: ReturnType<typeof postgres>,
fn: () => Promise<T>,
): Promise<T> {
@@ -1473,7 +1471,7 @@ async function seed() {
await client.end();
}
export async function runSeedBody(
async function runSeedBody(
client: ReturnType<typeof postgres>,
db: ReturnType<typeof drizzle>,
profile: SeedProfile,