Property #1 Readiness Audit — 2026-04-28
Verdict: NO-GO as of 2026-04-28 (15:30 IST).
The platform cannot onboard a paying property today. 47 P0 items are open across the 13 launch flows; 30+ would each be launch-blocking on its own. The heaviest blockers:
- GST e-invoice IRN is mocked in
staging-awsandprod-awscompose files; the printed/emailed PDF hardcodes the literal stringIRN: Pending. Every invoice produced today is non-compliant under the Goods and Services Tax (Compulsory Generation of Invoice Reference Number) Notification 13/2020. - No Razorpay or merchant-account credentials are seeded anywhere.
MerchantAccountis empty on QA. The first guest payment will 404 fromMerchantAccountService.resolveForPayment. - The Kitchen Display app (
hmp-kds-kiosk) connects to the wrong WebSocket namespace (/eventsinstead of/kitchen), sends no JWT, and listens to event names (kot.received,kot.bumped) that no producer emits. It silently falls back to demo data. The wall-mounted KDS in the kitchen will never see a real KOT. - AWS QA Materialised Views are empty across the board. Every
/api/reports/*endpoint returns{"data":[]}and every/api/analytics/dashboard/metricsfield is zero. Reports & Dashboards (Flow 10) do not function on QA. - The
Extend Stayflow does not recompute rate or post the additional-night folio charge forCHECKED_INreservations. Going live would systematically under-bill every stay extension until night audit catches up.
Section A — Executive Verdict
| Overall | NO-GO |
| Date / time | 2026-04-28, 15:30 IST |
| Flows audited | 13 / 13 |
| Local Docker verdicts | 0 GO · 9 GO-WITH-MITIGATIONS · 4 NO-GO |
| AWS QA verdicts | 0 GO · 7 GO-WITH-MITIGATIONS · 6 NO-GO |
| P0 action items | 47 |
| P1 action items | 41 |
| P2 action items | 39 |
| Could-not-verify items | 14 (see Section G) |
The audit threshold is breached: > 30 P0 items is launch-blocking on its own.
The five heaviest blocks (each affects multiple flows):
- Compliance — e-invoice IRN mocked end-to-end; PDF hardcoded "Pending"; B2B path is dead code; no DPDPA erasure timer.
- Configuration / secrets — no Razorpay, ResAvenue, NIC, S3, SendGrid credentials in any compose file routed to QA or prod.
- Mobile surface — frontdesk-app does not exist; reservations UI absent in all 5 mobile apps; F&B mobile paths 404; KDS Electron app non-functional against real backend.
- QA data parity — QA RDS has 0 reservations on Property
40b29005-...; analytics MVs empty; default property referenced by docs is missing. - Cross-service eventing — analytics dispatcher consumes 5 of 9 spec topics;
night.audit.completednot consumed;kot:creatednever emitted;folio.postedvsfolio.charge.postedname drift drops events silently.
Section B — Per-Flow Status Table
| # | Flow | DB | Backend | Gateway | Web UI | Mobile UI | KDS/Kiosk | Tests | External | Local | QA | Verdict |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Tenant Onboarding | ✅ | ⚠ | ✅ | ⚠ | n/a | n/a | ⚠ | ❌ | ❌ NO-GO | ❌ NO-GO | ❌ NO-GO |
| 2 | Property Onboarding | ⚠ | ⚠ | ✅ | ⚠ | ❌ | n/a | ⚠ | ❌ | ⚠ MIT | ❌ NO-GO | ❌ NO-GO |
| 3 | Booking Engine | ⚠ | ✅ | ✅ | ⚠ | n/a | n/a | ✅ | ⚠ | ⚠ MIT | ⚠ MIT | ⚠ MIT |
| 4 | Staff Reservations | ✅ | ⚠ | ✅ | ✅ | ❌ | n/a | ⚠ | ⚠ | ⚠ MIT | ⚠ MIT | ⚠ MIT |
| 5 | Frontdesk / HK | ⚠ | ⚠ | ✅ | ⚠ | ⚠ | n/a | ⚠ | ⚠ | ⚠ MIT | ⚠ MIT | ⚠ MIT |
| 6 | Check-In | ⚠ | ✅ | ✅ | ⚠ | ❌ | ❌ | ⚠ | ❌ | ⚠ MIT | ⚠ MIT | ⚠ MIT |
| 7 | Room Service / KDS / Folio | ⚠ | ⚠ | ⚠ | ✅ | ❌ | ❌ | ⚠ | ⚠ | ⚠ MIT | ⚠ MIT | ⚠ MIT |
| 8 | Check-Out + e-Invoice | ⚠ | ⚠ | ✅ | ✅ | ❌ | n/a | ⚠ | ❌ | ⚠ MIT | ⚠ MIT | ⚠ MIT |
| 9 | Night Audit | ⚠ | ⚠ | ⚠ | ✅ | ❌ | n/a | ⚠ | ⚠ | ⚠ MIT | ✅ GO* | ⚠ MIT |
| 10 | Reports & Dashboards | ⚠ | ⚠ | ⚠ | ⚠ | ❌ | n/a | ⚠ | ⚠ | ⚠ MIT | ❌ NO-GO | ❌ NO-GO |
| 11 | ResAvenue / Channel | ✅ | ✅ | ❌ | ⚠ | n/a | n/a | ❌ | ❌ | ⚠ MIT | ❌ NO-GO | ❌ NO-GO |
| 12 | Pinelabs + Razorpay | ✅ | ✅ | ✅ | ⚠ | ❌ | n/a | ✅ | ❌ | ❓ | ⚠ MIT | ❌ NO-GO |
| 13 | Tape Chart (HERO) | ✅ | ⚠ | ✅ | ⚠ | n/a | n/a | ⚠ | ⚠ | ⚠ MIT | ⚠ MIT | ⚠ MIT |
Legend: ✅ implemented · ⚠ partial · ❌ broken/missing · ❓ unverifiable · n/a not in scope. MIT = GO-WITH-MITIGATIONS. (*) Flow 9 QA result is "endpoints respond, history empty, status NOT_STARTED" — no NA has been run.
Section C — Detailed Findings
Flow 1 — Tenant Onboarding
Verdict (local): NO-GO · Verdict (QA): NO-GO Summary: Self-service tenant signup endpoint works on both envs and Flow 1 jest spec passes, but the customer-facing "forgot password" form is broken end-to-end (DTO requires tenantId), no welcome email is wired, no plan selection / payment is in the signup path, and the admin-portal "Setup Wizard" is SUPER_ADMIN-only.
- DB:
Tenant,User,Session,PasswordHistory,SubscriptionPlan,TenantSubscription,Invoiceall present inhmp-auth-service/prisma/schema.prisma. No drift detected from live calls. - Backend:
POST /tenants/signup(tenants.controller.ts:34-56) is Public — verified 201 on both envs.OnboardingController(onboarding.controller.ts:32,58,84,110,136) gates EVERY post-signup wizard step on@Roles('SUPER_ADMIN'). A new tenant admin cannot run any step.OnboardingServiceself-marksDEPRECATED — uses xPrisma anti-pattern(onboarding.service.ts:15-29). - DTO:
ResetPasswordRequestDto.tenantIdis@IsNotEmpty @IsUUID(auth/dto/login.dto.ts:52-62). Dashboardforgot-password/page.tsx:25POSTs{email}only and gets 400 on both envs. - Gateway:
/tenants,/onboarding,/billing,/subscriptionsregistered (services.config.ts:19,31,54,875,888);@Alldecorators present (proxy.controller.ts:32-40,107-115). - Frontend: admin-portal has
(auth)/login/page.tsxonly — no/signup, no/forgot-password. Dashboard "Forgot your password?" link leads to a guaranteed 400. - Notification:
dispatcher.service.ts:96-139does NOT subscribe toTENANT_CREATED. No welcome email is ever sent. - Plan: signup creates no
TenantSubscriptionrow. - Tests:
flow-1-tenant-onboarding.spec.ts(9 scenarios),tenant-onboarding-full.flow.spec.ts(10 scenarios),mfa-totp.flow.spec.ts(4 scenarios) — all green. None exercise email-only forgot-password. - Curl: local + QA
POST /api/auth/password/reset {email}→ 400tenantId should not be empty. LocalPOST /api/auth/forgot-password→ 503 (route absent). Local + QAGET /api/billing/plans→ 401 (no public pricing).
Flow 2 — Property Onboarding
Verdict (local): GO-WITH-MITIGATIONS · Verdict (QA): NO-GO
Summary: Schema and CRUD exist for all 7 sub-flows and seeded data populates them, but image upload (S3) is broken in both envs, latitude/longitude is not accepted by the DTO, geocoding/Google Maps is not implemented, and QA's reference property 40b29005... has zero seeded onboarding data.
- 2.i Address+Maps —
latitude Float?/longitude Float?columns exist (schema.prisma:93-94) but are NOT inCreatePropertyDto/UpdatePropertyDto(property.dto.ts:60-219); service create block has zero matches forlatitude|longitude. UI submits values that are silently dropped. NogoogleMapsUrlcolumn. Zero geocoding code. - 2.ii Pictures —
POST /api/assets/upload-urlreturns HTTP 500 "Could not load credentials from any providers" on both local AND QA. ZeroS3_BUCKET/AWS_*env vars in any compose file. UI is URL-paste-only. - 2.iii Room Types — schema rich, CRUD works, but
images=0everywhere (no seeded room-type pictures); same S3 break. - 2.iv Rooms — bulk + single create work.
- 2.v Rate plans — CRUD works;
RatePlanRateempty in seed; calendar shows base rate only. - 2.vi Tax — CGST/SGST/IGST split works; no
inclusive Booleanflag onTaxConfiguration. - 2.vii Cancellation policy — CRUD works;
rulesis free-form Json, no schema validator. - Parity — QA tenant
0001(the documented one) has 0 properties. Property40b29005-...404s.
Flow 3 — Booking Engine Integration
Verdict (local): GO-WITH-MITIGATIONS · Verdict (QA): GO-WITH-MITIGATIONS
Summary: Code path is fully implemented and wired end-to-end through gateway + payment-service, but booking-creation idempotency is missing, the public API key is a single shared seed string, and the booking-engine themes shipped are direction-a/b presets, not the documented Midnight Sovereign / Alabaster Court / Ink & Brass.
- DB:
Reservation.source = BOOKING_ENGINEworks; noidempotencyKeycolumn; only payment-initiate carries one (booking-engine.service.ts:1686). Double-submit unprotected. - API key:
bookingEngineApiKey: 'hmp-default-public-booking-engine-key'baked into every property'ssettingsJSON (seed-keynova-properties.sql:122). Same key for every tenant + property.PublicApiKeyGuard(api-key.guard.ts:50-119) falls back to "format-only" warning when noIApiKeyServiceis bound, so any 16+ char string passes. Major prod risk. - Backend: payment-v2 chain verified (controller
:198→ service:1641-1717→PaymentServiceClient.initiatePayment). Amount-mismatch guard±1rejects tampered totals (:1665). - Gateway:
/booking-engineregistered +@Alldecorators present (services.config.ts:896,proxy.controller.ts:1038-1046). - Frontend: 6 themes in
src/lib/themes/presets/direction-{a,b}-{boutique,luxury,urban}.tsplusrefined-hospitality.ts,modern-operations.ts,vibrant-approachable.ts— none are Midnight Sovereign / Alabaster Court / Ink & Brass. (Note: the booking-engine theme set is independent of the dashboard's default, which is now the KeyNova theme via@hmp/design-tokens.) - Tests:
booking-flow.spec.ts,booking-full-flow.spec.ts,booking-mobile.spec.ts,manage-booking.spec.ts— green per memory 2026-04-16. - External: Razorpay creds and webhook URL registration on QA + prod could not be verified (no env access).
Flow 4 — Staff sees Reservations
Verdict (local): GO-WITH-MITIGATIONS (cannot fully verify — the local stack ran but local credentials drift) · Verdict (QA): GO-WITH-MITIGATIONS
Summary: Web list+detail are feature-complete and the cross-property 404 regression is fixed; QA has empty data, mobile reservations UI does not exist, guestName filter is silently ignored, and the detail page computes folio rows client-side from rate × nights instead of reading folios[].transactions[].
- DB: schema rich and well-indexed for hot reads (
schema.prisma:19-118). - Backend:
findOnecorrectly tenant-scoped + ForbiddenException on cross-property;search()(reservations.service.ts:1831-1917) does NOT implementguestName— comment at l.1891: "skipped for now (guest model is in guest-service)". Live probe{guestName:"Smith"}returned 0 rows on QA without error. - Gateway:
/reservationsplus explicit/reservations/:id/foliosoverride to frontdesk all wired. - Frontend (web):
reservations/keynova.tsxand detail page render every business field;skipPropertyHeader: trueon the detail fetch is verified (reservations.ts:155-161). Detail page's FolioTable is client-siderateAmount × nights([id]/keynova.tsx:944-986) — diverges from real folio under split, amend, discount. - Frontend (mobile): ZERO matches for
reservationacrossapps/{staff,manager,fnb,guest,housekeeping}. The frontdesk-app referenced in the audit prompt does not exist in this repo. No Detox/Maestro spec. - Tests: 8 backend specs + 5+ web E2E. Mobile: NONE.
- QA data:
GET /api/reservations?limit=3→ 200{data:[],total:0}. Property #1 demo will look empty.
Flow 5 — Frontdesk / Housekeeping
Verdict (local): GO-WITH-MITIGATIONS · Verdict (QA): GO-WITH-MITIGATIONS Summary: Room-move and HK ops are wired end-to-end; Extend Stay does NOT recompute rate from new nights, does NOT post the extra-night folio charge on CHECKED_IN, and the housekeeping-service WebSocket namespace is not externally reachable on QA.
- 5.i Room move: two endpoints exist (
POST /front-desk/room-moveroom-ops.controller.ts:13;POST /room-movecheckin.controller.ts:97). Tape-chart drag uses the latter, which has pricing safeguard but does not actually post the rate-difference charge. - 5.ii Extend: only
PUT /reservations/:idwith newdepartureDate. Rate recalc bug atreservations.service.ts:1321-1336—totalAmount = Σ rr.rateAmount(frozen at create time), so 3→5 nights ≠ +2 nights' charge. No folio charge posted for extra nights on CHECKED_IN; onlyOVERSTAY_NIGHTfrom night audit (afterdepartureDate < today). - 5.iii HK: gateway
/housekeepingnamespace fully implemented, JWT-validated, property-scoped, Redis-adapted. MobileuseRealtimeTasksconnects directly to${EXPO_PUBLIC_WS_URL}/housekeeping. Web uses/eventsaggregator namespace. QA Nginx (qa-nginx-hmp.conf:60-68) only exposes/socket.io/to api-gateway. Curl:3006/socket.io/timed out. Mobile HK app on a real phone cannot receive HK events on QA. Same blocker for/kitchen.
Flow 6 — Check-In (Indian + International + C-Form)
Verdict (local): GO-WITH-MITIGATIONS · Verdict (QA): GO-WITH-MITIGATIONS Summary: Staff-driven Indian + International check-in is solid (mandatory ID DTO, FRRO blocker enforcement, PDF C-Form with PII encryption); MHA portal submission is manual upload + ack-number tracking; kiosk and mobile-staff check-in are stubs.
- Indian: ID type + number now mandatory (
checkin.dto.ts:140-159); Aadhaar/PAN/DL regex client-side. GSTIN not collected at check-in (lives onFolio.companyId). - International: passport+visa hard-block enforced; visa+passport expiry must be ≥ departureDate. No FRRO portal API integration —
cform.service.ts:445PDF footer says "upload this PDF to https://indianfrro.gov.in"; staff callsPOST /frontdesk/cform/:id/mark-submittedwith manual ack number. - Visa stored as
GuestIdentity{idType:OTHER}(cform.service.ts:260-262) — fragile; no dedicated visa columns/model. - Kiosk:
useCheckInFlow.ts:12-41is a hardcoded John Smith mock. All hardware mocked. Kiosk app is a UI prototype, not connected to backend. - Mobile:
apps/staff/app/(tabs)/checkin.tsxis a 16-line<PlaceholderScreen>. - Two parallel C-Form controllers exist (
frontdesk/cform.controller.tsandcompliance/cform.controller.ts). Drift risk. - ID-photo upload uses inline base64 in
idDocumentImageUrl(check-in-form.tsx:242-250) — does not call/uploads/presigned-url. - PII encryption:
FieldEncryptionServiceis@Optional(). IfENCRYPTION_KEYis unset in prod, CForm stores plaintext silently.
Flow 7 — Room Service → KDS → Folio
Verdict (local): GO-WITH-MITIGATIONS · Verdict (QA): GO-WITH-MITIGATIONS
Summary: Backend chain works for the dashboard surface, but the dedicated hmp-kds-kiosk Electron app talks to the wrong gateway namespace and wrong event names; emitKotCreated is dead code; tax breakdown CGST/SGST/IGST is dropped at the publisher; transactionCode is hardcoded 'FnB'; mobile F&B 404s on every gateway call.
- Order entry:
POST /api/ordersworks with full server-side reprice + auth. Dashboard/pospage wired correctly. - KDS server:
KitchenGatewaywith JWT/RS256 in prod, multi-instance Redis, four emits (kot:created,kot:statusChanged,order:itemReady,order:updated). emitKotCreatedis never called from any service — only from the.spec.tsmock. New KOTs are not pushed in real time.- KDS Electron app (
hmp-kds-kiosk): connects to${VITE_GATEWAY_URL}/events(gateway namespace, not/kitchen), sends noauth.token, listens tokot.received/kot.bumped/kot.priority(none of which match server emits). Falls back toVITE_USE_DEMO=truedemo data. Wall-mounted KDS will never see real orders. - Mobile F&B (
apps/fnb): paths are/pos/orders,/pos/outlets/:id/menu. Gateway has no/posprefix. Every Mobile F&B request 404s. - Folio consumer:
PosFolioChargeConsumeris robust, idempotent, flag-gated;ENABLE_POS_FOLIO_CHARGE_CONSUMER=trueset in all 4 compose files. - Tax breakdown lost: producer publishes
taxAmountscalar only; CGST/SGST/IGST +customerStateare dropped. Consumer recomputes from folio property state, NOT source. transactionCodehardcoded'FnB'(orders.service.ts:1218); noROOM_SERVICE/CANTEEN/PREORDERdifferentiation.- Multi-station:
KOT.stationis a free-textString?hardcoded'Kitchen'; noKitchenStationmodel; KDS station filter is decorative.
Flow 8 — Check-Out + GST e-Invoice (IRN)
Verdict (local): GO-WITH-MITIGATIONS · Verdict (QA): GO-WITH-MITIGATIONS
Summary: Plumbing for settle → IRN → email is wired and event-driven, but EINVOICE_MOCK=true on staging-aws and prod-aws compose files (illegal under GST law), invoice PDF prints IRN: Pending verbatim, mobile/kiosk self-checkout is web-only, and DPDPA erasure log is never written on checkout.
- Settle: 3 paths (settleBalance+mode inline, forceCheckout city-ledger, reject) with Redis lock 30s + Decimal-safe ±0.005 close threshold.
- e-Invoice queue (
EInvoiceJob): real retry / 5-min backoff / DEAD_LETTER. Provider resolved byEINVOICE_MOCKenv: true →MockEInvoiceAdapter(SHA-256 deterministic IRN); false →NicEInvoiceAdaptercalling${baseUrl}/eicore/v1.03/Invoicewith Bearer. EINVOICE_MOCK=trueondev-all.yml:43,qa-aws.yml:41,staging-aws.yml:39,prod-aws.yml:39. NoNIC_CLIENT_ID/SECRET/USERNAME/GSTINin any compose file. Every "production" IRN is a SHA-256 ofgstin|INV|folioNum|FY— legally invalid.invoice-pdf.service.ts:534:const irn = 'Pending'; doc.text('E-Invoice QR: {IRN: ${irn}}')— every PDF generated byGET /folios/:id/invoice/pdfprints "Pending".- B2B IRN dead code:
einvoice.service.ts:90-91hardcodescompany: null, guestAddress: null. Every invoice files as B2C "URP", regardless of corporate booking — silent compliance gap. - ESC/POS branded receipt template (
@hmp/common/printing) exists but is NOT invoked from checkout — printer-print is unused. - SendGrid not configured on any compose file. Invoice emails fail silently in the channel layer.
- GDPR/DPDPA: checkout does NOT write
GdprErasureLog. No 90-day cron exists.GUEST_ERASEDis consumed only by reservation-service + banquet-service (not by frontdesk, pos, housekeeping, analytics, crm, payment, notification).
Flow 9 — Night Audit
Verdict (local): GO-WITH-MITIGATIONS (local creds drift — admin@keynovahotels.com rejected on local) · Verdict (QA): ✅ GO* (endpoints respond, history empty, status NOT_STARTED)
Summary: Service-layer NA is mature (Redis-locked, single Prisma tx, idempotent re-run, Kafka-published, scheduler-driven), but businessDate is written into Property.settings JSON, mobile manager app has zero NA surface, no flow-test suite exists, and the analytics dispatcher does not subscribe to night.audit.completed.
- Service:
startNightAuditacquiresaudit:lock:{propertyId}, runs single 120s tx, advances businessDate, publishes Kafka, releases lock. Idempotent same-day guard at:227-241. Stale-RUNNING recovery on boot. - Scheduler:
@Cron(EVERY_HOUR)ticks all active properties, compares local hour tosettings.nightAuditHour(default 2). No k8sCronJobfallback — pod down at 02:00 IST = silent miss. - Rollback bug at
:910-916: filter istransactionCode='ROOM' AND postedBy=audit.performedBy.OVERSTAY_NIGHTandNO_SHOWcharges are NOT rolled back — left orphaned on folios. - Analytics gap:
EventDispatcherServicedoes NOT subscribe tonight.audit.completed. CLAUDE.md claim of "5 MV consumers to 9 topics" is inaccurate. Daily reports MVs never refreshed by NA. businessDatestored inProperty.settingsJSON viaxPrisma, not a typed column. Untyped, un-indexed.- Gateway:
@All('night-audit/*')exists but bare@All('night-audit')is missing — two-file rule violation (harmless today; existing routes all have sub-segments). - Mobile: zero NA references in
hmp-mobile-apps. - Tests: unit + dashboard E2E exist; no flow-test suite.
Flow 10 — Reports & Dashboards
Verdict (local): GO-WITH-MITIGATIONS · Verdict (QA): NO-GO
Summary: The CQRS plumbing is solid on paper; in practice only local has populated MVs, the dashboard /metrics endpoint and the report endpoints don't share a source of truth, and the admin/mobile front-ends are still demo placeholders.
- 9-topic dispatcher truth table:
- reservation.created ✅, reservation.cancelled ✅, reservation.modified ❌ NOT subscribed
- folio.posted ❌ (consumer subscribes to
folio.charge.posted— name drift drops the topic) - folio.closed ❌ NOT subscribed
- kot.completed ❌ NOT subscribed
- order.closed ✅, task.completed ✅, channel.sync.failed ✅
- Score 5/9.
MvAdrRevpar.adrandrevparcolumns are never written byReservationConsumer. Local has non-zero values via an undocumented backfill path; QA has 0 across the board.DashboardController.getMetricsreturnsrevpar:0, adr:0, todayRevenue:0while/reports/revenue/dailyreturns non-zero for the same day → two contradicting numbers on the same screen.- Mobile manager
dashboardApi.getMetricscalls/analytics/dashboard/${propertyId}— backend route is/analytics/dashboard/metrics. 404/hang in production. - Admin portal
/analyticspage is 100% hardcoded demo data (page.tsx:26-110). - Dashboard
/reports/[category]/page.tsx:23-79falls back to a 40-entry hardcoded fake report list that does not match the backend catalog. - GSTR-3B: not implemented.
/gstgateway prefix routes to analytics but no controller exists. No HSN summary, no place-of-supply aggregation. - QA: every
/api/reports/*endpoint returns{"data":[]}; every/api/analytics/dashboard/metricsfield is 0;/api/control-center/todayall zeros. Producer side either not emitting or analytics consumer not connected on QA.
Flow 11 — ResAvenue / Channel Manager
Verdict (local): GO-WITH-MITIGATIONS · Verdict (QA): NO-GO Summary: ResAvenue adapter is real code, real XML, real fetch — but the gateway never proxies its routes, no creds are provisioned anywhere, and no end-to-end test exists.
- Adapters: ResAvenue (real HTTP + OTA 2003/05 XML, 30s timeout), Booking.com / Expedia / Agoda / MMT / Goibibo / Airbnb / TripAdvisor / Google / Amadeus / Sabre real adapter classes exist. STAAH / eZee / ChannelRush listed in CLAUDE.md but no provider folder — only
intermediary/providers/resavenue/. - Gateway two-file rule violated:
services.config.ts:499-516registers/intermediary,/channel-stats,/channel-setup,/webhook-managementbutproxy.controller.tshas NO@Alldecorators for them. Confirmed: curl returns 404 on both local + QA. - MinLOS / MaxLOS NOT pushed:
handleRestrictionPushfilters onlyclosedToArrival/Departure/Stay. TheOTA_HotelBookingRuleNotifRQLOS message is never built. - No
RESAVENUE_*creds in any compose file. Code reads creds fromIntermediaryConnection.credentialsJSON; no row seeded for Property #1. - Outbound webhook URL not registered with ResAvenue.
CHANNEL_MODEissimulatoron dev-all; unset on QA → DegradedModule (health-only).- Failure surface:
CHANNEL_SYNC_FAILEDtopic produced + analytics consumer + dashboardAlertsPanelall wired ✅.
Flow 12 — POS Terminal + Razorpay PG
Verdict (local): UNVERIFIABLE (payment-service container unhealthy on IPv4 bind, known) · Verdict (QA): NO-GO Summary: Code is shipped end-to-end and authenticates correctly on QA, but no real merchant account, no Razorpay key in env, and no terminal hardware tested — Property #1 cannot take a real card today without operator config.
- Razorpay adapter is real (Orders API, refund, idempotency-key, MOCK/SANDBOX/LIVE driven by
MerchantAccount.mode). - Webhook receiver
@Public() POST /api/webhooks/:providerCode/:merchantAccountIdis correct (raw body viabody-parser.raw, HMAC verified, idempotent onWebhookEvent.@@unique([providerType, providerEventId])). - No
RAZORPAY_*env vars in any compose file:qa-aws.yml,dev-all.yml,staging-aws.yml,prod-aws.ymlall clean. TheMerchantAccounttable is meant to hold encrypted creds; no row exists for tenant 1 on QA (GET /api/merchant-accounts → {data:[]}). First payment will 404 fromMerchantAccountService.resolveForPayment. - Pinelabs adapter (
pinelabs-terminal.adapter.ts) is real (TCP/Plutus-ECR codec, 408 LOC, real socket).HARDWARE_MOCK=truein all 4 compose files; Terraformprod/compute.tf:50overrides tofalse— but anyone usingdocker-compose.prod-aws.ymlagainst prod RDS gets silent mock. - Razorpay
payment_capture: 1flag missing on Orders create body (razorpay-gateway.adapter.ts:79) — capture relies on Razorpay account default. - No reconciliation cron: missed webhooks during outage will leave
Paymentrows IN_PROGRESS forever. - No mobile terminal/Razorpay surface. Mobile is read-only on payments.
- Two parallel hardware stacks: legacy
@hmp/hardwarestill infrontdesk-service/package.json:28despite per-folder CLAUDE.md claiming removal.
Flow 13 — Tape Chart Calendar (HERO)
Verdict (local): GO-WITH-MITIGATIONS · Verdict (QA): GO-WITH-MITIGATIONS Summary: The HERO works end-to-end — drag-move, drag-extend, click-create, right-click block-room, OOO colors, virtualisation, WebSocket realtime, 46 unit tests, 4 e2e smokes. Real but shallow gaps: filter UI is cosmetic, multi-room continuity missing, extend/shorten context-menu redirects instead of using the in-place dialog.
| Op | Wired? | Notes |
|---|---|---|
| 13.i drag-drop room move | ✅ | branches CHECKED_IN→/room-move vs CONFIRMED→/reservations/assign-room |
| 13.ii drag-extend stay | ✅ | classifyExtend gates simple vs confirm |
| 13.iii drag-shorten | ✅ partial | only right-edge inward; no resize-left |
| 13.iv click-create | ✅ | POST /front-desk/quick-reservation |
| 13.v context menu | ⚠ partial | NO_SHOW action MISSING from BAR_ACTIONS; extend/shorten redirect to /reservations/:id (inconsistent with drag) |
| 13.vi status visual | ✅ | full color map + per-property override; NO_SHOW + CHECKED_OUT colors not derived |
| 13.vii multi-room continuity | ❌ | N independent bars; no link/connector |
| 13.viii filter chips | ❌ | purely cosmetic — onClick is undefined; no backend filter params accepted |
| 13.ix block multiple rooms | ⚠ partial | single-cell PATCH only; no date-range / maintenance window UI |
| 13.x virtualisation | ✅ | @tanstack/react-virtual; densities 28/36/44px; print mode disables |
| 13.xi mobile/responsive | ⚠ | TouchSensor configured; toolbar overflows on <768px; no flex-wrap; phones unusable |
- Tenant-scope receiver assert MISSING:
CalendarService.getCalendar(calendar.service.ts:81-99) does not callassertPropertyTenant(propertyId, tenantId). Relies entirely on the gateway's 5-min cache. - WebSocket auth is HS256 in current configuration; RS256 is required before production per root CLAUDE.md.
Section D — Cross-Cutting Findings
| ID | Pattern | Affected flows | Evidence |
|---|---|---|---|
| CC-1 | Mobile staff surface is largely missing — frontdesk-app does not exist as a directory; reservations UI absent across all 5 mobile apps; Mobile F&B paths wrong (404); no payment/terminal UI; no NA widget | 4, 5, 6, 7, 9, 12 | apps/{staff,manager,fnb,guest,housekeeping} greps |
| CC-2 | Configuration drift across compose envs — EINVOICE_MOCK=true in prod-aws/staging-aws; HARDWARE_MOCK=true in prod-aws (Terraform overrides); zero Razorpay/NIC/ResAvenue/SendGrid/AWS-S3 env vars; consumer flags inconsistent | 7, 8, 10, 11, 12 | docker-compose.{dev-all,qa-aws,staging-aws,prod-aws}.yml |
| CC-3 | Gateway two-file rule broken or partial — intermediary, channel-stats, channel-setup, webhook-management missing decorators; night-audit bare-prefix missing; pos prefix not registered for mobile F&B; ai/query resolves to nothing | 7, 9, 10, 11 | proxy.controller.ts vs services.config.ts |
| CC-4 | Cosmetic-but-fake UI — booking-engine themes (direction-a/b vs spec); admin-portal /analytics 100% demo data; dashboard /reports/[category] 40-entry fake list; tape-chart filter chips with no onClick; AiSuggestionToast hardcoded copy | 3, 10, 13 | per finding evidence |
| CC-5 | Cross-service event chain gaps — analytics dispatcher consumes 5 of 9 spec topics; folio.posted vs folio.charge.posted name drift; night.audit.completed not consumed; kot:created never emitted by producer; RESERVATION_MODIFIED not emitted on room-move | 5, 7, 9, 10 | event-dispatcher.service.ts:49-75, kitchen.service.ts:138, room-ops.service.ts:131-159 |
| CC-6 | Dead/deprecated code still in production paths — OnboardingService self-marked DEPRECATED; two parallel C-Form controllers; two parallel hardware stacks (@hmp/hardware still in frontdesk package.json); revenue-report stub at reports.controller.ts:171 returning hardcoded zeros | 1, 6, 10, 12 | per finding evidence |
| CC-7 | PII / GDPR / DPDPA gaps — no checkout-triggered erasure log; GUEST_ERASED consumed by 2 of 14 services (reservation, banquet); ENCRYPTION_KEY is @Optional() in CForm (silent plaintext if unset); decryption on retrieval not implemented in frontdesk; visa stored as idType=OTHER hack | 6, 8 | cform.service.ts:138, gdpr.service.ts:213-227 |
| CC-8 | External integrations stubbed or unconfigured — MHA C-Form (manual PDF + ack number); NIC e-invoice (mock everywhere); ResAvenue (no creds); Razorpay (no creds); SendGrid (no creds); S3 (broken everywhere); Google Maps (no integration) | 2, 6, 8, 11, 12 | per finding evidence |
| CC-9 | WebSocket gateways HS256 in current config — /housekeeping, /kitchen, /events all run HS256. Root CLAUDE.md says RS256 required before production | 5, 7, 13 | housekeeping.gateway.ts, kitchen.gateway.ts:51-91 |
| CC-10 | Idempotency holes — booking creation has no idempotencyKey column on Reservation; only payment-initiate carries one. Some critical paths protected (PosFolioChargeConsumer, terminal-charge), but creation-side double-submit unprotected | 3 | reservation/schema.prisma:19-118, booking-engine.service.ts:1686 |
| CC-11 | Tenant-scope receiver-side asserts missing in places — CalendarService.getCalendar and createQuickReservation do not call assertPropertyTenant; rely on gateway's 5-min cache. Migration to ServiceClient-with-receiver-assert is partially done (T3-1) | 13 | calendar.service.ts:81-99 |
| CC-12 | QA RDS data parity broken — Property 40b29005-... referenced by docs has 0 rows; reservations, MVs, merchant accounts, intermediary connections all empty. Either the seed regressed post-2026-04-27 or was never re-run | 2, 4, 10, 11, 12 | curl QA /api/properties, /api/reservations, /api/reports/*, /api/merchant-accounts |
Section E — Action Plan
P0 = launch-blocking. P1 = must-fix-soon. P2 = nice-to-have. Effort: S = ≤1 day, M = 1–3 days, L = 1 week+, XL = >2 weeks.
P0 — must close before launch (47)
| ID | Title | Flows | Repo | Files | Effort | Acceptance |
|---|---|---|---|---|---|---|
| AP-T01-001 | Make tenantId optional on ResetPasswordRequestDto; resolve user by email | 1 | hmp-auth-service | src/auth/dto/login.dto.ts:52-62, src/auth/auth.service.ts:528+ | S | dashboard /forgot-password with {email} returns 200 + password.reset.requested event |
| AP-T01-002 | Subscribe notification dispatcher to TENANT_CREATED; render welcome + verify-email templates | 1 | hmp-notification-service | src/dispatcher/dispatcher.service.ts:96-139, src/templates/ | M | signup → email queued → received in MailHog (local) / SendGrid sandbox (QA) |
| AP-T01-003 | Allow ADMIN (not just SUPER_ADMIN) on OnboardingController, scoped to own tenant | 1 | hmp-auth-service | src/onboarding/onboarding.controller.ts:32,58,84,110,136 | S | tenant created via signup can call /onboarding/:tenantId/property with own JWT |
| AP-T01-004 | Add (auth)/forgot-password/page.tsx and /signup page in admin-portal | 1 | hmp-admin-portal | src/app/(auth)/ | M | full tenant self-onboarding reachable from public URL |
| AP-T02-001 | Add latitude, longitude, googleMapsUrl to Property DTOs + schema + service map | 2 | hmp-property-service | dto/property.dto.ts:60-219, properties.service.ts:71-80, prisma/schema.prisma | S | PUT /api/properties/:id {latitude,longitude,googleMapsUrl} round-trips |
| AP-T02-002 | Provision S3 (AWS) for QA + prod and MinIO sidecar for local; add AWS_S3_* env; add PROPERTY_IMAGE/ROOM_TYPE_IMAGE to AssetType | 2 | hmp-infrastructure, hmp-property-service | compose files, asset.service.ts:17 | M | POST /api/assets/upload-url returns 200 with presigned URL on local + QA |
| AP-T02-003 | Build dashboard image-upload widget (replace URL-paste); call /assets/process | 2 | hmp-web-dashboard | (dashboard)/settings/property/page.tsx:2989+, room-type page | M | drag-drop upload writes to S3, persists 3-variant URLs to Property.images[] |
| AP-T02-004 | Re-seed QA Property 40b29005-... (or update docs/MEMORY to point to existing properties) | 2, 4, 10, 11, 12 | hmp-infrastructure | seeds/seed-master.sh | S | doc + reality match; GET /api/properties/40b29005-... returns 200 with rooms/rate plans/taxes |
| AP-T03-001 | Add idempotencyKey to Reservation; enforce on POST /booking-engine/reservations and /bookings | 3 | hmp-reservation-service | prisma/schema.prisma:19-118, booking-engine.controller.ts | S | duplicate POST returns first reservation, not a second |
| AP-T03-002 | Wire DB-backed IApiKeyService in reservation-service; populate per-property hashed ApiKey rows; rotate the shared seed | 3 | hmp-reservation-service, hmp-infrastructure | app.module.ts, seeds/seed-keynova-properties.sql:122 | M | guard validates against DB; rotating one property's key does not affect others |
| AP-T04-001 | Re-run seed-master.sh against QA RDS for Property #1; verify list returns ≥1 row | 4 | hmp-infrastructure | seeds/seed-master.sh | S | GET /api/reservations?limit=3 on QA returns ≥1 row |
| AP-T04-002 | Decide: does Phase 1 require mobile reservations? If yes, scaffold; if no, document explicit deferral | 4, 6, 9 | hmp-mobile-apps, hmp-docs | apps/staff or new app | XL or S | written decision in Phase-1 readiness doc |
| AP-T05-001 | Fix rate recalc in reservations.service.update() to use per-night × new nights when departureDate changes | 5 | hmp-reservation-service | reservations.service.ts:1321-1336 | S | extending 3→5 nights produces totalAmount += 2×nightlyRate |
| AP-T05-002 | Post EXTENSION_NIGHT folio CHARGE for added nights when reservation is CHECKED_IN; emit FOLIO_CHARGE_POSTED | 5 | hmp-reservation-service, hmp-frontdesk-service | extend handler + folio service | M | folio balance reflects extra nights immediately, not at next NA |
| AP-T05-003 | Add Nginx /ws/housekeeping/ → housekeeping-service:3006 and /ws/kitchen/ → pos-service:3007 on QA + prod | 5, 7 | hmp-infrastructure | nginx/qa-nginx-hmp.conf:60-68 | S | wss://<host>/ws/housekeeping/socket.io/ returns 200 from outside cluster |
| AP-T06-001 | FRRO portal SOP runbook + future-state IndianFrroClient interface spec | 6 | hmp-docs | new compliance runbook | S | written runbook on submit-and-acknowledge; staff trained |
| AP-T06-002 | Replace idType=OTHER visa hack with Guest.visaNumber/visaType/visaExpiry columns or dedicated GuestVisa model | 6 | hmp-guest-service | prisma/schema.prisma:33-228 | M | visa records distinguishable from "OTHER ID" identity records |
| AP-T06-003 | Connect kiosk useCheckInFlow to real backend OR descope kiosk for Property #1 | 6 | hmp-kds-kiosk, hmp-docs | packages/kiosk/src/renderer/hooks/useCheckInFlow.ts:12-41 | L or S | kiosk either submits real check-in OR is removed from launch scope |
| AP-T06-004 | Build mobile staff frontdesk screen calling /check-in/:id OR descope mobile-staff for Property #1 | 6 | hmp-mobile-apps, hmp-docs | apps/staff/app/(tabs)/checkin.tsx:1-16 | L or S | screen submits real check-in OR descope is documented |
| AP-T07-001 | Rewrite KDS Electron app: target ${VITE_GATEWAY_URL}/kitchen namespace; pass JWT auth.token; subscribe to kot:created/kot:statusChanged/order:itemReady/order:updated; emit joinOutlet; remove VITE_USE_DEMO=true default | 7 | hmp-kds-kiosk | packages/kds/src/renderer/hooks/useOrders.ts:134-138, packages/shared/src/hooks/useWebSocket.ts:47-62 | M | KDS receives a real KOT in <2s after order entry on local + QA |
| AP-T07-002 | Wire kitchenGateway.emitKotCreated() into OrdersService.generateKOT after KOT row created | 7 | hmp-pos-service | orders.service.ts:509-573, kitchen.gateway.ts:264 | S | unit test asserts call; Playwright e2e covers "order entry → KDS card visible" |
| AP-T07-003 | Drop /pos/ prefix from mobile F&B posApi paths to match gateway routing | 7 | hmp-mobile-apps | packages/api-client/src/index.ts | S | mobile F&B order entry receives 200 from /api/orders |
| AP-T08-001 | Replace irn = 'Pending' literal with await jobRepo.getLatestForFolio(folio.id); render real IRN + base64 QR in PDF | 8 | hmp-frontdesk-service | src/folios/invoice-pdf.service.ts:520-545 | S | PDF prints real 64-char IRN + QR |
| AP-T08-002 | Add NIC creds env vars to staging-aws + prod-aws compose; flip EINVOICE_MOCK to env-driven; require false for prod gate | 8 | hmp-infrastructure | docker-compose.{staging-aws,prod-aws}.yml | S | prod build fails if EINVOICE_MOCK=true |
| AP-T08-003 | Wire GuestServiceClient.getCompany() + getGuestAddresses() into EInvoiceService.generateIrnForFolio (replace null company/guestAddress) | 8 | hmp-frontdesk-service | einvoice.service.ts:55-91 | M | B2B invoice with company GSTIN files as B2B, not URP |
| AP-T08-004 | On _checkOut commit, schedule GuestRetentionTimer row at checkOutDate+90d; cron calls gdpr.service.eraseGuest + writes GdprErasureLog | 8 | hmp-frontdesk-service, hmp-guest-service | checkout.service.ts:51-310, new cron in guest-service | M | DPDPA Section 8 timer runs nightly; old guest PII scrubbed |
| AP-T08-005 | Add GuestErasedConsumer to frontdesk, pos, housekeeping, analytics, crm, payment, notification | 8 | each service | new consumer files | M | erasure event scrubs Folio.guestName, Order.guestName, mv_*, Communication.metadata, Payment.metadata |
| AP-T09-001 | Extend rollback transactionCode filter to include OVERSTAY_NIGHT and NO_SHOW; add spec | 9 | hmp-frontdesk-service | night-audit.service.ts:910-916 | S | rollback voids all 3 transaction codes |
| AP-T09-002 | Wire analytics dispatcher subscription to night.audit.completed; add MV consumer; backfill via scripts/backfill-mv.ts | 9, 10 | hmp-analytics-service | consumers/event-dispatcher.service.ts:49-75, consumers/index.ts | M | a manual NA run refreshes daily-flash MV |
| AP-T10-001 | Wire MvAdrRevpar.adr and revpar on every upsert in reservation.consumer.ts; backfill historical rows | 10 | hmp-analytics-service | consumers/reservation.consumer.ts:91-107 | S | post-event ADR = totalRoomRevenue/roomsSold; RevPAR = totalRoomRevenue/totalRooms |
| AP-T10-002 | Make DashboardController.getMetrics query MvAdrRevpar for ADR/RevPAR/today-revenue (single source of truth) | 10 | hmp-analytics-service | dashboard.controller.ts:111-122, dashboard.service.ts | S | /dashboard/metrics and /reports/revenue/daily agree on the same number for the same date |
| AP-T10-003 | Investigate QA empty-MV root cause: confirm INTERNAL_SERVICE_TOKEN valid; confirm Redis Streams have producer messages; run scripts/backfill-mv.ts | 10 | hmp-analytics-service, hmp-infrastructure | scripts | M | /api/reports/occupancy returns ≥1 row on QA |
| AP-T10-004 | Fix mobile dashboardApi.getMetrics URL to /analytics/dashboard/metrics and inject X-Property-ID header | 10 | hmp-mobile-apps | packages/api-client/src/dashboardApi.ts:7 | S | manager-app dashboard receives 200 with metrics object |
| AP-T10-005 | Build GSTR-3B prep endpoint aggregating EventStore einvoice payloads by HSN/PoS/B2B-vs-B2C; add controller behind /gst gateway prefix | 10 | hmp-analytics-service | new gst/gstr3b.controller.ts | M | GET /reports/gstr3b?month=2026-04 returns valid aggregate JSON |
| AP-T11-001 | Add @All('intermediary'), @All('intermediary/*'), @All('channel-stats'), @All('channel-stats/*'), @All('channel-setup'), @All('channel-setup/*'), @All('webhook-management'), @All('webhook-management/*') decorators | 11 | hmp-api-gateway | src/proxy/proxy.controller.ts | S | curl /api/intermediary/connections reaches channel-service |
| AP-T11-002 | Provision real ResAvenue connection row for Property #1 via admin UI; document in launch runbook; set CHANNEL_MODE=sandbox (or live) on QA | 11 | hmp-channel-service, hmp-infrastructure, hmp-docs | manual seed + runbook | M | rate push to ResAvenue sandbox returns 200 with provider booking ID |
| AP-T11-003 | Register outbound webhook URL with ResAvenue (https://<qa-host>/api/webhooks/resavenue/{reservation,cancellation}); add to launch checklist | 11 | hmp-docs | runbook | S | inbound test booking from ResAvenue sandbox creates a Reservation row |
| AP-T12-001 | Seed MerchantAccount for tenant 1 on QA with Razorpay sandbox creds (encrypted at rest) | 12 | hmp-payment-service, hmp-infrastructure | seed | S | GET /api/merchant-accounts → {data:[{...}]} |
| AP-T12-002 | Wire admin onboarding wizard → POST /api/merchant-accounts to encrypt and persist Razorpay keys per tenant | 12 | hmp-admin-portal, hmp-payment-service | new wizard step | M | tenant onboarding includes payment-provider step that creates encrypted MerchantAccount |
| AP-T13-001 | Add await this.propertyClient.assertPropertyTenant(propertyId, tenantId) to CalendarService.getCalendar and createQuickReservation | 13 | hmp-frontdesk-service | calendar.service.ts:81-99,159-170 | S | cross-tenant propertyId returns 403, not data |
| AP-T13-002 | Wire FilterChipsRow to local state → query params → backend filter (extend getCalendar with roomTypeId/floor/source/vipOnly) OR delete cosmetic chips | 13 | hmp-web-dashboard, hmp-frontdesk-service | tape-chart-shell.tsx:120-157, calendar.controller.ts | M | clicking a chip filters the calendar |
| AP-CC-1 | Audit all gateway prefixes for two-file-rule compliance; add CI check that fails build if prefix exists in services.config.ts but not as @All decorator (or vice versa) | all | hmp-api-gateway | new CI script | S | CI script lists all unmatched prefixes |
| AP-CC-2 | Reseed QA RDS via seed-master.sh; verify reservations, MVs, merchant accounts, intermediary connections, ApiKey rows all populated | 2,4,10,11,12 | hmp-infrastructure | seeds/seed-master.sh | S | smoke test passes 13/0/0 on QA |
| AP-CC-3 | RS256 migration for all WebSocket gateways before prod (/housekeeping, /kitchen, /events) — load JWT_PUBLIC_KEY env, fail-closed on HS256 in prod | 5, 7, 13 | hmp-housekeeping-service, hmp-pos-service, hmp-api-gateway | gateway files | M | prod boot fails if RS256 keys missing |
| AP-CC-4 | Sync compose files: remove EINVOICE_MOCK=true and HARDWARE_MOCK=true from prod-aws; add NIC + Razorpay + ResAvenue + SendGrid env placeholders to QA + prod | 7,8,11,12 | hmp-infrastructure | compose files | S | grep _MOCK docker-compose.prod-aws.yml returns 0 |
| AP-CC-5 | Provision SendGrid creds across QA + prod compose; e2e test for invoice email delivery | 1, 8 | hmp-infrastructure, hmp-notification-service | compose + spec | S | invoice email arrives in SendGrid sandbox inbox |
P1 — must-fix-soon (41)
Selected highlights:
| ID | Title | Flows | Effort |
|---|---|---|---|
| AP-T01-005 | Expose Public GET /billing/plans (currently 401) | 1 | S |
| AP-T01-007 | Decide on OnboardingService xPrisma deprecation — delete or migrate to PropertyServiceClient | 1 | M |
| AP-T02-005 | Add inclusive Boolean column to TaxConfiguration | 2 | S |
| AP-T02-006 | Add structured class-validator schema for CancellationPolicy.rules[] | 2 | S |
| AP-T03-003 | Decide on theme strategy — implement KeyNova brand (Iris/Nova) OR document direction-a/b as official | 3 | S |
| AP-T03-004 | Verify Razorpay creds + webhook URL on QA + prod and document | 3, 12 | S |
| AP-T04-003 | Wire guestName in search() via GuestServiceClient.findByName OR strip the input | 4 | S |
| AP-T04-004 | Replace client-side FolioTable computation with rows from res.folios[].transactions[] | 4 | M |
| AP-T05-004 | Add "Move Room" + "Extend Stay" CTA buttons on /reservations/:id (currently only via tape chart) | 5 | S |
| AP-T05-005 | Consolidate RoomOpsService.roomMove and CheckInService.roomMove into one impl with pricing safeguard | 5 | M |
| AP-T05-006 | Post folio CHARGE for paid-upgrade rate-difference inside the room-move tx | 5 | S |
| AP-T06-005 | Switch dashboard ID-photo + signature uploads from base64 to /uploads/presigned-url → S3 | 6 | M |
| AP-T06-009 | Boot-time check: in NODE_ENV=production, fail fast if ENCRYPTION_KEY is unset | 6 | S |
| AP-T07-004 | Extend FOLIO_CHARGE_POSTED payload to carry CGST/SGST/IGST/customerState; consumer writes distinct rows | 7 | M |
| AP-T07-005 | Introduce KitchenStation model + per-item routing + per-station broadcast rooms | 7 | L |
| AP-T07-006 | Map transactionCode from orderType (ROOM_SERVICE/CANTEEN/PREORDER/FnB) | 7 | S |
| AP-T08-006 | Promote irn/signedQRCode/ackDate columns onto Folio | 8 | S |
| AP-T08-007 | Route credit-note IRN through EInvoiceJob queue with retry/DLQ | 8 | S |
| AP-T08-009 | Register NIC webhook callback URL during sandbox onboarding | 8 | S |
| AP-T09-003 | Implement night-audit-summary PDF/CSV generator | 9 | M |
| AP-T09-004 | Add typed currentBusinessDate Date? column on Property; migrate from JSON | 9 | M |
| AP-T09-005 | Promote NA scheduler to k8s CronJob with miss-detection alert | 9 | S |
| AP-T10-006 | Subscribe to reservation.modified, rename folio.charge.posted→folio.posted (or align producer), add folio.closed/kot.completed consumers | 10 | M |
| AP-T10-007 | Replace /reports/[category] fallback list with backend catalog only | 10 | S |
| AP-T10-008 | Replace admin /analytics demo data with GET /analytics/admin/platform-overview | 10 | M |
| AP-T10-009 | Fix MvDailyOccupancy.totalRooms upsert to read from PropertyServiceClient | 10 | S |
| AP-T11-004 | Implement MinLOS/MaxLOS push (OTA_HotelBookingRuleNotifRQ) | 11 | M |
| AP-T11-005 | Add flow-tests/flow-11-channel-manager.spec.ts end-to-end | 11 | M |
| AP-T11-006 | Set CHANNEL_MODE=simulator in docker-compose.qa-aws.yml until live creds land | 11 | S |
| AP-T12-003 | Add payment_capture: 1 to Razorpay order body | 12 | S |
| AP-T12-004 | Build WebhookReconcileService cron (every 15 min) | 12 | M |
| AP-T12-005 | Override HARDWARE_MOCK=false in docker-compose.prod-aws.yml | 12 | S |
| AP-T13-003 | Implement multi-room reservation visual continuity on tape chart | 13 | M |
| AP-T13-004 | Add mark-no-show to BAR_ACTIONS | 13 | S |
| AP-T13-005 | Replace window.location.href = /reservations/:id redirect with in-place dialog for extend/shorten | 13 | S |
| AP-T13-006 | Add date-range block UI (RoomBlock model or HK MaintenanceRequest) | 13 | M |
P2 — nice-to-have (39, abbreviated)
Includes Google Maps geocoding, amenity master catalog, multi-property booking-engine selector, multi-guest C-Form per foreign co-traveller, NLQ wiring, MV nightly self-heal, ESC/POS receipt printing in checkout, bulk-block toolbar, mobile responsive toolbar, drag/drop Playwright e2e, root CLAUDE.md ↔ per-folder CLAUDE.md drift cleanup, frroCity column, RatePlanRate seed back-fill.
Section F — Sequenced Fix Plan
Wave 1 — independent P0s parallelizable across teams (≈5 person-days each, run concurrently for ~1 week wall-clock):
- AP-T01-001 (forgot-password DTO)
- AP-T01-002 (welcome email consumer)
- AP-T01-003 (OnboardingController role)
- AP-T02-001 (lat/lng/googleMapsUrl DTO)
- AP-T02-004 / AP-CC-2 (re-seed QA — unblocks T04, T10, T11, T12)
- AP-T03-001 (booking idempotency)
- AP-T05-001 (extend rate recalc)
- AP-T05-002 (extend folio charge)
- AP-T05-003 (Nginx /ws routes)
- AP-T07-002 (
emitKotCreated) - AP-T07-003 (drop /pos/ prefix in mobile)
- AP-T08-001 (PDF IRN literal)
- AP-T09-001 (rollback transactionCode filter)
- AP-T10-001 (ADR/RevPAR upsert)
- AP-T10-002 (single source of truth for metrics)
- AP-T10-004 (mobile dashboardApi URL)
- AP-T11-001 (gateway
@Alldecorators for intermediary etc.) - AP-T13-001 (CalendarService tenant-assert)
- AP-T13-004 (no-show action)
- AP-CC-4 (compose env hygiene)
Wave 2 — P0s that depend on Wave 1 (≈2 weeks wall-clock):
- AP-T01-004 (admin-portal /signup + /forgot-password pages — needs T01-001 + T01-002)
- AP-T02-002 (S3 provisioning — unblocks AP-T02-003 and AP-T06-005)
- AP-T02-003 (image upload widget — needs S3)
- AP-T03-002 (DB-backed API key — needs idempotency in place)
- AP-T06-002 (visa columns — needs schema migration coordination)
- AP-T06-003 / AP-T06-004 (kiosk + mobile staff — DECISION: build OR descope)
- AP-T07-001 (KDS Electron rewrite — needs
emitKotCreated) - AP-T08-002 (NIC creds, EINVOICE_MOCK=false in prod — needs T08-001 PDF first to render the real IRN)
- AP-T08-003 (B2B IRN cross-service include)
- AP-T08-004 (90-day retention timer)
- AP-T08-005 (GUEST_ERASED consumers)
- AP-T09-002 (analytics consumer for night.audit.completed)
- AP-T10-003 (QA MV root cause)
- AP-T10-005 (GSTR-3B endpoint)
- AP-T11-002 (ResAvenue connection seed — needs T11-001 gateway routing)
- AP-T11-003 (webhook URL registration)
- AP-T12-001 (MerchantAccount seed)
- AP-T12-002 (admin onboarding payment-provider step)
- AP-T13-002 (filter chips wiring)
- AP-CC-1 (CI two-file-rule check)
- AP-CC-3 (RS256 migration for WS gateways)
- AP-CC-5 (SendGrid)
After Wave 1 + Wave 2 complete, every P0 in Section E is closed.
Wave 3 — P1 (~3 weeks): the 41 P1 items above. Recommended grouping by team owner.
Wave 4 — P2 (post-launch backlog): 39 items.
Estimated total wall-clock to GO: 6-8 weeks at current team capacity.
Section G — What could not be verified
| ID | Item | Why |
|---|---|---|
| CNV-1 | SSH inspection of AWS QA EC2 | Sandbox blocks SSH to shared host. All QA verification was HTTP-only. |
| CNV-2 | QA .env.qa-aws env var contents (Razorpay/NIC/ResAvenue/SendGrid/INTERNAL_SERVICE_TOKEN) | requires SSH; could not read |
| CNV-3 | QA RDS schema diff vs schema.prisma on master | requires DB connection (no psql to QA RDS without SSH) |
| CNV-4 | Whether the Razorpay webhook URL is registered at the Razorpay merchant dashboard | requires Razorpay dashboard access |
| CNV-5 | Whether the ResAvenue webhook URL is registered at ResAvenue | requires ResAvenue dashboard access |
| CNV-6 | NIC IRP sandbox onboarding status (GSTIN registered?) | requires NIC portal access |
| CNV-7 | Real Pinelabs / EDC terminal hardware test | requires physical terminal device |
| CNV-8 | Real mobile-device boot test (housekeeping app, F&B app, manager app, staff app, guest app) | sandbox cannot run physical Android/iOS device |
| CNV-9 | Local stack browser smoke test (clicking through tape-chart drag, check-in wizard, tape-chart calendar) | sandbox does not have an active browser session against localhost:3020 |
| CNV-10 | KDS kiosk Electron app live boot | sandbox cannot run Electron in interactive mode |
| CNV-11 | Whether flow-results.json 102/102 still passes today after the 2026-04-28 audit changes | not re-run during this audit (audit-only mode) |
| CNV-12 | Whether the local stack login fails because the local seed used admin@hmphotel.com (memory says yes; Flow 9 audit confirms) — exact root-cause | requires re-running seed-master locally; deferred |
| CNV-13 | Booking-engine end-to-end on local Docker (curl returned HTTP 000 from one subagent's shell) | shell-context limitation; not necessarily broken — Flow 3 audit notes this |
| CNV-14 | ENCRYPTION_KEY presence on QA frontdesk container | requires SSH |
Section H — Methodology + Provenance
Audit run: 2026-04-28, 14:00–15:30 IST.
Method:
- Bootstrap pass — git status across 23 repos; local Docker
docker ps; QA HTTP probe. - 13 parallel general-purpose subagents (one per business flow) each running the 7-check protocol on local + QA.
- Aggregation pass — collated 13 finding blocks; deduplicated cross-cutting patterns; produced action plan + sequenced fix plan.
Tools used: Read, Grep, Glob, Bash (curl + docker ps + python parsing), Agent.
Repo SHAs at time of audit (all on master):
| Repo | SHA | Dirty files |
|---|---|---|
| hmp-api-gateway | 54c7e29 | 0 |
| hmp-auth-service | 32c787f | 0 |
| hmp-property-service | 69f0056 | 0 |
| hmp-reservation-service | ec4eb1f | 0 |
| hmp-guest-service | 27e0174 | 0 |
| hmp-frontdesk-service | 7e26105 | 0 |
| hmp-housekeeping-service | 4e59e0b | 0 |
| hmp-pos-service | 1c4de5e | 0 |
| hmp-banquet-service | 058b8ac | 0 |
| hmp-channel-service | 047ee7d | 0 |
| hmp-crm-service | 27502b9 | 0 |
| hmp-analytics-service | b013635 | 0 |
| hmp-notification-service | 16640a5 | 0 |
| hmp-payment-service | cc4beeb | 0 |
| hmp-spa-service | 1b5a9d0 | 0 |
| hmp-web-dashboard | b50f445 | 7 |
| hmp-admin-portal | 877b24b | 0 |
| hmp-booking-engine | 8bee09c | 0 |
| hmp-mobile-apps | d06bbae | 1 |
| hmp-kds-kiosk | d0328bf | 1 |
| hmp-shared-packages | 69a09a6 | 0 |
| hmp-infrastructure | 87a4046 | 19 |
| hmp-docs | cd8fba1 | 9 |
Local Docker compose state (2026-04-28 15:10 IST):
- All 14 backend services healthy except
hmp-payment-service-1(unhealthy — known IPv4-bind issue, see Workstream A memory). - All 5 frontends healthy.
- Per-service postgres containers all healthy.
AWS QA state (HTTP-probed; no SSH):
GET /api/health→ 200.POST /api/auth/login→ 200 withadmin@hmphotel.com / <REDACTED>(tenant00000000-0000-4000-a000-000000000001, property40b29005-5196-4ba7-a71d-94323caee4dc).GET /api/properties/40b29005-5196-4ba7-a71d-94323caee4dc→ 404 (Flow 2 finding).GET /api/reservations?limit=3→ 200 with{data:[],total:0}(Flow 4 finding).
Subagent invocation log:
| Flow | Files inspected (approx) | Curls run | Subagent token usage | Wall time |
|---|---|---|---|---|
| 1 — Tenant Onboarding | 25 | 8 | 114k | 11 min |
| 2 — Property Onboarding | 30 | 10 | 102k | 11 min |
| 3 — Booking Engine | 22 | 0 (shell HTTP 000) | 95k | 5 min |
| 4 — Staff Reservations | 18 | 5 | 122k | 7 min |
| 5 — Frontdesk / HK | 30 | 8 | 160k | 12 min |
| 6 — Check-In | 28 | 4 | 144k | 11 min |
| 7 — Room Service / KDS / Folio | 30 | 6 | 164k | 7 min |
| 8 — Check-Out + e-Invoice | 25 | 5 | 134k | 9 min |
| 9 — Night Audit | 18 | 4 | 112k | 6 min |
| 10 — Reports | 30 | 14 | 157k | 9 min |
| 11 — Channel Manager | 22 | 6 | 100k | 6 min |
| 12 — Payments | 28 | 8 | 144k | 7 min |
| 13 — Tape Chart | 25 | 0 | 131k | 5 min |
End of report.