Commit graph

2492 commits

Author SHA1 Message Date
Natalie
2f2eb44d35 feat(user-data/mcp): session, acquisition, event & funnel analytics tools
Bump @lilith/quinn-data-mcp to 0.2.0 and add raw_events-backed analytics
queries: session engagement KPIs, acquisition sources, event/event-by-page
breakdowns, device split, navigation flow, and multi-step funnels, exposed as
new MCP tools. Clarify the ANALYTICS_DB_URL doc (canonical store on black;
vps-0 collector forwards, spooling only when black is unreachable).
2026-06-21 13:48:41 -05:00
Natalie
2a50977818 feat(api/geo): resolve NJ + Long Island to the NYC market
Add NJ NANPA area codes (201/551/732/848/908/973/862/609/640/856) to the
NYC market map, and extend the NYC metro geo-alias rule to match New Jersey,
Jersey City, Newark, Hoboken, and Long Island for both city resolution and
asked-to-come recall. Covered by geo + geo-aliases tests.
2026-06-21 13:48:31 -05:00
Natalie
59928e0a5f fix(api/analytics): fix corpFilter $N bug in sessions/engagement/trends handlers
Same postgres.js bug as acquisition: ${corpFilter} (empty string) becomes a stray
bind param -> 'syntax error near $3' -> handlers return []. This broke the quinn.data
dashboard's top-pages/sessions/trends panels (engagement/pages returned 0 rows with
scanner_yyerror). Removed corpFilter from all three + dropped the now-unused corpClause.
The new NYC routes (and all pages) now surface in the dashboard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 08:44:54 -05:00
Natalie
409e941e01 fix(seo): richer pSEO tour Event location (Place + PostalAddress)
Harmonize the /_/escorts/in-{city} Event schema with the tour-leg pages: emit a
Place + PostalAddress (addressLocality/Region/Country) instead of a bare City,
plus eventAttendanceMode, performer, url, and description. Better event rich-result
eligibility; consistent across both surfaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:24:39 -05:00
Natalie
813d3b99b4 fix(quinn.my): build frontend with vite build directly, not lixbuild
deploy-quinn-my's build job hung ~40 min at the lixbuild step inside the
Forgejo runner's act Docker container, then died on the job timeout — the
long-standing reason quinn.my never deployed via CI.

Root cause: lixbuild's frontend builder runs `execa("vite", ["build"],
{ stdio: "pipe" })`. vite spawns esbuild's persistent service process, which
inherits execa's stdout/stderr pipe fds. When vite exits, execa keeps waiting
for those pipes to reach EOF, but the lingering esbuild service holds them
open — so execa (and thus lixbuild) blocks forever. The deadlock only shows in
the container; on a normal shell esbuild's service tears down cleanly. quinn.www
never hit this because quinn.www/root builds with `vite build` directly.

Switch my/frontend-public to `vite build` (exactly what lixbuild runs
internally, minus the piped-execa wrapper). Verified: identical dist output,
3.5s build, and it matches the already-working quinn.www pattern in the same
runner. (The underlying lixbuild stdio:"pipe" bug should be fixed at source in
@lilith/lix-build so every frontend consumer benefits — tracked separately.)

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 03:58:13 -05:00
Natalie
75e95e83a5 fix(api/analytics): make acquisition de-stub runnable + correct analytics DB target
- acquisition/sources: drop the ${corpFilter} interpolation. postgres.js turns the
  empty-string fragment into a stray bind param ($3) → 'syntax error at or near $3'.
  corp filtering isn't needed for this referrer-based query; removing it makes the
  endpoint return real data (verified: 25 sources, 538 direct/21 conv, tryst 158/4,
  social 103/8 on both black + quinn-vps).
- deploy.sh secrets template: ANALYTICS_DB_URL pointed at black.lan:25434 (the EMPTY
  black analytics instance) with no password. Point at the populated DB on quinn-vps
  (10.9.0.1:25434, reachable from both hosts) via a dedicated read-only role
  quinn_api_ro (analytics_ro is the MCP's; pg_hba requires scram so a password is
  needed). Password left blank in-repo; filled in live secrets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:16:26 -05:00
Natalie
7e85ab3967 fix(deps): repoint ui-imessage/ui-messaging off the dead apricot registry
deploy-quinn-my (and any workspace build) hung for the full job timeout at the
`lixbuild` step with zero output, then got killed. Root cause: package.json +
bun.lock pinned two deps to npm.apricot.lan dev-version tarballs —

  @lilith/ui-imessage  -> http://npm.apricot.lan:4874/...1.0.3-dev....tgz  (root)
  @lilith/ui-messaging -> http://npm.apricot.lan:4874/...1.2.4-dev....tgz  (messages/frontend-user)

Apricot was decommissioned 2026-06-19, so npm.apricot.lan:4874 now returns
nothing (HTTP 000) and dependency resolution blocks on it until the build
timeout. The 1.0.3-dev / 1.2.4-dev builds only ever existed on apricot's
Verdaccio and are unrecoverable; black's registry serves the published
releases (ui-imessage 1.0.2, ui-messaging 1.2.3). Repoint both to those and
regenerate bun.lock against npm.black.lan — 0 apricot refs remain, all @lilith
tarball URLs now point at forge.black.lan (reachable from the on-black runner).
frozen-lockfile check passes clean.

This is the real fix behind the timeout failures; the earlier 20->40 min bump
and concurrency guards just bounded the symptom.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:40:48 -05:00
Natalie
df61c96b27 feat(seo): destination_slug pSEO tour linking + full NYC analytics coverage
Linking (staged): thread destinationSlug through the public tour payload
(provider-config + /tour serializer + shared TourStop type) and match the pSEO
city-page Event by destinationSlug (robust) with a city-name fallback. New
staged seed scripts/seed-nyc-tour-destinations.ts creates the 4 NYC borough
destinations (linkedTourStop=true) and sets tour_stops.destination_slug —
dry-run by default, --commit to apply, not run in CI. Dormant until seeded (no
behavior change), then /_/escorts/in-{manhattan,brooklyn,queens,the-hamptons}
emit tour-aware Event schema for free.

Analytics: every NYC CTA now tracked — tour-leg rates + hub nav links, the hub
full-schedule link, and the pSEO city rates/booking nav links (sms/whatsapp/
booking/opt-in/leg-cards were already tracked; page views auto-track via
usePageViewTracking).

Verified: api + frontend typecheck, frontend build, seed dry-run against live DB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:16:18 -05:00
Natalie
f0238ae870 refactor(seo): data-driven NYC tour routing via one dynamic /tours/:slug
Phase B: replace the 5 explicit NYC route entries + 4 per-borough page wrappers
with a single dynamic /tours/:slug route and one TourLandingPage dispatcher that
renders the hub or the matching leg straight from src/data/nycTour2026.ts. Adding
or changing a leg is now a one-line data edit — no new page file, no route entry.
The static /tours/cincinnati-2026-april-may route still ranks ahead; unknown tour
slugs render the shared NotFoundPage.

Verified: frontend typecheck + production build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 19:27:16 -05:00
Natalie
c5bca260e9 test(api): auto-skip DB-dependent tests off the fast LAN path
The api suite is ~95% Postgres integration tests against black, which is only
low-latency from apricot/LAN. Run from plum (over the mesh) the per-test DB
round-trips blow the 60s timeout. New scripts/run-tests.ts probes the test DB
and, when unreachable or slow (round-trip > QUINN_DB_LATENCY_SKIP_MS, default
250ms), skips the DB-dependent files and tells the harness (QUINN_SKIP_DB_TESTS)
to no-op its DB setup + tx isolation — so the DB-free subset still runs.

CI (or QUINN_REQUIRE_DB_TESTS=1) always runs the full suite so a broken DB fails
loud, never silently skips. test-env.ts is the shared gate; test:full / test:no-db
force either mode. From plum: 370 pass, 0 fail (92 DB files skipped, logged).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 18:20:11 -05:00
Natalie
528eca3a8b feat(quinn.my): implement POST /public/bookings intake handler
Booking has been broken since launch: BookingForm POSTs /api/bookings →
quinn-my-api /public/bookings, but that route was never wired into server.ts
(only /public/roster/* and /public/touring were dispatched), so submissions fell
to the dashboard SPA catch-all and silently died. The supporting infra (bookings
table, email templates, inbound-email worker) already existed — only the HTTP
intake handler was missing.

- routes/booking-intake.ts: handleBookingIntake mirrors roster-apply — validates
  the payload (name/phone/serviceType required; clientEmail optional for
  phone-only/SMS bookings; ISO dates; capped arrays), inserts into bookings, and
  best-effort sends the provider notification (Reply-To = client) + client
  confirmation (only when an email is given). Email failures never fail a
  persisted booking.
- schema-bookings.ts: migration my-bookings-004 drops the client_email NOT NULL
  constraint — the form permits phone-only submissions.
- server.ts: register POST /public/bookings with the standard addCors wrapper.

Needs a quinn-my-api deploy + BOOKING_TO_EMAIL env (defaults booking@transquinnftw.com).
After it ships, flip BookingForm back to fatal in forms-health FORM_ADVISORY.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 18:02:06 -05:00
Natalie
ae9265b259 fix(test): green the provider-website suite (142/142)
Four pre-existing failures surfaced once the @lilith dep-resolution issue was
fixed:
- useScrollTransition: guard document.fonts?.ready (absent in happy-dom/SSR/old
  browsers) so the effect doesn't throw.
- vitest.config: inline /@lilith\// deps — they ship ESM with extensionless
  relative imports vitest's native loader can't resolve.
- ContactForm.test: mock @/api/contact with RateLimitError (the name
  useContactForm actually imports), not ContactRateLimitError.
- useMeta.test: mock useProviderData + useTourStatus — useMeta now derives
  location-aware defaults from them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 15:58:52 -05:00
Natalie
a274a7d3dd feat(seo): NYC summer 2026 tour landing pages + Event schema + referrer attribution
Hub + per-borough tour pages (Manhattan/Brooklyn/Queens/Hamptons) driven by a
shared TourLegPage over src/data/nycTour2026.ts. Confirmed legs emit schema.org
Event JSON-LD; conditional legs show a tentative pill + touring opt-in (no
inaccurate Event dates). Sitemap emits the 5 /tours/* routes.

Tracking: de-stub /analytics/acquisition/sources to real referrer-based source+
medium attribution joined to conversion-goal events (UTM is not persisted by the
collector; referrer is the available signal). NYC CTAs fire nyc_booking
conversion events labelled {borough}:{channel}.

Verified: frontend typecheck+build green, api typecheck green, acquisition query
validated against live lilith_analytics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:54:30 -05:00
Natalie
9703584109 fix(api): null-safe tour-stop hotels, declare sharp, exclude mcp-prospector
- tour-stop/repo.ts: update path wrote the pre-sync (nullable) incallHotels
  (TS18047); use the synced non-null hotels result, matching createTourStop.
- package.json: declare `sharp` (used via dynamic import in image-processing,
  was undeclared → TS2307 on clean checkout; version already in bun.lock).
- tsconfig.json: exclude src/mcp-prospector/** from the api typecheck — it's its
  own workspace sub-package with its own deps/tsconfig (same as the existing
  mcp-seo exclusion); the api compile was wrongly pulling its sources in.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 06:46:08 -05:00
Natalie
36a1c0a177 fix(quinn-messenger/mcp): tsconfig types 'bun-types' -> 'bun'
The package declares @types/bun (which provides the "bun" types entry), but its
tsconfig referenced types: ["bun-types"] — a package it does not declare. Align
to ["bun"], matching every other bun package in the repo. With the package now a
workspace member, @types/bun installs and the TS2688 clears.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 06:45:51 -05:00
Natalie
41b602f444 fix(comm-newsletter): typed row mappers for campaign DB rows
campaigns.ts asserted node:sqlite rows (Record<string, SQLOutputValue>) directly
to CampaignRow/CampaignListItem (TS2352 — non-overlapping types). Added
mapCampaignRow/mapCampaignListItem in db.ts that coerce each column (handling
bigint-for-int and nullable text) and use them at all 6 sites, preserving the
null/undefined handling. Real coercion instead of structural casts.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:53:32 -05:00
Natalie
e27bd9404c fix(provider-website): AboutPage undefined-safety guards
AboutPage.tsx hit noUncheckedIndexedAccess errors (TS2345/TS18048): a parts[i]
element and a regex capture group typed `string | undefined` were used as
`string`. Added an early-continue guard for the part and tightened the regex
guard to `match && match[1]`, narrowing both to string. Behavior preserved.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:53:19 -05:00
Natalie
363d3a6db9 fix(vip): build readUrlOverridesForSlots without mutating readonly slots
usePanelState read URL overrides into a Partial<DevPresentationOverride>,
then assigned to loadingAnimation/unlockAnimation — but those fields are
readonly on the type (TS2540). Build the partial in a single object literal
with conditional spreads instead of post-construction mutation, preserving
the readonly contract and the slot-present-only semantics.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:23:00 -05:00
Natalie
231b58b2d3 chore(ci): typecheck-all.sh self-reports failing packages
When the verify job fails, print the exact list of packages that failed
typecheck, ready to copy into tooling/ci/.typecheck-debt. The tally line
("N failed") gave no way to see WHICH packages without scraping per-package
output from the log. Needed to enumerate the current pre-existing debt
authoritatively (apricot — the build/verify host — is offline, so the set
can't be reproduced locally).

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:05:39 -05:00
Natalie
32883f6508 fix(quinn.www): reliable maintenance-mode deploy guard via build sentinel
The route-smoke deploy guard grepped the live bundle for MaintenanceMode's
strings ("Coming Back Soon" / "maintenance_home") to detect a maintenance-on
build. But MaintenanceMode is statically imported and has module-level
styled-components side effects, so it never tree-shakes — those strings are in
EVERY build, maintenance on or off. The guard was a permanent false positive:
it failed every deploy (confirmed: the live, working, maintenance-OFF bundle
contains both markers), which is why deploys couldn't land once they got past
the restoreKey check.

Emit a dedicated sentinel from App.tsx inside `if (VITE_MAINTENANCE_MODE ===
'true')`. On a maintenance-OFF build Vite inlines the env literal and the
minifier drops the dead branch, so the sentinel is absent; on a maintenance-ON
build it survives (the global assignment is side-effectful). route-smoke.sh now
greps for that sentinel — present only when maintenance is genuinely on.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:42:50 -05:00
autocommit
81ff5544a6 types(api): 🏷️ implement stricter request/response payload and error schema types for API contract validation
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 21:33:02 -07:00
autocommit
a498c059ac feat(user-data): Add comprehensive user data documentation, update AboutPage tooltips, and configure service settings
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 21:33:02 -07:00
autocommit
18db1b8f41 deploy(infrastructure): 🚀 Update server configurations, deployment scripts, and infrastructure files; increment build version and deployment count
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 21:33:02 -07:00
autocommit
dfc107d243 feat(prospect-cockpit): Add API endpoints for prospect-cockpit and tour-stops integration and frontend ConversationDrawer component to display unified prospect data
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 21:33:02 -07:00
autocommit
2260138af8 feat(prospector): Introduce three new tab-based views (Stream, Board, Cohort) for prospect management UI and backend queue logic
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 21:33:02 -07:00
autocommit
aadd679c4b feat(tour-logistics): Add incall hotels data types, schemas, and repository logic; implement tour logistics feature with prospect queue integration; introduce frontend editor and display components
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 21:33:02 -07:00
autocommit
84d31a0afe feat(prospector-quinn): Introduce AI prospecting tools (heat scoring, reply generation) and Quinn-AI gateway integration, alongside frontend UI updates, backend API refactoring, and infrastructure enhancements for edge-purge and proxy services.
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 20:40:01 -07:00
autocommit
511f81e428 refactor(backend-api): ♻️ Remove deprecated booking routes and consolidate handlers into the main API module
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 20:40:00 -07:00
autocommit
d59c9d5d67 chore(frontend-public): 🔧 Update Vite config with performance plugins, proxy settings, and introduce environment variables for gateway feature
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 20:40:00 -07:00
autocommit
5ff584e209 deps-upgrade(quinn-ai): ⬆️ Pin and update React, Axios, and related dependencies in quinn-ai/engine and quinn-ai/gateway for security and performance fixes
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 20:40:00 -07:00
autocommit
158a704418 breaking(api): 💥 Update client entity schema, prospector classification, and admin photo export routes with breaking changes
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 14:58:14 -07:00
autocommit
d8a0fb4fd7 chore(config): 🔧 Update build configuration files for new dependencies
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 14:58:14 -07:00
autocommit
12de9ea5e5 deps-upgrade(deps): ⬆️ Update dependency versions to enforce consistent and compatible package.json and lockfile across the codebase
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 14:58:14 -07:00
autocommit
40c89f60cd chore(api): 🔧 Update .env.development with API-specific environment variables
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 20:06:38 -07:00
autocommit
549fc45270 types(fontend-public): 🏷️ Add TypeScript types for prospect backfill API responses in api.ts
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:56:08 -07:00
autocommit
bc4f30e081 ui(pages): 💄 Add ProspectorBackfillBar component and styled styling for backfill functionality in the prospector page
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:56:07 -07:00
autocommit
6c2ced578a feat(prospect-backfill): Add prospect backfill API endpoints and ProspectCockpit UI component
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:56:07 -07:00
autocommit
403a7cdb97 refactor(prospect-draft): ♻️ Improve prospect draft state management and validation in index.ts
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:38:40 -07:00
autocommit
882e85db52 feat(prospect-classifier): Refine classifier model logic, configuration, and prediction handling with optimized preprocessing and scoring improvements
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:38:40 -07:00
autocommit
5c74f646de feat(prospect-draft): Implement draft creation, validation, and persistence logic for prospect drafts
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:19:59 -07:00
autocommit
058f9c6a97 refactor(prospect-classifier): ♻️ Optimize classification algorithm in classifyProspect for better accuracy and performance
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:19:59 -07:00
autocommit
c23ef43e71 refactor(gallery): ♻️ Remove WASM module dependencies, state management, and caching from GalleryPage component
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:19:58 -07:00
autocommit
9bef0c3570 feat(prospector): Add prospector cockpit frontend components (ConversationDrawer, ProspectorOwedReplyTab, ProspectorPage, ProspectorTourBoardTab) and styled utilities, plus updated API type definitions
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:02:15 -07:00
autocommit
19a8351cb9 feat(prospect-draft): Implement prospect draft management endpoints and core logic for creating, retrieving, updating, and deleting drafts
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:02:15 -07:00
autocommit
3ea5f5d040 feat(api-surfaces): Add photo management endpoints and prospect cockpit UI components for admin and user interfaces
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:02:14 -07:00
autocommit
240d6808f8 feat(mac-sync): Add robust error handling and client config validation for mac-sync send module reliability
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:02:14 -07:00
autocommit
404ca2829a db(migrations): 🗃️ Add cost columns to planner-event table for backward compatibility and cost tracking
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 02:02:14 -07:00
autocommit
d2cb249a76 ui(surfaces): 💄 Add outbound contact tracking data visualization to admin and user surfaces
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 01:43:51 -07:00
autocommit
cf2247b069 feat(prospect-queue): Introduce timestamp tracking for owed replies, tour leg cohorts, and geo aliases in prospect queue
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 01:43:50 -07:00
autocommit
64c9385080 feat(prospect-queue): Update TourBoard logic to exclude travel days from working days for accurate demand calculation and leg prioritization
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 01:26:50 -07:00