Risk-aware micro-itineraries for airport layovers, built with React, Express, Gemini, OpenStreetMap, and Playwright.
LayoverPlus answers a deceptively difficult travel question: is there enough time to leave the airport, enjoy one nearby stop, and return without gambling on the next flight? It turns flight timing, airport-specific buffers, traveler risk tolerance, routing estimates, and nearby places into a timestamped plan with an explainable go/no-go recommendation.
Try the live portfolio demo — no account, API key, or paid service required. The demo uses representative route data and runs the deterministic safety engine entirely in the browser.
Portfolio status: functional full-stack prototype with deterministic safety logic, graceful third-party fallbacks, contract tests, browser-level interaction coverage, an interactive recruiter demo, and automated CI. It is not production flight-operations guidance.
The GitHub Pages experience is a focused, zero-cost version of the product built for quick evaluation. Change the airport, layover duration, connection type, risk tolerance, or ranked destination and the safety call, score, and timestamped itinerary update together.
- A hard feasibility rule prevents a destination from receiving a
GOresult when its required time exceeds the layover. - Every recommendation exposes its processing, travel, experience, and protected-return assumptions.
- A two-hour layover demonstrates the explicit
STAY AIRSIDEfailure state instead of forcing a recommendation. - The Pages build isolates the recruiter demo from the full React/Express application, so it ships only the zero-key interactive case study; authentication, provider integrations, and the saved-plan experience remain available in this repository.
| Ranked, explainable destinations | Synchronized selected route |
|---|---|
![]() |
![]() |
- Safety logic stays deterministic. Gemini can rank feasible candidates and improve wording, but it cannot override processing time, return buffers, feasibility math, or risk labels. Fast boundary tests prove that an exact-fit itinerary is accepted, a one-minute overrun is rejected, and risk labels change only as protected slack increases. Mocked-provider tests cover infeasible picks, malformed responses, and unsupported numeric or categorical safety claims; generated prose is not a substitute for verifying real-world conditions.
- The AI boundary has a repeatable offline evaluation set. Versioned synthetic responses exercise feasible and infeasible picks, schedule tampering, unsupported claims, malformed JSON, and provider failures without an API key. The result is a contract-test outcome, not an estimate of model accuracy or traveler safety; factual prose still needs human review.
- The product degrades gracefully. If Gemini is unavailable or exceeds a bounded request deadline, the API returns deterministic narrative guidance. If live POI or route services fail, curated destinations and conservative distance estimates keep the planning flow usable.
- Choices remain synchronized. Selecting another destination replans the recommendation, timeline, and map as one state transition; Playwright verifies that behavior end to end.
- The API exposes its reasoning. Responses include score components, effective buffers, selection source, AI metadata, latency, and map-service runtime counters.
- The service includes practical guardrails. Zod validation, Helmet, rate limiting, JWT authentication, password hashing, bounded in-memory stores, retries, and caches cover the prototype's main failure modes.
- Enter the airport and layover window.
- Select connection type, interests, and risk tolerance.
- Review three ranked destinations and the timing tradeoffs for each.
- Apply a destination to update the recommendation, timestamped itinerary, and route map.
- Optionally use Gemini and place-provider keys for richer ranking, language, photos, ratings, and reviews.
Supported airports: SFO, LAX, and JFK.
| Layer | Responsibility |
|---|---|
| React + Vite PWA | Planner form, candidate comparison, itinerary timeline, map, auth views, and saved-plan experience |
| Express API | Validation, auth, planning orchestration, telemetry, feedback, and place-preview proxying |
| Deterministic planning engine | Airport processing assumptions, return buffers, travel limits, dwell time, feasibility, scoring, and risk classification |
| Gemini | Optional feasible-candidate ranking, constrained itinerary wording, and traveler tips; prose still requires judgment |
| Overpass + OSRM | Live nearby-place discovery and route estimates, protected by retry, cache, and fallback behavior |
| Playwright | API contracts plus the browser-level destination choice synchronization test |
The backend computes a structured feasibility result first. AI receives constrained context and may explain or rank feasible stops, but its output is not permitted to change the schedule or timing values. The numeric and safety-phrase checks are bounded safeguards, not a guarantee that every sentence is factually correct or that real-world travel is safe.
- React 19, React Router, Vite, Framer Motion, Leaflet, and PWA support
- Node.js 20.19+ and Express
- Gemini Generative Language API
- OpenStreetMap Overpass and OSRM
- Zod, Helmet, express-rate-limit, JWT, bcrypt, and Pino
- Playwright contract and end-to-end tests
- GitHub Actions CI
Requirements: Node.js 20.19 or newer.
git clone https://github.com/calebponce/Layover-Plus.git
cd Layover-Plus
npm ciFor local development, run the API and frontend in separate terminals:
npm run dev:servernpm run dev:clientOpen http://localhost:5173. For a production-style local run:
npm run build
npm startThe application works without paid API credentials. To enable optional integrations, copy .env.example to .env and add the keys you want:
GEMINI_API_KEY=your_key
GEMINI_MODEL=gemini-2.0-flash
GEMINI_REQUEST_TIMEOUT_MS=8000
GOOGLE_PLACES_API_KEY=your_optional_key
YELP_API_KEY=your_optional_keynpm run build
npm run test:safety
npm run eval:offline
npm run test:contract
npm run test:e2e
npm audit --omit=devCI runs clean installs, deterministic safety-boundary tests, the versioned offline AI-boundary evaluation, a production build, the production dependency audit, API contract tests, and the end-to-end choice synchronization scenario. Browser tests force the curated POI mode so their result does not depend on live third-party availability. A separate GitHub Pages workflow verifies and deploys the portfolio demo from main.
The offline corpus is evals/v1/fixtures.json. npm run eval:offline runs each synthetic provider response through the real selection or schedule service with a mocked fetch; it fails if an expected boundary behavior changes. Gemini calls use an 8-second request/body deadline by default; GEMINI_REQUEST_TIMEOUT_MS accepts 100–15,000 ms, with invalid values falling back to the default. This does not call Gemini, measure live-model quality, validate real-time travel estimates, or prove all generated claims are grounded. See the human-review queue for claims outside the automated checks.
For actual captured application outputs, the local human-review format validates provenance fields and reports explicit verdict counts without inventing an accuracy rate. It makes no provider calls and ships with no real-output dataset or claimed model-quality result.
| Endpoint | Purpose |
|---|---|
GET /api/health |
Health and API metadata |
GET /api/config |
Supported airports, interests, and options |
POST /api/plan |
Generate or replan a risk-aware itinerary |
POST /api/flight-status |
Simulate gate, delay, and replan signals |
GET /api/place-preview |
Fetch normalized place details and provider links |
GET /api/place-photo |
Proxy Google Place photos without exposing the server key |
POST /api/feedback |
Record a plan rating and comment |
POST /api/event |
Accept best-effort product telemetry |
GET /api/usage |
Return request and planning performance counters |
GET /api/replan-history |
Return recent replan events for a session |
See the API structure for request, response, and error examples.
LayoverPlus began as a three-person San Francisco State University project. The original ownership areas were:
| Contributor | Primary area |
|---|---|
| Caleb Ponce | AI and backend |
| Edson Sanchez Bernal | Frontend and UX |
| Omshree Rajanikant Bharodiya | Data and planning logic |
The repository preserves the full team history and names instead of presenting collaborative work as a solo project. Cross-cutting integration was collaborative.
- Airport timing assumptions are configurable estimates, not live operational guarantees.
- Flight status is simulated; a production version should integrate an authoritative provider.
- In-memory auth, telemetry, and saved data are prototype storage and reset on restart.
- Only three airports have curated configuration today.
- Live provider quality and quotas vary, so fallbacks intentionally favor continuity over freshness.
Released under the MIT License.


