Skip to content
All work
LiveProduction Prototype2025 - 2026

HotelMind AI

A hotel operations platform with a predictive layer - and a published analysis of where it falls short.

Five repositories: an event-driven FastAPI backend, a Next.js operations dashboard, a machine-learning service behind five prediction problems, an Airflow and dbt warehouse, and the Docker infrastructure that runs it. Built solo, deployed, and evaluated as a product rather than demoed as one.

Role
Sole builder - product, analysis, engineering, data, and ML
Team
Solo project
Last updated
August 2026

Disciplines

  • Product Management
  • Business Analysis
  • Software Engineering
  • Data Engineering
  • Machine Learning
  • Generative AI
  • MLOps
  • Cloud & DevOps
The executive dashboard, updated over WebSockets as operational events land.
HotelMind AI operations dashboardThe executive dashboard, updated over WebSockets as operational events land.

System stack

What the system is built with. Personal contribution is itemised per discipline below.

  • Next.js 16
  • React 19
  • TypeScript
  • Tailwind CSS v4
  • FastAPI
  • Python
  • PostgreSQL 16
  • Redis 7
  • Apache Kafka
  • WebSockets
  • SQLAlchemy
  • Alembic
  • Apache Airflow
  • dbt
  • MinIO
  • XGBoost
  • Prophet
  • scikit-learn
  • FAISS
  • BM25
  • MLflow
  • Evidently
  • Prometheus
  • Grafana
  • Docker Compose
  • Nginx
  • Vitest
  • pytest

At a glance

Every figure states its basis. None are projections.

5
repositories in the system

backend, frontend, ml, data, and infra repositories

5 problems / 10 artifacts
trained prediction models

10 .pkl files across 5 problems - restaurant splits by meal period, churn has two candidate algorithms

20
functional requirements documented

FR-01 to FR-20 in the business gap analysis, with status per requirement

~34
capability areas benchmarked

compared against Oracle OPERA, Mews, Cloudbeds, and Apaleo; fewer than 5 rated strong

180
automated tests across the system

19 frontend (Vitest, 10 files, verified passing) + 51 backend + 82 ML + 28 data test functions

Overview

Problem

Hotel operations run on disconnected tools: a PMS for reservations, a spreadsheet for rate decisions, a POS for the restaurant, a separate scheduling tool, review monitoring done by hand, and reporting stitched together at month-end from exports of all of them. The decisions that matter - drop the weekend rate, add a server on Friday, intervene with a guest who is about to leave - get made on gut feel and lagging indicators.

Why I built it

I wanted one project where the whole chain was mine end to end - the problem framing, the requirements, the schema, the models, the infrastructure, and the honest assessment afterwards. Portfolio projects usually stop at a working demo, which hides the part of the job that actually matters: deciding what to build, knowing which numbers to trust, and being able to say precisely where the thing falls short. Hospitality was the right domain because its problems are genuinely cross-functional - pricing is a data problem, staffing is an operations problem, and guest churn is both - so a single system had to span disciplines rather than showcase one.

Who it’s for

Built for the management team of a single property or a small branch group: the owner or general manager who needs a live picture, the revenue manager setting rates, and the operations, restaurant, and guest-experience managers running their departments. It is entirely internal - guests are subjects of the data, not users of the system.

Context

Built as a single-developer system spanning five repositories, then reviewed against the platforms a hotel group would actually compare it to: Oracle OPERA, Mews, Cloudbeds, and Apaleo. The review is published rather than summarised, because its conclusions are unflattering and still worth reading.

Objectives

  • Unify pricing, occupancy, restaurant, staffing, and guest sentiment onto one operational schema.
  • Put a predictive layer under the operational data without letting a model act unsupervised.
  • Keep the dashboard current enough that an operator trusts it during a shift.
  • Document what the system genuinely does, and what it only appears to do.

Scope & stakeholders

In scope

  • Live operational dashboard over a shared schema for one property or branch group
  • Predictive layer: pricing, occupancy, restaurant demand, staffing, and churn
  • Retrieval-augmented assistant over the hotel's own operational data
  • Bookings, restaurant, staffing, housekeeping, maintenance, and guest-experience modules
  • Role-scoped access for five real operational job functions
  • Data warehouse with orchestrated extraction and scheduled model retraining

Deliberately out of scope

  • Property management system replacement - no front-desk check-in or folio close
  • Payment processing - the payments module is an internal ledger with no processor behind it
  • Channel manager and OTA connectivity - a room cannot be sold through HotelMind
  • POS integration - the restaurant module is first-party only
  • Multi-tenant SaaS isolation and multi-property portfolio rollup
  • Guest-facing surfaces - no booking widget, guest portal, or mobile app

The exclusions are deliberate and they are also the reason this is positioned as an analytics overlay rather than a system of record. A hotel would run it alongside an existing PMS, not instead of one.

Stakeholders

Marked by whether the system as built actually serves them.

  • Owner / General Manager

    Served

    A live, cross-departmental picture of the property instead of month-end reports assembled by hand.

  • Revenue Manager

    Served

    Rate decisions backed by a model rather than a spreadsheet and competitor spot-checks, with final say retained.

  • Operations Manager

    Served

    One view of bookings, staffing, and restaurant activity, plus authority over refunds and exceptions.

  • Restaurant Manager

    Partially served

    Demand forecasts and prep planning instead of estimating from yesterday's covers.

  • Guest Experience Manager

    Partially served

    Early warning on unhappy guests rather than discovering a problem in a review weeks later.

  • Finance Manager

    Not served

    Reconciliation and invoicing against real transactions.

  • Front Office Manager

    Not served

    Check-in, room assignment, and a room-status board.

  • Regional / Multi-property Manager

    Not served

    Cross-property comparison and portfolio-level investment decisions.

  • IT Administrator

    Not served

    User provisioning, granular permissions, SSO, and an audit trail to review.

  • Guest

    Not served

    Fair pricing and responsive service. A subject of the data, never a user of the system.

Product Management

Scoped a system that recommends rather than acts, aimed at a specific operator rather than at a category.

Product scope

One operational dashboard over a shared schema, covering pricing, occupancy, restaurant demand, staffing, and guest sentiment for a single property or small branch group. The deliberate exclusion is as important as the inclusion: this is not a PMS, does not take payments, and does not sell inventory. Scoping to an analytics-and-recommendation layer kept the build honest about what one developer could actually finish.

The decision that mattered most

Every model-generated rate change is held behind a guardrail requiring explicit human approval. The model recommends, a revenue manager decides, and the recommendation is logged either way - including when it is rejected. "Will the AI change my prices without asking" is the first objection any AI-pricing product meets, and the answer has to be structural rather than reassuring.

Authorization tied to real roles

Five roles exist because five job functions exist - owner, revenue manager, operations manager, restaurant manager, and guest experience manager - and permissions follow the job rather than a generic admin/user split. Refund approval is restricted to owners and operations managers.

Positioning, revised by the analysis

The build began as an "AI hospitality platform". The gap analysis forced a narrower and more defensible claim: an ops-intelligence layer that runs alongside an existing PMS for an independent or small-group hotel. Pitching it as a PMS replacement would not survive a first vendor call.

My contribution

  • Designed the human-in-the-loop guardrail on AI-driven pricing.

    Model output is a recommendation with an explicit approval gate and a logged decision, never a direct write to live rates.

  • Defined the role model and its authorization boundaries.

    Five operational roles mapped to real job functions, with refunds gated to owner and operations manager.

  • Revised the product positioning based on the capability benchmark.

    Moved from "AI hospitality platform" to an ops-intelligence overlay for independent and small-group hotels.

Current limits

  • No customer discovery: personas and workflows are reasoned from domain research and the data model, not from interviews with a real general manager or revenue manager.
  • No business model defined - no pricing strategy, no go-to-market plan.
  • No multi-property or portfolio rollup, which rules out chain buyers structurally.

Business Analysis

Reverse-engineered a full requirements set from working software, then benchmarked it against the platforms a real buyer would compare it to.

The gap analysis

A pre-launch product evaluation written from the perspective of a reviewer preparing to pitch an enterprise hotel group. It covers the problem space, stakeholder analysis across nineteen roles, business process mapping, functional and non-functional requirements, a capability matrix, a business value matrix, a risk register, and a roadmap. Its conclusion is that fewer than five of roughly thirty-four benchmarked capability areas are strong.

Requirements

Twenty functional requirements (FR-01 to FR-20) each carry a status: implemented, implemented with caveats, or not implemented. Non-functional requirements cover availability, security, compliance, scalability, localisation, disaster recovery, and AI governance - most of which are recorded as unaddressed rather than quietly omitted.

As-Is and To-Be

The guest lifecycle is mapped end to end - booking, reservation, check-in, stay, checkout, review, reporting - with HotelMind's actual coverage marked against it. The mapping makes the structural gap visible: the system picks the process up after a reservation already exists and drops it before money changes hands, which is exactly where the financial risk concentrates.

Business rules and acceptance criteria

Observed rules were captured explicitly: refunds require owner or operations-manager approval, pricing changes require a guardrail check before taking effect, and users are branch-scoped except owners. Acceptance criteria are written in given/when/then form for the pricing workflow - including one criterion the analysis flags as unverified, because it was not possible to confirm the audit record persists.

Self-assessment

The work is scored across six professional lenses - product manager, business analyst, AI product manager, technical product manager, solution consultant, and digital transformation consultant. Business analysis scores four out of ten and solution consulting three, because the artifacts that define those roles largely did not exist before this document.

My contribution

  • Authored the business gap analysis end to end.

    Problem framing, stakeholder analysis, process maps, requirements, capability benchmarking, risk register, roadmap, and self-assessment.

  • Documented 20 functional and a full non-functional requirement set from a running system.

    Each requirement carries an implementation status rather than an aspiration.

  • Benchmarked ~34 capability areas against Oracle OPERA, Mews, Cloudbeds, and Apaleo.

    Severity-rated each gap and separated portfolio value from commercial readiness.

Current limits

  • Requirements were reverse-engineered from finished code rather than gathered before it - the analysis names this as the wrong order to work in.
  • The remaining BA backlog is unwritten: PRD, BRD, formal personas, competitive analysis, requirements traceability matrix, and AI governance policy.
  • No validation with a real hotel operator, so the requirements are a reasoned hypothesis rather than confirmed findings.

Software Engineering

An event-driven backend and a real-time dashboard across roughly twenty feature modules, held together by a deliberately small shared component layer.

Architecture

A FastAPI backend over PostgreSQL 16 with SQLAlchemy and Alembic migrations, publishing domain events to Kafka. Redis carries fast shared state and cached dashboard reads. Connected clients receive updates over WebSockets, so the dashboard changes because something happened rather than because the page asked.

Why push instead of polling

Polling was the obvious choice and the wrong one. It scales badly across twenty modules, and the staleness it leaves is exactly what makes an operator stop trusting the screen and go ask a colleague instead. Event-driven delivery was chosen to protect trust in the data, not for elegance.

Reliability in the consumers

Kafka consumers share a base class implementing retry handling, dead-letter-queue routing for messages that cannot be processed, and idempotent handling so a redelivered event does not double-apply. This is the part of event-driven architecture that is easy to skip and expensive to omit.

Frontend and design system

Next.js 16 with React 19 and Tailwind v4. Tokens are layered - fixed brand constants, shadcn-standard semantic tokens, and HotelMind-specific status tokens - so components consume roles rather than colours. UI primitives were migrated onto Radix and CVA while deliberately keeping each component's original prop API, which let dozens of existing call sites stay untouched during the migration.

Theming as a product decision

The app ships light and dark, and opens light regardless of the operating system setting. That is deliberate: the users are hotel managers and executives, and the default should be a bright, manager-oriented screen rather than a dark analytics console. Two things enforce it - system preference detection is intentionally left off, and there is no prefers-color-scheme fallback. Dark is a fully designed opt-in theme built on its own deep-green base rather than an inversion of the light one.

A boundary worth naming

Only five real backend roles exist, but the product needed to demonstrate views for personas the backend has no role for. The solution was a client-side "Viewing as" selector that re-labels and re-orders navigation - and never touches the session role or makes an authorization decision. Display convenience and access control stay strictly separate, and the design system documents that the boundary must not be blurred.

Testing

Vitest with Testing Library on the frontend, concentrated on the shared primitives where a regression would propagate into every module, and pytest on the backend.

My contribution

  • Implemented event-driven communication over Kafka with production-grade delivery semantics.

    Shared consumer base class providing retry handling, dead-letter-queue routing, and idempotent processing.

  • Built real-time dashboard delivery over Redis and WebSockets.

    Operational events reach connected clients without polling.

  • Designed the three-layer token architecture and light-first theming.

    Brand, semantic, and status token layers; light default enforced as a product decision for manager users.

  • Migrated the UI primitives onto Radix and CVA without breaking call sites.

    Kept each component's original prop API so existing usage across the app stayed valid.

  • Separated display perspective from authorization.

    The "Viewing as" selector re-labels navigation only; the five-role backend model remains the sole access-control mechanism.

Current limits

  • JWT authentication only - no SSO, no MFA, no session policy.
  • No audit logging, so sensitive actions like refunds and rate overrides leave no reviewable trail.
  • Single-tenant with multi-branch support; unrelated hotel groups cannot be isolated.
  • Accessibility has not been audited against WCAG, so no claim is made either way.

Data Engineering

An operational schema that models the real hotel hierarchy, lifted into a dbt star schema - built before the models rather than after.

Operational schema

Hotel, branch, floor, room type, and room are modelled as distinct levels rather than flattened, because analytical questions get asked at every one of them. Grain becomes a property of the model instead of something each query reconstructs. Branch scoping runs through the schema, so authorization and analytics agree on what a user can see.

Pipeline

Airflow DAGs extract from Postgres on a schedule using incremental watermark loading rather than full reloads. MinIO provides the data-lake layer. dbt builds the warehouse: conformed dimensions, fact tables at defined grain, and marts shaped around the questions the product actually asks rather than mirroring source tables.

Decisions recorded as ADRs

Five architecture decision records cover keeping the warehouse in Postgres rather than adopting a separate engine, choosing dbt for transformations, using MinIO for the lake, incremental watermark loading, and the star-schema design. Each states the context and the trade-off accepted.

Why this came first

Building the warehouse before the models meant the ML layer trained on consistent, correctly-grained history instead of ad-hoc extracts. It is also what makes the training-data audit in the ML section possible - provenance can only be traced when there is a defined pipeline to trace it through.

My contribution

Current limits

  • First-party data only - no ingestion from an external PMS, POS, or channel manager, so in a real multi-vendor hotel this becomes another disconnected system rather than a single source of truth.
  • No business-facing data governance: no documented ownership, lineage, or stewardship, and no data dictionary mapping warehouse fields to industry metrics.

Machine Learning

Five prediction problems served by ten trained artifacts - with a written account of which ones the evidence actually supports.

The problems and the models

Pricing uses XGBoost, feeding the guardrailed recommendation rather than writing rates directly. Occupancy forecasting uses Prophet and XGBoost together. Restaurant demand is modelled per meal period - breakfast, lunch, and dinner are separate artifacts, because they behave nothing alike. Staffing requirements use a regression model. Churn has two candidate algorithms, Random Forest and XGBoost. That is five problems across ten model files.

Serving

Models are served behind FastAPI prediction endpoints, one per problem, each documented with its request and response contract so consuming code needs no knowledge of the model behind it.

The finding that mattered

Keeping an explicit record of what each model was trained on is what surfaced that the churn model's headline accuracy was inflated by label leakage rather than earned by predictive skill. It looked like the strongest number in the project and was in fact the least trustworthy. Finding that in your own work is uncomfortable and considerably cheaper than a buyer finding it during a pilot.

My contribution

  • Trained and evaluated models across five prediction problems.

    Model selection matched to each problem - Prophet for seasonality, per-meal-period models where the periods genuinely differ.

  • Diagnosed label leakage in the churn model and documented it.

    Identified that reported accuracy came from leakage rather than predictive skill, and recorded it rather than shipping the number.

  • Maintained per-model training-data provenance.

    Documented which models train on real operational history and which on generated data.

Current limits

  • Restaurant demand and staffing are trained entirely on synthetic data with no real-world validation - their output is directional and should not drive prep quantities or rosters.
  • The churn model's metrics are compromised by label leakage; the feature set needs rebuilding before the model is usable.
  • Occupancy is validated against a relative baseline rather than real room-inventory ground truth.
  • The shift optimiser that would turn a staffing forecast into an actual schedule is scaffolded but not built.

Generative AI

A retrieval-augmented assistant over the hotel's own operational data, behind a provider abstraction - and currently degraded in production.

Retrieval

Hybrid retrieval combines FAISS dense vector search with BM25 keyword matching, so both semantic similarity and exact operational terms are covered. Documents are chunked and indexed from the hotel's own data, with conversation memory maintained across turns.

Provider abstraction

Generation sits behind a factory over three interchangeable providers - Gemini, OpenAI, and Ollama - so the assistant is not welded to a single vendor and can run locally. This is what made it possible to keep the retrieval layer serving when hosted generation had to be switched off.

Applied elsewhere

The same layer produces review analysis and executive insight summaries over operational data, rather than only powering a chat window.

The lesson

After a server-overload incident, generation was disabled in production and the assistant silently fell back to returning raw retrieved documents with no generated answer and no indication anything had changed. That reframed the incident: a silent fallback is a product defect, not an operations footnote. A user who asks a question and receives documents instead of an answer has been misled by the interface. It needs an explicit "generation unavailable" state.

My contribution

  • Built hybrid RAG retrieval combining dense vectors with keyword search.

    FAISS and BM25 together, with chunking, indexing, and conversation memory.

    • Hybrid RAG retriever
  • Designed the pluggable LLM provider abstraction.

    One interface over Gemini, OpenAI, and Ollama, including a local option.

    • LLM provider abstraction
  • Reclassified the silent retrieval fallback as a product defect.

    Identified that degrading without telling the user is an interface failure, requiring an explicit unavailable state.

Current limits

  • LLM generation is currently disabled in production following a server-overload incident, so the assistant returns retrieved documents rather than generated answers.
  • The silent degradation has no user-facing indicator yet - the fix is identified, not shipped.
  • Review sentiment scoring uses a keyword lexicon rather than a trained NLP model.

MLOps

Experiment tracking, a real model registry with promotion and rollback, scheduled retraining, drift detection, and metrics - all self-hosted.

From pickle files to a platform

The models began as flat .pkl files loaded by hardcoded path. That works until the first time you need to know which data produced a given model, or to roll one back. The MLOps layer replaced that convention with tracked experiments and a versioned registry, without breaking any existing API or test.

Registry and promotion

Models move through registry, staging, production, and archived directories with explicit promotion and rollback, so the serving layer resolves the current production model rather than a hardcoded filename.

Tracking, orchestration, observability

MLflow records experiments and artifacts against its own Postgres backend. Airflow orchestrates retraining. Prometheus scrapes API and model metrics through middleware exposing a metrics endpoint, and Grafana visualises them. Evidently handles drift detection.

Local-first by design

The whole platform runs on Docker with no paid cloud service required, while staying portable to a managed environment later. For a self-funded project that constraint is the difference between having an MLOps story and not having one.

My contribution

  • Built a self-hosted MLOps platform around the existing models.

    MLflow tracking, versioned registry with promotion and rollback, Airflow retraining, Prometheus/Grafana observability, and Evidently drift detection - added without breaking existing APIs or tests.

  • Replaced hardcoded model paths with registry-based resolution.

    Serving resolves the current production model through the registry rather than a fixed filename.

    • Model registry

Current limits

  • No MLOps view is exposed to business users - model health and drift are visible to an engineer, not to whoever would own the risk.
  • Retraining is orchestrated but not yet driven by drift signals in a closed loop.

Cloud & DevOps

The full stack composed in Docker behind Nginx and TLS on a single provisioned VM - honest about what that does and does not withstand.

Composition

One Docker Compose stack brings up Kafka and Zookeeper, PostgreSQL 16, Redis 7, MinIO, Prometheus, and Grafana alongside the application services, with Kafka UI and the MinIO console for inspection.

Provisioning and hardening

Hosts are provisioned from cloud-init: a non-root deploy user with SSH key access, Docker, Nginx, certbot for TLS, ufw for the firewall, and fail2ban. Nginx terminates TLS and reverse-proxies to the application.

Operations

Deploy, backup, and restore scripts exist and are version-controlled rather than living in someone's shell history.

What this does not survive

It is a single virtual machine. There is no high availability and no tested disaster-recovery procedure - backup scripts exist, but a restore has not been rehearsed. One host failure is an extended outage. The server-overload incident that disabled the assistant's generation layer came directly out of this topology. This is why the project is labelled a production prototype rather than production.

My contribution

  • Composed and deployed the full multi-service stack.

    Kafka, Postgres, Redis, MinIO, Prometheus, and Grafana plus application services as one reproducible Docker stack.

  • Automated host provisioning and hardening.

    cloud-init with a non-root deploy user, TLS via certbot, ufw firewall, and fail2ban.

  • Scripted deployment, backup, and restore.

Current limits

  • Single VM with no high availability - one host failure is an extended outage.
  • Backup scripts exist but restore has never been rehearsed, so disaster recovery is untested.
  • No business-facing SLA, uptime dashboard, or incident communication process.

Decisions & trade-offs

Every decision cost something. These are the ones worth defending, with what each gave up.

  • Model-generated price changes require explicit human approval.

    Unsupervised AI pricing is the first thing a hotel buyer objects to, and the objection is legitimate - a bad automated rate change costs real revenue immediately and is hard to unwind.

    Alternatives considered

    • Fully automatic rate updates - the strongest demo and the weakest risk position
    • Automatic within a bounded band, escalating only outliers - viable later, but it needs a track record of accepted recommendations to set the band honestly

    Trade-off: Recommendations only create value when someone reviews them, so the system depends on an operator staying in the loop.

  • Build the data warehouse before training any models.

    Models trained on ad-hoc extracts cannot be audited or reproduced later.

    Trade-off: Delayed the first visible ML feature by a significant margin.

  • Event-driven delivery over Kafka rather than polling.

    Staleness is what makes an operator stop trusting a dashboard; push keeps the screen authoritative. Kafka also gave durable, replayable event history, which meant consumers could be added later without backfilling by hand.

    Alternatives considered

    • HTTP polling on an interval - simplest, but scales badly across twenty modules and leaves exactly the staleness that erodes trust
    • Postgres LISTEN/NOTIFY - no new infrastructure, but no durability or replay, and it couples every consumer to the database
    • Redis pub/sub alone - lightweight and already in the stack, but fire-and-forget with no retry or replay semantics

    Trade-off: Kafka and Zookeeper are substantial infrastructure for a single-property system. This is a real over-engineering risk, accepted for the durability guarantees and the learning - and worth naming rather than defending.

  • PostgreSQL for both the operational schema and the warehouse.

    The hotel domain is strongly relational - hotel, branch, floor, room type, room, booking - and needs real constraints and transactions. Keeping the warehouse in the same engine avoided a second system to operate for a data volume that never warranted one.

    Alternatives considered

    • A dedicated warehouse engine such as BigQuery, Snowflake, or ClickHouse - better at scale, but adds cost and operational surface for a dataset this size
    • A document store for flexible booking shapes - loses the relational integrity the domain actually depends on

    Trade-off: Analytical queries compete with transactional load on the same engine, and the design would need revisiting well before multi-property scale.

  • Hybrid retrieval - FAISS dense vectors plus BM25 - for the assistant.

    Operational questions mix semantics with exact terms. Dense vectors alone miss a specific room number or a rate code; keyword search alone misses paraphrasing. Combining them covers both failure modes.

    Alternatives considered

    • Dense vector search alone - simpler, but weak on exact identifiers and rare terms
    • Keyword search alone - precise but brittle to phrasing
    • A hosted vector database - less to operate, but adds cost and a dependency for a local-first project

    Trade-off: Two retrieval paths to tune and maintain, and hybrid scoring needs its own weighting decisions.

  • Keep original component prop APIs during the Radix migration.

    Dozens of call sites already consumed those APIs; changing them would have turned a primitive upgrade into an app-wide rewrite.

    Trade-off: The components diverge from stock shadcn composition patterns, so upstream examples need translating.

  • Open in light theme regardless of OS preference.

    The users are hotel managers, and a bright manager-oriented screen suits the context better than a dark analytics console.

    Trade-off: Users who prefer dark everywhere must opt in each time on a new device.

  • Self-host the entire MLOps stack on Docker.

    A self-funded project cannot carry managed-service costs, and local-first keeps the design portable to Kubernetes or a cloud platform later without rework.

    Alternatives considered

    • Managed MLflow or a cloud ML platform (SageMaker, Vertex) - less to operate, but a recurring cost with no funding behind it
    • No MLOps layer, keeping flat pickle files - simplest, but leaves models unversioned and un-rollbackable

    Trade-off: No managed reliability - the platform inherits the single VM's failure modes.

  • Publish the gap analysis, including the parts that score the work poorly.

    A reviewer doing diligence would find the synthetic training data and the label leakage anyway. Disclosing them first is both more useful and more credible.

    Trade-off: A casual reader may take the disclosed weaknesses as the headline rather than the capability behind them.

Challenges

  • The churn model's best-looking metric turned out to be label leakage, which meant discarding a result rather than shipping it.
  • A server-overload incident forced LLM generation off in production, and exposed that the fallback path degraded silently.
  • Two models had no real training data available, so they were trained on synthetic data and had to be labelled as directional rather than dependable.
  • Maintaining consistency across roughly twenty feature modules as a single developer, which the small shared primitive layer exists to solve.
  • Writing requirements after the fact for a system already built - useful, but the wrong order, and the analysis says so.

Media

Screenshots, recordings, and diagrams from the running system.

Operations

Real-time executive dashboard
Real-time executive dashboard
Staffing requirement and schedule
Staffing requirement and schedule

Revenue

Model output presented as a recommendation, not an action.
Pricing recommendationModel output presented as a recommendation, not an action.
Every model-generated rate change passes through this human-approval gate.
Guardrail approval sheetEvery model-generated rate change passes through this human-approval gate.
Occupancy forecast
Occupancy forecast

Guests

Guest experience and churn risk
Guest experience and churn risk
AI assistant
AI assistant

Evidence

The documents, code, and running system behind the claims above.

Live systemLive deployment

The running dashboard, reachable publicly behind Nginx and TLS.

DocumentBusiness Gap Analysis

Pre-launch product evaluation: requirements, stakeholders, capability benchmarking, risk register, and a self-assessment across six professional lenses.

DocumentFrontend Design System & Architecture

Token architecture, light-first theming rationale, the Radix/CVA migration, and the perspective-selector authorization boundary.

DocumentKafka Event Architecture

Topics, consumers, retry handling, and dead-letter queues.

DocumentWarehouse Schema

Dimensions, facts, and marts in the dbt star schema.

DocumentChurn Model

Includes the label-leakage finding that invalidated the headline accuracy.

DocumentSynthetic Data

Which models are trained on generated data rather than real operational history.

DocumentMLOps Platform

MLflow tracking, model registry with promotion and rollback, Airflow retraining, Prometheus/Grafana, and Evidently drift detection.

Code

Kafka consumer base class

Retry handling, dead-letter queue routing, and idempotent message processing.

hotelmind-backend / app/consumers/base.py

Code

LLM provider abstraction

Factory over Gemini, OpenAI, and Ollama providers behind one interface.

hotelmind-ml / genai/llm/factory.py

Code

Model registry

Registry, staging, production, and archived model directories with promotion.

hotelmind-ml / models/registry

Code

Hybrid RAG retriever

FAISS dense vectors combined with BM25 keyword search.

hotelmind-ml / genai/rag/retriever.py

Code

Infrastructure composition

Kafka, Zookeeper, Postgres, Redis, MinIO, Prometheus, and Grafana as one stack.

hotelmind-infra / docker-compose.yml

Screenshot
Real-time executive dashboard
Real-time executive dashboardLive occupancy and revenue, updated over WebSockets.
Screenshot
Pricing recommendation
Pricing recommendation
Screenshot
Guardrail approval sheet
Guardrail approval sheetThe human-approval gate every model-generated rate change passes through.
Screenshot
Occupancy forecast
Occupancy forecast
Screenshot
Staffing requirement and schedule
Staffing requirement and schedule
Screenshot
Guest experience and churn risk
Guest experience and churn risk
Screenshot
AI assistant
AI assistant

Documentation

Written during the build, not for the portfolio. Published as-is.

Exists but not published

Operational documents that would be shared in an interview rather than posted publicly. Listed so the inventory above isn’t mistaken for everything that exists.

  • Deployment GuideArchitecture · Contains host and environment specifics.
  • Troubleshooting RunbookEngineering · Operational detail, not useful as portfolio evidence.
  • Data Pipeline RunbookData · Operational detail.
  • ML Server Incident LogMLOps · Raw incident notes; the substance is summarised in the limitations instead.

Limitations

What this does not do, stated plainly. A reviewer would find these anyway.

  • Restaurant demand and staffing models are trained on synthetic data with no real-world validation.
  • The churn model's accuracy is inflated by label leakage and the feature set needs rebuilding.
  • Occupancy forecasting is validated against a relative baseline, not real room-inventory ground truth.
  • The AI assistant's LLM generation is disabled in production and degrades to raw retrieval without telling the user.
  • Review sentiment uses a keyword lexicon rather than a trained NLP model.
  • Deployed on a single VM with no high availability and no rehearsed disaster recovery.
  • JWT authentication only - no SSO, no MFA.
  • No audit logging for sensitive actions such as refunds and rate overrides.
  • Single-tenant: multi-branch is supported, unrelated hotel groups are not.
  • No integration with any external PMS, POS, channel manager, or payment processor - it cannot take a booking or a payment end to end.
  • No multi-property or portfolio-level reporting.
  • GDPR, PCI DSS, and SOC 2 posture are entirely unaddressed.
  • The BA document backlog is incomplete - PRD, BRD, personas, competitive analysis, and traceability matrix are unwritten.
  • Accessibility has not been audited against WCAG.

What I learned

  • A metric that looks unusually good deserves suspicion before celebration. The churn accuracy was the best number in the project and the least real.
  • Silent degradation is a product defect. If a system quietly stops doing the thing a user asked for, the interface has misled them regardless of how correct the fallback is.
  • Requirements written after the build are useful documentation and poor analysis. The value came from what the exercise exposed, not from the artifact itself.
  • Building the warehouse before the models felt like a detour and was the reason the ML work went quickly and stayed auditable.
  • Choosing infrastructure for what it teaches is legitimate, but the cost should be stated plainly - Kafka for a single property is over-engineered, and pretending otherwise would be dishonest.
  • Disclosing a weakness costs less than having it discovered. Writing the gap analysis was more valuable to the work than any feature added in the same period.

Roadmap

Next

  • Retrain restaurant and staffing models on real data

    Replace synthetic training data with real transactional and attendance history so the recommendations become dependable.

  • Rebuild the churn feature set without leakage

    Eliminate the leaking features and validate against held-out real outcomes.

  • Re-enable the assistant with an explicit degraded state

    Resilient generation hosting, plus a visible indicator when generation is unavailable instead of a silent fallback.

Then

  • Audit logging

    An action-level trail for refunds, rate overrides, and access changes.

  • Identity: SSO, MFA, granular roles

  • Documented and tested disaster recovery

    Rehearse a restore, then move toward a resilient topology.

  • Complete the BA artifact set

    PRD, BRD, personas, competitive analysis, traceability matrix, and AI governance policy.

Later

  • External integrations

    Channel manager and OTA connectivity, POS integration, and a real payment processor - the work that turns an overlay into a system of record.

Skills demonstrated

Product Management
Product scopingFeature prioritisationHuman-in-the-loop AI designRoadmap planningProduct positioning
Business Analysis
Requirements elicitationFunctional & non-functional requirementsAs-Is / To-Be process mappingGap analysisStakeholder analysisBusiness rulesAcceptance criteriaCompetitive benchmarking
Software Engineering
Next.jsReactTypeScriptFastAPIREST API designEvent-driven architectureWebSocketsPostgreSQLRedisApache KafkaDesign systemsTesting
Data Engineering
Dimensional modellingStar schema designApache AirflowdbtETL pipelinesIncremental loadingData lake (MinIO)
Machine Learning
XGBoostProphetRandom ForestRegressionTime-series forecastingFeature engineeringModel evaluationData-leakage diagnosis
Generative AI
RAGFAISSBM25Hybrid retrievalEmbeddingsChunking strategyLLM provider abstractionPrompt design
MLOps
MLflowModel registryPromotion & rollbackDrift detection (Evidently)PrometheusGrafanaScheduled retraining
Cloud & DevOps
DockerDocker ComposeNginx reverse proxyTLS / certbotcloud-init provisioningufw & fail2banBackup & restore scripting
Tools
GitGitHubNetlifyKafka UIMinIO Console

Next

Questions about any of this — including the parts that don’t work yet — are the ones worth having.