Skip to main content

EP-002 — Property & Room Management RTM

EPIC: EP-002 Property & Room Management Reconciled: 2026-05-24 Pilot artifact — see _scratch/source-map.md (deleted at Task 11) for derivation notes and ./PLAYBOOK.md for the pattern. Source of truth for "% passing": hmp-docs/docs/platform-registry/2026-05-16-13-flow-demo-readiness.md until Task #12 re-measures.

Status legend​

StatusMeaning
BuiltCode exists + tests exist + tests pass
Built-UntestedCode exists, no automated test coverage (or test exists but is a stub)
PartialCode exists for some acceptance criteria, gaps for others
StubCode is a placeholder / TODO / returns mock data
Not StartedNo code

Summary​

MetricCount
Total stories14
Catalog claims "Built"9
Verified-Built7 (US-002-001, -002, -003, -004, -005, -006, -014)
Built-Untested1 (US-002-010 — covered only implicitly via rooms tests)
Partial4 (US-002-007, -008, -009, -013)
Stub1 (US-002-011 — uses PATCH /rooms/:id/status with no dedicated controller / no reason taxonomy)
Not Started1 (US-002-012)
Drift findings14 (D-001..D-014; 5 P0 / 7 P1 / 2 P2)
[SME-PROPOSED] refinements30

US-002-001 — Property Create & Configuration​

Catalog status: Built (P0, 3 points) Verified status: Built

Code locations:

  • hmp-property-service/src/properties/properties.service.ts — Properties domain service
  • hmp-property-service/src/properties/properties.controller.ts:36-275 — POST /, GET /, GET /search, POST /search, GET /feature-gates, GET /current, GET /:id, PUT /:id, PATCH /:id/onboarding, PATCH /:id/theme, POST /:id/activate, PATCH /:id/business-date, DELETE /:id
  • hmp-property-service/src/properties/dto/property.dto.ts — CreatePropertyDto, UpdatePropertyDto with GSTIN + PAN regex + lat/lng/googleMapsUrl (AP-T02-001)
  • hmp-property-service/src/properties/dto/set-business-date.dto.ts — Business date DTO (AP-T09-004)
  • hmp-property-service/src/properties/dto/search-properties.dto.ts — Search DTO
  • hmp-property-service/src/properties/dto/property-policies.dto.ts — Policies DTO
  • hmp-web-dashboard/src/app/(dashboard)/settings/property/page.tsx — Property settings page
  • hmp-web-dashboard/src/app/properties/page.tsx — Properties landing

Unit + integration tests:

  • hmp-property-service/src/properties/properties.service.spec.ts — 25 tests covering CRUD + AP-T02-001 lat/lng round-trip + AP-T09-004 business-date
  • hmp-property-service/src/properties/properties.controller.spec.ts — 12 controller tests
  • hmp-property-service/src/properties/dto/property.dto.spec.ts — 11 GSTIN/PAN validator tests
  • hmp-property-service/src/properties/__tests__/properties.search.spec.ts — Search filter coverage
  • hmp-property-service/src/properties/__tests__/property-cross-tenant-denial.spec.ts — Tenant isolation
  • hmp-property-service/src/properties/property-create-no-tenant-stub.spec.ts — Tenant stub auto-creation
  • hmp-property-service/src/property-review.spec.ts — Composite property-flow assertions

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/flow-1-tenant-onboarding.spec.ts :: "S3 — POST /api/properties" — PARTIAL (5/10 overall; S2a/S3/S4 currently FAIL on stale read model) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/flow-2-property-onboarding.spec.ts :: "S1 — PUT /properties/:id with name/address/coords" — PASS (7/7) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/tenant-onboarding-full.flow.spec.ts :: "Scenario 3 — property create" — TBD (not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • hmp-web-dashboard/e2e/real-backend/smoke-property-setup.real.spec.ts — covers /settings/property loading with seeded GSTIN/timezone
  • hmp-web-dashboard/e2e/real-backend/smoke-taxes.real.spec.ts — covers tax configurations on property

E2E (Playwright):

  • hmp-web-dashboard/e2e/sprint1-property.spec.ts — sprint 1 property smoke
  • hmp-web-dashboard/e2e/onboarding-wizard.spec.ts — full setup wizard UI
  • hmp-web-dashboard/e2e/tests/settings-property.spec.ts — property settings page

Use cases referencing this story:

  • docs/requirements/use-cases/uc-property-address-maps.yaml (AP-T02-001 — lat/lng/googleMapsUrl)
  • docs/requirements/use-cases/uc-property-business-date.yaml (AP-T09-004 — currentBusinessDate PATCH for night audit)
  • docs/requirements/use-cases/uc-property-image-upload.yaml (AP-T02-002 + AP-T02-003 — S3 presigned, dashboard ImageUploadWidget)

SME notes:

  • GSTIN regex per AP-T02-001 enforces the canonical 15-char format ^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$ — covers the per-state prefix (01–37), PAN-embedded slice, entity-number, default Z literal, and checksum. PAN regex enforces ^[A-Z]{5}[0-9]{4}[A-Z]$. Spec stories should make these explicit, not "GSTIN format validation" by hand-wave.
  • 4 orphaned endpoints not referenced by US-002-001 story: PATCH /:id/theme (branding — BF-001 D-005 catalog gap), POST /:id/activate (onboarding lifecycle — belongs in US-002-009), PATCH /:id/business-date (night-audit op — belongs in EP-005C), PATCH /:id/onboarding (onboarding step-progress — belongs in US-002-009). Each is shipped + tested but the story body still describes a simple CRUD surface.
  • currentBusinessDate (AP-T09-004) is a hotel-industry-specific concept: the business date can differ from calendar date during the night-audit window. The story body does not explain this; new engineers misread it as a redundant createdAt. Surface as Day-2 doc gap.

Drift findings:

D-001 — [P1] [Test-vs-code] BF-001 D-001 — flow-1-tenant-onboarding.spec.ts S2a/S3/S4 fail (5/10 overall) because the property list read model is stale after TENANT_CREATED Kafka propagation. The race is between POST /api/properties and read-model-sync.service.ts consuming the event. Property create itself succeeds, but the immediate GET /api/properties returns an empty list.

  • [SME-PROPOSED] Add AC: "After POST /api/properties returns 201, the subsequent GET /api/properties for the same tenant SHALL include the newly-created property within 500 ms p95 (read-after-write consistency)."

D-002 — [P2] [Orphaned-code] 4 PATCH endpoints (/theme, /business-date, /onboarding) and 1 POST (/activate) live on PropertiesController but are not referenced by US-002-001 acceptance criteria. They semantically belong to US-002-NEW-BRANDING (BF-001 D-005), US-002-009 (onboarding lifecycle), and EP-005C (night audit). Today: a new engineer modifying US-002-001 will not know they're touching three other domains.

  • [SME-PROPOSED] Split US-002-001 into US-002-001a (Property CRUD — core fields) and reassign /theme to US-002-NEW-BRANDING, /activate + /onboarding to US-002-009, /business-date to EP-005C. Or, document the four endpoints under US-002-001 as "Property lifecycle ops" sub-section.

D-003 — [P1] [Story-incomplete] Story body says "GSTIN format validation" without specifying the canonical 15-char regex or the state-prefix table (01..37). DTO + spec enforce it but the story is hand-wavy.

  • [SME-PROPOSED] Add AC: "GSTIN SHALL match regex ^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$. The first two digits (state code 01–37) SHALL be cross-validated against the property's address state. PAN SHALL match ^[A-Z]{5}[0-9]{4}[A-Z]$. Both SHALL be validated by class-validator at DTO boundary."

US-002-002 — Room Type CRUD with Amenities & Photos​

Catalog status: Built (P0, 5 points) Verified status: Built

Code locations:

  • hmp-property-service/src/room-types/room-types.service.ts — Room types domain service
  • hmp-property-service/src/room-types/room-types.controller.ts:27-106 — POST /room-types, GET /room-types, GET /properties/:propertyId/room-types, GET /room-types/:id, PUT /room-types/:id, POST /room-types/:id/duplicate, DELETE /room-types/:id
  • hmp-property-service/src/room-types/dto/room-type.dto.ts — Create/Update DTOs (code-format constraints)
  • hmp-web-dashboard/src/app/(dashboard)/settings/room-types/page.tsx — Room types management page

Unit + integration tests:

  • hmp-property-service/src/room-types/room-types.service.spec.ts — 20 service tests
  • hmp-property-service/src/room-types/room-types.controller.spec.ts — 13 controller tests
  • hmp-property-service/src/room-types/dto/room-type.dto.spec.ts — 10 DTO/code-format tests

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/flow-2-property-onboarding.spec.ts :: "S3 — POST /room-types" — PASS (7/7) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/tenant-onboarding-full.flow.spec.ts :: "Scenario 4 — room types" — TBD (not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • hmp-web-dashboard/e2e/real-backend/smoke-settings-roomtype.real.spec.ts — room types page loads with seed

E2E (Playwright):

  • hmp-web-dashboard/e2e/tests/settings-room-types.spec.ts — room types CRUD

Use cases referencing this story:

  • none

SME notes:

  • Property existence check on create shipped per implementation report — story body doesn't enumerate it.
  • POST /room-types/:id/duplicate endpoint exists but story body doesn't reference it. Duplicate semantics (cascades photos? rates? amenities?) are undocumented.
  • Amenities and photos are mentioned in the title but not in the AC body — the 10 DTO tests cover code format only; amenity shape (free-form string[] today) is unconstrained. New OTAs (e.g. GoMMT) require a canonical amenity taxonomy (smoking, AC, WiFi, etc.) to map to their fields — today the SME stores free-text and channel-service has to fuzzy-match.

Drift findings:

D-004 — [P1] [Story-incomplete + integration-risk] Amenity field is free-form string[] with no canonical taxonomy. Channel-service (OTA sync) must fuzzy-match into Booking.com / Expedia / GoMMT amenity codes. Risk: rate-parity audits will flag mismatched amenities.

  • [SME-PROPOSED] Add AC: "Amenities SHALL be drawn from a canonical taxonomy (≥40 codes covering OTA-required + India-specific items: smoking, AC, WiFi, breakfast, prayer mat, kettle, geyser, mini-fridge, balcony, sea-view, etc.). Free-text "custom amenities" allowed but flagged in OTA-sync warnings."
  • [SME-PROPOSED] Add AC for POST /:id/duplicate: "Duplicating a room type SHALL copy amenities + photos + max-occupancy + bed-type but NOT rates (rates linked to rate plans, not type)."

US-002-003 — Room Inventory Management (Add/Edit/Delete Rooms)​

Catalog status: Built (P0, 3 points) Verified status: Built

Code locations:

  • hmp-property-service/src/rooms/rooms.service.ts — Rooms domain service
  • hmp-property-service/src/rooms/rooms.controller.ts:37-233 — POST /rooms/bulk, POST /rooms, GET /rooms, GET /properties/:propertyId/rooms, GET /properties/:propertyId/rooms/summary, GET /rooms/:id, PUT /rooms/:id, PATCH /rooms/:id/notes, PATCH /rooms/:id/status, DELETE /rooms/:id. Also bed-map endpoints (/bed-map/summary, /bed-map, /beds/:id/assign)
  • hmp-property-service/src/rooms/dto/* — CreateRoomDto, UpdateRoomDto, BulkCreateRoomsDto, UpdateRoomStatusDto, UpdateRoomNotesDto
  • hmp-web-dashboard/src/app/(dashboard)/settings/rooms/page.tsx — Rooms management page

Unit + integration tests:

  • hmp-property-service/src/rooms/rooms.service.spec.ts — 18 service tests (covers isActive filter, isOccupied auto-sync, FK validation)
  • hmp-property-service/src/rooms/rooms.controller.spec.ts — 14 controller tests

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/flow-2-property-onboarding.spec.ts :: "S4 — POST /rooms/bulk" — PASS (7/7) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/rooms-crud.flow.spec.ts :: "Full CRUD lifecycle on canary property" — TBD (P-GAP-1, not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/tenant-onboarding-full.flow.spec.ts :: "Scenario 4 — 10 rooms" — TBD (not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • none specific (rooms exercised via property/room-type smokes + settings-rooms.spec.ts E2E)

E2E (Playwright):

  • hmp-web-dashboard/e2e/tests/settings-rooms.spec.ts — rooms CRUD
  • hmp-web-dashboard/e2e/sprint1-property.spec.ts — rooms smoke

Use cases referencing this story:

  • none

SME notes:

  • Bed-map endpoints (/bed-map/summary, /bed-map, /beds/:id/assign) live on RoomsController but are not part of US-002-003 ACs. They are pre-work for dorm / hostel inventory which is otherwise unrepresented in the catalog.
  • POST /rooms/bulk is the seed-friendly path but story does not document the max batch size or the partial-failure semantics (does row 50 failing roll back rows 1–49? today: yes, single transaction).
  • Soft-delete via isActive=false (per implementation report) is shipped but story says "Delete" — clarify hard-delete vs soft-delete and the cascade behaviour (linked reservations, bookings, folios).

Drift findings:

D-005 — [P2] [Orphaned-code] Bed-map endpoints (3 routes) live on RoomsController but no US-002-NNN story owns them. They are pre-work for dorm/hostel inventory which is not catalogued. New engineers will not know which story to update when modifying bed-map behaviour.

  • [SME-PROPOSED] Author a new story US-002-NEW-BED-MAP "Per-room bed inventory & assignment (dorm/hostel mode)" or attach the bed-map endpoints as a sub-section of US-002-003 with explicit ACs.

US-002-004 — Room Status Lifecycle Management​

Catalog status: Built (P0, 3 points) Verified status: Built

Code locations:

  • hmp-property-service/src/rooms/rooms.controller.ts:149 — PATCH /rooms/:id/status (writes RoomStatusLog)
  • hmp-property-service/src/rooms/rooms.service.ts — updateStatus(), getRoomStatusSummary()
  • hmp-housekeeping-service/src/rooms/* — Housekeeping-side PATCH /rooms/:id/status, POST /rooms/:id/inspection, GET /rooms/discrepancy (controller path [CITED-BUT-NOT-FOUND] — specific file not enumerated in source map)
  • hmp-frontdesk-service/src/checkin/checkin.service.ts — Check-in cascade sets room status + emits Kafka events
  • hmp-frontdesk-service/src/checkout/checkout.service.ts — Check-out cascade sets room status + emits Kafka events
  • hmp-web-dashboard/src/components/.../room-grid.tsx [CITED-BUT-NOT-FOUND] — front-desk room grid
  • hmp-web-dashboard/src/components/.../room-status-board.tsx [CITED-BUT-NOT-FOUND] — housekeeping status board

Unit + integration tests:

  • hmp-property-service/src/rooms/rooms.service.spec.ts — updateStatus() and getRoomStatusSummary() coverage (part of the 18-test suite)
  • hmp-frontdesk-service/src/checkin/checkin.service.spec.ts — check-in cascade

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/housekeeping-lifecycle.flow.spec.ts :: "HK status transitions" — TBD (not enumerated in 13-flow doc with explicit pass count) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/flow-5-fd-hk-updates.spec.ts :: "room move/extend/HK lifecycle" — 11/12 PASS (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/flow-6-check-in.spec.ts :: "check-in cascade" — 6/13 PASS (Aadhaar/C-Form pending) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • none specific

Use cases referencing this story:

  • none

SME notes:

  • Three services write the room status (property, frontdesk, housekeeping) and there is no central state-machine validator — the implementation report explicitly defers "Room status transition validation" as LOW severity. Real-world risk: a housekeeping update could set status CLEAN while frontdesk has the room mid-check-out — last-writer-wins.
  • Status transition rules (Inspected → Dirty after check-out, Dirty → Cleaning, Cleaning → Clean, Clean → Inspected → AVAILABLE) are encoded in code but not in the story body.
  • RoomStatusLog write captures the cascade audit but the story does not require it. If a future refactor drops the log row, no test will fail.

Drift findings:

D-006 — [P0] [Cross-service-handoff + missing-validation] Three services (property/frontdesk/housekeeping) can independently PATCH /rooms/:id/status with no central state-machine guard. Last-write-wins is documented as deferred per the EP-002 implementation report, but room-status integrity is BF-001-critical for housekeeping discrepancy reports and night audit. A check-out cascade overlapping with a HK "Dirty → Cleaning" PATCH can leave the room in an inconsistent state across services.

  • [SME-PROPOSED] Add AC: "Room status transitions SHALL be validated against an explicit state machine (e.g. AVAILABLE → OCCUPIED requires active reservation, OCCUPIED → CLEANING requires checkout, DIRTY → CLEAN requires HK actor). Invalid transitions return 409 with code=ROOM_STATUS_INVALID_TRANSITION."
  • [SME-PROPOSED] Add AC: "Every status change SHALL write a RoomStatusLog row with (propertyId, roomId, fromStatus, toStatus, actorService, actorUserId, reason, at). Test asserts the row is written on every cascade path."

US-002-005 — Rate Plan CRUD (BAR, Corporate, Seasonal)​

Catalog status: Built (P0, 5 points) Verified status: Built

Note: EP-002 numbering drift — catalog title (Rate Plan CRUD) ≠ EPIC body title (Room Status Summary Dashboard). The existing review file reviews/EP-002/US-002-005-review.md matches the catalog title. The source map records both interpretations; this RTM follows the catalog per task instructions. Task 6 P0 drift candidate.

Code locations:

  • hmp-property-service/src/rate-plans/rate-plans.service.ts — Rate plans domain service
  • hmp-property-service/src/rate-plans/rate-plans.controller.ts:33-153 — POST/GET/PUT/DELETE /rate-plans, GET /properties/:propertyId/rate-plans, GET/PUT /rate-plans/:id/rates, POST/GET /rate-plans/:id/restrictions
  • hmp-property-service/src/rate-plans/dto/* — Rate plan DTOs
  • hmp-web-dashboard/src/app/(dashboard)/settings/rate-plans/page.tsx — Rate plans page

Unit + integration tests:

  • hmp-property-service/src/rate-plans/rate-plans.controller.spec.ts — 14 controller tests
  • hmp-property-service/src/rate-plans/rate-plans.tenant-guard.spec.ts — Tenant guard coverage

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/flow-2-property-onboarding.spec.ts :: "S5 — rate plans" — PASS (7/7) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/property-settings.flow.spec.ts :: "Scenarios 1 and 5 — rate plan create + list-roundtrip" — TBD (FT-2, not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • hmp-web-dashboard/e2e/real-backend/smoke-rate-plans.real.spec.ts — rate plans page loads with seed

E2E (Playwright):

  • hmp-web-dashboard/e2e/tests/settings-rate-plans.spec.ts — rate plans

Use cases referencing this story:

  • none

SME notes:

  • Catalog vs EPIC-body numbering drift (D-007 below) is a P0 reconciliation item: the catalog says US-002-005 = "Rate Plan CRUD" but the EPIC body says US-002-005 = "Room Status Summary Dashboard". Same shift through US-002-014. Review files follow the catalog/review hybrid. This must be settled before any downstream pilot uses the same story IDs.
  • Rate plans hold per-day rates and restrictions but the story does not mention GST-inclusive vs GST-exclusive pricing, which is the most important pricing decision an Indian hotel makes. Today: handled in tax module (AP-T02-005 TaxConfiguration.inclusive flag) but the rate-plan story is silent. Risk: a rate plan can be marked "₹4,000 inclusive" while the linked tax config flag is exclusive, producing a 12% miscalculation on every booking.
  • Indian hospitality tax stack: CGST+SGST (intra-state) vs IGST (inter-state) depends on the guest's billing state vs the property's state. Rate plan today does not capture this — it's resolved at folio post-time. Story should reference the dependency.
  • "Corporate" rate plans typically need a corporateAccountId linkage + per-account negotiated rates — today the model has no corporateAccountId field, just a code discriminator. Surface as Day-2 gap.

Drift findings:

D-007 — [P0] [Catalog-vs-EPIC-body] Catalog (01-epic-catalog.md) and EPIC body (epics/EP-002-property-management.md) disagree on which title goes with which story ID from US-002-005 onward. All reviews/EP-002/US-002-NNN-review.md files follow the catalog numbering. Two referent docs giving different answers to "what does US-002-006 mean" is a P0 pilot-blocking drift.

  • [SME-PROPOSED] Adopt the catalog numbering as canonical, rewrite the EPIC body's §US-002-005..§US-002-014 to match, and add an alias line > Previously titled "X" in EPIC body draft prior to 2026-05-24 reconciliation to each section so historic references stay traceable.

D-008 — [P0] [Story-incomplete + India-hospitality-critical] Rate plan story does not specify GST-inclusive vs GST-exclusive pricing semantics, CGST+SGST vs IGST resolution, or the relationship to TaxConfiguration.inclusive flag (AP-T02-005). This is the most error-prone pricing surface in Indian hospitality.

  • [SME-PROPOSED] Add AC: "RatePlan SHALL carry a pricingMode enum (GST_INCLUSIVE | GST_EXCLUSIVE) that MUST match the linked TaxConfiguration.inclusive flag. Mismatch is a 409 validation error at rate-plan PUT time."
  • [SME-PROPOSED] Add AC: "Folio posting SHALL resolve CGST+SGST (intra-state: property state == guest billing state) vs IGST (inter-state: property state ≠ guest billing state) using the property's GSTIN state-code + guest profile state. Both paths covered by tax.calculate.spec.ts."

D-009 — [P1] [Story-incomplete] Corporate rate plans typically need a corporateAccountId linkage to support per-account negotiated rates + invoice routing. Today the model has no such field; "corporate" is a code discriminator only.

  • [SME-PROPOSED] Add AC: "Rate plans of type CORPORATE SHALL be linkable to a CorporateAccount entity (Day-2 model — surface as new story US-002-NEW-CORPORATE-ACCOUNT or attach to CRM EPIC EP-010)."

US-002-006 — Cancellation Policy Configuration​

Catalog status: Built (P0, 3 points) Verified status: Built

Note: EP-002 numbering drift — catalog title (Cancellation Policy Configuration) ≠ EPIC body title (Property Onboarding Wizard). Review file at reviews/EP-002/US-002-006-review.md matches the catalog. Task 6 P0 drift candidate (see D-007).

Code locations:

  • hmp-property-service/src/cancellation-policies/cancellation-policies.service.ts — Cancellation policies service
  • hmp-property-service/src/cancellation-policies/cancellation-policies.controller.ts:32-102 — CRUD endpoints for cancellation policies
  • hmp-property-service/src/cancellation-policies/dto/cancellation-policy.dto.ts — Policy DTO
  • hmp-property-service/src/cancellation-policies/dto/cancellation-rules-validator.ts — Zod v4 typed rules validator (AP-T02-006)
  • hmp-web-dashboard/src/app/(dashboard)/settings/cancellation-policies/page.tsx — Cancellation policies page

Unit + integration tests:

  • hmp-property-service/src/cancellation-policies/cancellation-policies.tenant-guard.spec.ts — Tenant guard
  • hmp-property-service/src/cancellation-policies/cancellation-policy-rules.spec.ts — 14 tests on Zod schema, validator helper, DTO

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/flow-2-property-onboarding.spec.ts :: "S7 — cancellation-policies" — PASS (7/7) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/property-settings.flow.spec.ts :: "Scenarios 3 and 7 — policy create + roundtrip" — TBD (FT-2, not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • hmp-web-dashboard/e2e/real-backend/smoke-cancellation-policies.real.spec.ts — cancellation policies page loads

E2E (Playwright):

  • hmp-web-dashboard/e2e/tests/settings-policies.spec.ts — cancellation policies

Use cases referencing this story:

  • docs/requirements/use-cases/uc-cancellation-rules-validation.yaml (AP-T02-006 — Zod typed rules)

SME notes:

  • Zod v4 typed validator (AP-T02-006) is the clean win here — replaces an earlier free-form JSON shape with a typed rules[] ladder.
  • Story does not reference DPDPA refund rules: under DPDPA art. 8(3), refunds for cancelled bookings must be processed within "a reasonable period" (industry interpretation: 7 business days). Today cancellation policies define the charge ladder but say nothing about refund SLA. Risk: a tenant sets a "100% refund within 24 h" policy but the platform has no SLA contract that the refund actually fires.
  • Duplicate-name check (per implementation report) is shipped — story doesn't enumerate it.

Drift findings:

D-010 — [P1] [Story-incomplete + DPDPA-relevant] Cancellation policy defines the charge ladder but not the refund SLA. DPDPA art. 8(3) requires "reasonable period" refunds (industry: 7 business days). A 100%-refund policy with no SLA contract is a compliance trap.

  • [SME-PROPOSED] Add AC: "Cancellation policies SHALL include a refundSlaHours field (default 168 = 7 days, max 720 = 30 days). Refund processor (payment-service) SHALL emit refund.processed event within the SLA window; missed SLA emits refund.sla.breached for ops alerting."
  • [SME-PROPOSED] Add AC: "Cancellation API SHALL return both the charge amount AND the expected refund amount + expected refund date, so the guest receives a clear refund commitment in the confirmation email."

US-002-007 — Seasonal & Dynamic Rate Management​

Catalog status: Partial (P1, 5 points) Verified status: Partial — Seasons CRUD shipped (review file stale, D-011); BAR engine + dynamic pricing exist but full dynamic-pricing loop is incomplete

Note: Source map flags a P0 review-vs-code drift — reviews/EP-002/US-002-007-review.md says "NOT_IMPLEMENTED, 0/10" but Season model + 6 endpoints + frontend page all exist per implementation report.

Code locations:

  • hmp-property-service/src/seasons/seasons.controller.ts:25-73 — POST /seasons, GET /properties/:propertyId/seasons, GET /seasons/:id, GET /properties/:propertyId/seasons/active, PUT /seasons/:id, DELETE /seasons/:id
  • hmp-property-service/src/seasons/seasons.service.ts — Seasons service
  • hmp-property-service/src/seasons/dto/season.dto.ts — Season DTOs
  • hmp-property-service/src/rates/bar-engine.service.ts — BAR rate engine
  • hmp-property-service/src/rates/rate-calendar.controller.ts — Rate calendar controller
  • hmp-property-service/src/rates/rate-calendar.service.ts — Rate calendar service
  • hmp-property-service/src/rates/pricing-recommendation.controller.ts — Pricing recommendations
  • hmp-property-service/src/pricing/dynamic-pricing.controller.ts — Dynamic pricing controller
  • hmp-property-service/src/season-templates/season-templates.controller.ts — Season templates
  • hmp-web-dashboard/src/app/(dashboard)/settings/seasons/page.tsx — Seasons page
  • hmp-web-dashboard/src/app/(dashboard)/settings/season-templates/page.tsx — Season templates page

Unit + integration tests:

  • hmp-property-service/src/rates/bar-engine.service.spec.ts — BAR engine tests
  • hmp-property-service/src/rates/rate-calendar.service.spec.ts — Rate calendar tests
  • hmp-property-service/src/pricing/dynamic-pricing.service.spec.ts [CITED-BUT-NOT-FOUND] — dynamic pricing tests (source map cites by name; existence to be verified Task 6)
  • hmp-property-service/src/seasons/*.spec.ts — [CITED-BUT-NOT-FOUND] — source map notes Seasons module spec coverage is unclear and is a Task 6 verification gap

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/property-settings.flow.spec.ts :: "Scenario 4 — season create" — TBD (FT-2, not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/ari-grid.flow.spec.ts :: "ARI grid" — TBD (not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • hmp-web-dashboard/e2e/real-backend/smoke-seasons.real.spec.ts — seasons page loads

E2E (Playwright):

  • hmp-web-dashboard/e2e/tests/rates-calendar.spec.ts — rate calendar
  • hmp-web-dashboard/e2e/tests/rates-seasons.spec.ts — rate seasons
  • hmp-web-dashboard/e2e/tests/rates-restrictions.spec.ts — rate restrictions

Use cases referencing this story:

  • none

SME notes:

  • The Seasons module ships per implementation report — review-file "NOT_IMPLEMENTED" is ~10 weeks stale.
  • Indian hospitality seasonality is festival-driven (Diwali/Holi/Eid/Christmas/New Year are peak; monsoon is low for coastal; "wedding season" Nov–Feb peak for Tier-2 cities). The SeasonType enum (PEAK/HIGH/REGULAR/LOW/FESTIVAL/BLACKOUT) covers this but the story does not enumerate which season types are required vs optional, or whether season-templates ship with India-specific festival defaults.
  • Dynamic pricing surface (pricing/dynamic-pricing.controller.ts) is shipped but the upstream hmp-forecast-sidecar (Python FastAPI / Prophet) integration is unclear from the source map. The story should specify whether dynamic pricing recommendations are advisory (RM approves) or auto-apply (with guardrails).
  • Revenue-strategy service (hmp-revenue-strategy) on port 3017 exists per platform CLAUDE.md as "RM Phase 1" — its relationship to this story is undocumented. Risk: two services with overlapping responsibilities.

Drift findings:

D-011 — [P0] [Review-vs-code] reviews/EP-002/US-002-007-review.md verdict NOT_IMPLEMENTED is stale — Season model + 6 endpoints + frontend page + smoke test all exist per implementation report (2026-03-07). A new reviewer reading the review file alone would reopen closed work.

  • [SME-PROPOSED] Re-run the US-002-007 review with the post-implementation-report state and refresh the verdict to PARTIAL (Seasons CRUD shipped; full dynamic-pricing loop still incomplete).

D-012 — [P1] [Story-incomplete + India-hospitality-critical] Story does not document India-specific festival calendar defaults (Diwali, Holi, Eid, Christmas, New Year, wedding season Nov–Feb) or whether season-templates ship with these out-of-box.

  • [SME-PROPOSED] Add AC: "Season templates SHALL include India-specific defaults: Diwali (5 days around Hindu calendar), Holi, Eid (lunar — needs annual rebuild), Christmas+New Year (Dec 22 – Jan 2), Republic Day weekend, Independence Day weekend, Wedding Season block (Nov 15 – Feb 28). Tenant can clone + edit per property."

D-013 — [P1] [Cross-service-boundary] Dynamic pricing surface lives in BOTH hmp-property-service/src/pricing/ AND hmp-revenue-strategy (port 3017, RM Phase 1). The story does not explain the split or which service owns which responsibility. Risk: overlapping logic, double-emit of pricing-decision events.

  • [SME-PROPOSED] Refine US-002-007 to delineate boundary: property-service owns "static seasonal rate calendar"; revenue-strategy owns "demand-forecast-driven dynamic recommendations + guardrails + approvals". pricing/dynamic-pricing.controller.ts in property-service is the receive-pricing-decision endpoint, NOT the decision-maker.

US-002-008 — Data Migration from Legacy PMS​

Catalog status: Partial (P1, 8 points) Verified status: Partial — 3 importers (eZee/Hotelogix/Opera) + job model shipped; IDS Next missing; BullMQ + chunked upload deferred

Code locations:

  • hmp-property-service/src/migration/migration.controller.ts:36-321 — legacy importer endpoints (ezee/hotelogix/opera + OTA remap + reconciliation)
  • hmp-property-service/src/migration/migration-import.controller.ts:39-213 — job-based: POST /upload, POST /:jobId/validate, POST /:jobId/execute, GET /:jobId/status, DELETE /:jobId
  • hmp-property-service/src/migration/migration.service.ts — Migration coordinator
  • hmp-property-service/src/migration/ezee-importer.service.ts — eZee importer
  • hmp-property-service/src/migration/hotelogix-importer.service.ts — Hotelogix importer
  • hmp-property-service/src/migration/opera-importer.service.ts — Opera importer
  • hmp-property-service/src/migration/ota-remapper.service.ts — OTA remapper
  • hmp-property-service/src/migration/reconciliation.service.ts — Reconciliation service
  • hmp-property-service/src/migration/migration-import.service.ts — Job-based import service
  • hmp-web-dashboard/src/app/(dashboard)/settings/migration/page.tsx — Migration settings page
  • hmp-web-dashboard/src/app/(dashboard)/onboarding/migrate/page.tsx — Onboarding migration page

Unit + integration tests:

  • hmp-property-service/src/migration/migration.service.spec.ts — Migration coordinator tests
  • hmp-property-service/src/migration/ezee-importer.service.spec.ts — eZee importer tests
  • hmp-property-service/src/migration/hotelogix-importer.service.spec.ts — Hotelogix importer tests
  • hmp-property-service/src/migration/opera-importer.service.spec.ts — Opera importer tests
  • hmp-property-service/src/migration/ota-remapper.service.spec.ts — OTA remapper tests
  • hmp-property-service/src/migration/reconciliation.service.spec.ts — Reconciliation tests
  • hmp-property-service/src/migration/migration-import.service.spec.ts — Job-based import tests

Flow tests:

  • none (no flow test for migration per source map)

Real-backend smokes:

  • none

Use cases referencing this story:

  • none

SME notes:

  • 3 legacy importers shipped (eZee, Hotelogix, Opera) — sane choices for Indian-market migration targets. IDS Next is the obvious next add (large incumbent in Indian Tier-2/3 hotels) and is explicitly deferred per the implementation report.
  • Job-based path (migration-import.controller.ts) exists but the implementation report defers (a) chunked upload endpoint, (b) BullMQ background processing, (c) WebSocket progress events. Without BullMQ, a large import will block the request thread and likely timeout at the gateway's 60-s downstream limit.
  • No flow test for migration. Real-world tenant migration is high-risk (one-shot, irreversible) — no end-to-end test = high regression risk.
  • The story does not specify rollback semantics. If 50,000 rooms import succeeds but 20,000 reservations fail, can the tenant roll back the room import?

Drift findings:

(carries forward D-007 numbering-drift recommendation; no new drift specific to this story beyond the SME notes above)

  • [SME-PROPOSED] Add AC: "A flow test SHALL exercise the full migration round-trip (upload → validate → execute → status → reconcile) for at least one importer (eZee or Hotelogix) against a fixture file. Test runs in compose-only mode (matches brute-force-lockout pattern)."
  • [SME-PROPOSED] Add AC: "Migration jobs SHALL support rollback within 24 h of completion: a POST /migration-import/:jobId/rollback endpoint reverts all Property, Room, RoomType, and Reservation rows created with the job's migrationJobId tag."

US-002-009 — Onboarding Wizard (Guided Setup)​

Catalog status: Partial (P1, 5 points) Verified status: Partial — backend saga shipped (auth-service orchestrator + property-service seed); frontend has 4 steps vs spec 7; seed-defaults wiring incomplete

Code locations:

  • hmp-auth-service/src/onboarding/onboarding.controller.ts:50-188 — POST /onboarding/start, POST /onboarding/:tenantId/property, POST /onboarding/:tenantId/rooms, POST /onboarding/:tenantId/activate, GET /onboarding/:tenantId/status (@Roles('SUPER_ADMIN','ADMIN') with assertTenantScope())
  • hmp-auth-service/src/onboarding/onboarding.service.ts — Saga via PropertyServiceClient (xPrisma removed per AP-T01-007)
  • hmp-auth-service/src/onboarding/dto/onboarding.dto.ts — StartOnboardingDto, AddPropertyDto, ConfigureRoomsDto, ActivateSubscriptionDto
  • hmp-property-service/src/onboarding/onboarding.controller.ts — POST /onboarding, GET /onboarding/:propertyId/status, POST /onboarding/seed-defaults (AP-T01-007 follow-up: seed-defaults wiring still flagged)
  • hmp-property-service/src/onboarding/onboarding.service.ts — Property-side onboarding service
  • hmp-property-service/src/onboarding/seed-defaults.service.ts — Seed-defaults service (exists; saga still treats as stub per AP-T01-007 follow-up)
  • hmp-web-dashboard/src/app/(dashboard)/settings/setup-wizard/page.tsx — 4-step setup wizard (spec wants 7)
  • hmp-web-dashboard/src/app/(dashboard)/onboarding/property/page.tsx — Onboarding hub
  • hmp-web-dashboard/src/app/(dashboard)/onboarding/migrate/page.tsx — Onboarding migrate entry

Unit + integration tests:

  • hmp-auth-service/src/onboarding/onboarding.service.spec.ts — 15 saga behaviour tests
  • hmp-auth-service/src/onboarding/onboarding.controller.spec.ts — 4 tenant-scope tests
  • hmp-property-service/src/onboarding/onboarding.service.spec.ts — Property-side onboarding tx
  • hmp-property-service/src/onboarding/seed-defaults.spec.ts — Seed-defaults endpoint

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/admin-onboarding.flow.spec.ts :: "4-step saga end-to-end" — TBD (not in 13-flow doc; FINDING-001 gate via TENANT_CREATED Kafka propagation) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/tenant-onboarding-full.flow.spec.ts :: "FT-4 — 10-scenario full wizard" — TBD (not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
  • hmp-infrastructure/testing/flow-tests/src/flows/flow-1-tenant-onboarding.spec.ts :: "signup → login → property → isolation" — 5/10 PASS (S2a/S3/S4 fail on stale read model) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • (covered by smoke-property-setup / smoke-settings-roomtype chain via the wizard)

E2E (Playwright):

  • hmp-web-dashboard/e2e/onboarding-wizard.spec.ts — full setup wizard UI

Use cases referencing this story:

  • docs/requirements/use-cases/uc-onboarding-admin-role.yaml (AP-T01-003 — ADMIN tenant-scope check)
  • docs/requirements/use-cases/uc-tenant-self-onboarding.yaml (AP-T01-004 — admin-portal signup)
  • docs/requirements/use-cases/uc-tenant-onboarding-saga.yaml (AP-T01-007 — PropertyServiceClient saga)

SME notes:

  • The wizard lives in two services — auth-service orchestrates; property-service does the per-property seed. This cross-EPIC seam is the most fragile part of BF-001 and is not narrated in the story body.
  • Spec wants 7 steps; frontend has 4. Implementation report flags "Onboarding wizard 7-step expansion" as deferred (frontend-heavy). The 4-step compression conflates rate-plan + cancellation-policy + tax + branding into the "Configure" step, which is the highest cognitive-load step in the wizard.
  • seed-defaults.service.ts exists in property-service but the auth-service saga does not call it. Either wire the saga → seed-defaults call or delete the endpoint to avoid dead code.

Drift findings:

(carries forward BF-001 D-003: POST /onboarding/seed-defaults endpoint exists but saga still treats seedPropertyDefaults as a stub)

  • [SME-PROPOSED] Add AC: "Wizard POST /api/onboarding/:tenantId/property (auth-service) SHALL call PropertyServiceClient.seedDefaults(propertyId) after successful property create, which proxies to POST /api/onboarding/seed-defaults (property-service). Seed-defaults SHALL create: 1 default rate plan (BAR), 2 default cancellation policies (Flexible + Non-Refundable), India tax defaults (CGST 9% + SGST 9% — AP-T02-005), and the 11 built-in roles."
  • [SME-PROPOSED] Add AC: "If the spec 7-step wizard is the canonical target, the 4-step compression SHALL be tracked as US-002-NEW-WIZARD-7-STEPS (per BF-001 Step 14 SME proposal) with explicit step-mapping: 1-Property, 2-Rooms, 3-RatePlans, 4-CancellationPolicies, 5-Taxes, 6-Branding, 7-Activate."

US-002-010 — Room Features (Smoking, Accessible, Connecting)​

Catalog status: Built (P1, 3 points) Verified status: Built-Untested — covered implicitly via rooms.service.spec.ts 18-test suite; no dedicated assertions

Code locations:

  • hmp-property-service/src/rooms/rooms.controller.ts — PUT /rooms/:id flow (no dedicated controller); isSmoking + features[] fields on CreateRoomDto/UpdateRoomDto
  • hmp-property-service/src/rooms/dto/* — isSmoking, features[] field shape in CreateRoomDto / UpdateRoomDto
  • hmp-web-dashboard/src/app/(dashboard)/settings/rooms/page.tsx — Room edit form

Unit + integration tests:

  • hmp-property-service/src/rooms/rooms.service.spec.ts — coverage included in the 18-test suite (features asserted alongside CRUD)

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/rooms-crud.flow.spec.ts :: "features in lifecycle CRUD" — TBD (P-GAP-1, not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • none specific

Use cases referencing this story:

  • none

SME notes:

  • Features is a free-form string[] — no canonical taxonomy (same gap as US-002-002 D-004). "Connecting" specifically requires a paired-room reference (room A is connecting to room B) — today the model has no connectingRoomId field; "connecting" is a flag with no linkage.
  • "Accessible" maps to multiple regulations: India ADA (Rights of Persons with Disabilities Act 2016) + international ADA/WCAG. Today: single boolean. Spec should require finer-grained accessibility tags (wheelchair-accessible bathroom, visual-alert system, etc.) for OTA-required compliance.

Drift findings: (none net new — feeds D-004 amenity-taxonomy ask)

  • [SME-PROPOSED] Add AC: "Rooms flagged features: ['CONNECTING'] SHALL carry a connectingRoomId FK to the paired room. The relation SHALL be bidirectional (A.connecting=B implies B.connecting=A) enforced by rooms.service.ts::updateConnectingPair()."
  • [SME-PROPOSED] Add AC: "ACCESSIBLE SHALL be replaced (or composed) by a structured accessibility[] array drawing from a canonical taxonomy aligned with India RPwD 2016 + OTA-required tags (wheelchairAccessible, lowFloor, visualAlertSystem, brailleSignage, accessibleBathroom)."

US-002-011 — Room Blocking (Out of Order / Out of Service)​

Catalog status: Built (P1, 3 points) Verified status: Stub — re-uses PATCH /rooms/:id/status; no dedicated controller, no blocking-reason taxonomy, no integration to work-orders, no auto-clear

Code locations:

  • hmp-property-service/src/rooms/rooms.controller.ts:149 — PATCH /rooms/:id/status (subset: set status to OUT_OF_ORDER / OUT_OF_SERVICE; reason logged to RoomStatusLog); no dedicated controller
  • hmp-property-service/src/rooms/rooms.service.ts — updateStatus() writes the RoomStatusLog row
  • hmp-web-dashboard/src/components/.../front-desk room grid [CITED-BUT-NOT-FOUND] — room status toggle
  • hmp-web-dashboard/src/components/.../housekeeping status board [CITED-BUT-NOT-FOUND] — room status toggle

Unit + integration tests:

  • hmp-property-service/src/rooms/rooms.service.spec.ts — updateStatus() coverage in the 18-test suite

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/flow-5-fd-hk-updates.spec.ts :: "room move/extend/HK lifecycle (includes OOO/OOS path)" — 11/12 PASS (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • none

Use cases referencing this story:

  • none

SME notes (no dedicated review file in reviews/EP-002/):

  • "Room Blocking" is implemented as a special status value on the room — there is no dedicated RoomBlock entity, no expected-return-date, no maintenance-reason taxonomy, no auto-clear, no work-order linkage. This is a real-world gap: a hotel that takes 5 rooms offline for renovation cannot track expected-back date or link to a vendor work-order.
  • hmp-asset-service (port 3016 per platform CLAUDE.md) owns work-orders, PPM/AMC, engineering spares. Room blocking should integrate with it — today, no integration.
  • Story does not specify the actor scope: can housekeeping mark a room OOO, or only engineering? Today: any user with PATCH room access can — overpermissive.

Drift findings:

D-014 — [P1] [Stub-shipped-as-Built + integration-gap] Catalog says "Built" but the implementation is a thin alias over PATCH /rooms/:id/status. No dedicated RoomBlock model, no work-order linkage to hmp-asset-service, no expected-return-date, no auto-clear, no actor-role restriction. Calling this "Built" misleads operators planning a maintenance project.

  • [SME-PROPOSED] Bump status from Built → Stub (or Partial) and author a new story US-002-NEW-ROOM-BLOCKING "Dedicated room blocking with expected-return-date + work-order linkage + role restriction".
  • [SME-PROPOSED] Add AC: "Room blocking SHALL create a RoomBlock row with (roomId, reason, expectedClearDate, workOrderId?, blockedByUserId, autoClearAt?). Optional workOrderId SHALL FK to hmp-asset-service.WorkOrder. Cleared at expectedClearDate by a daily cron unless autoClearAt=null (operator-managed)."
  • [SME-PROPOSED] Add AC: "Only users with role MAINTENANCE_STAFF, ADMIN, SUPER_ADMIN, or MANAGER MAY mark a room OOO/OOS. Housekeeping/frontdesk staff get 403."

US-002-012 — Floor Plan Visual Editor​

Catalog status: Not Started (P2, 8 points) Verified status: Not Started

Code locations: NONE FOUND (drift candidate — Task 6)

Source map note: catalog and EPIC body both say "Not Started". POS module's POSTable.posX/posY exists as a pattern reference only — not a Floor Plan implementation.

Unit + integration tests: NONE FOUND

Flow tests: NONE FOUND

Real-backend smokes: NONE FOUND

Use cases referencing this story:

  • none

SME notes:

  • "Not Started" is honest — no code, no tests, no UI. The POS posX/posY pattern is a useful precedent (drag-drop floor layout) but is at table granularity, not room.
  • Indian-market reality: most Tier-2/3 hotels manage rooms by list/grid, not by visual floor plan. Visual editor is a nice-to-have for chains + premium properties.
  • BF-001 Step 7 (Add rooms) notes "Floor Plan Visual Editor (US-002-012) is Not Started — manual room entry only today" — this is correctly reflected.

Drift findings: (none — story-vs-reality matches: Not Started)


US-002-013 — Multi-Building Support​

Catalog status: Partial (P2, 5 points) Verified status: Partial — Room.building field + filter only; no dedicated Building entity

Code locations:

  • hmp-property-service/prisma/schema.prisma [CITED-BUT-NOT-FOUND] (exact line/model not enumerated) — Room.building field is String? on the Room model
  • hmp-property-service/src/rooms/rooms.controller.ts — GET /properties/:propertyId/rooms?building=X filter supported (no dedicated Building entity)

Unit + integration tests:

  • (implicit via rooms.service.spec.ts query filter coverage; no dedicated multi-building spec)

Flow tests: NONE FOUND

Real-backend smokes: NONE FOUND

Use cases referencing this story:

  • none

SME notes (no dedicated review file):

  • Room.building is a free-text String?. No building-level attributes (address, GPS, year-built, building-wide amenities, floor-count, lift-count). Real chains need this for guest wayfinding and for distributing housekeeping zones.
  • Story does not specify whether multi-building support means (a) multiple buildings under one property (resort with cottages + main block), or (b) multi-property = multi-building. Today: (a) via free-text only.

Drift findings: (no new — captured in summary as Partial)

  • [SME-PROPOSED] Author a new story US-002-NEW-BUILDING-ENTITY "Dedicated Building entity under Property" with ACs for (Building has its own address+GPS+amenities; Room.buildingId FK; building-level housekeeping zone assignment).

Catalog status: Built (P2, 3 points) Verified status: Built

Code locations:

  • hmp-property-service/src/assets/asset.service.ts — generateUploadUrl() validates content-type, filename, returns presigned PUT URL
  • hmp-property-service/src/assets/asset.controller.ts:36-156 — POST /upload-url, POST /register-image, GET /images, DELETE /images/:id, POST /process, GET /:tenantId/:type, DELETE /:tenantId/:type
  • hmp-web-dashboard/src/components/modules/settings/image-upload-widget.tsx — Reusable upload widget (drag-drop, MIME, 5MB cap)
  • hmp-web-dashboard/src/app/(dashboard)/settings/property/page.tsx — Photo Gallery tab consumer
  • hmp-web-dashboard/src/app/(dashboard)/settings/room-types/page.tsx — Room-type photos consumer

Unit + integration tests:

  • hmp-property-service/src/assets/asset.service.spec.ts — 9 specs (validation, AssetType enum, presigned URL)
  • hmp-property-service/src/assets/asset.controller.spec.ts — controller tests
  • hmp-web-dashboard/src/components/modules/settings/image-upload-widget.test.tsx — 13 specs (drag-drop, MIME, 5MB cap, sequencing)

Flow tests:

  • hmp-infrastructure/testing/flow-tests/src/flows/flow-2-property-onboarding.spec.ts :: "S2 — POST /assets/upload-url" — PASS (7/7) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)

Real-backend smokes:

  • none specific

Use cases referencing this story:

  • docs/requirements/use-cases/uc-property-image-upload.yaml (AP-T02-002 backend + AP-T02-003 frontend)

SME notes (no dedicated review file):

  • 5 MB cap is generous for thumbnails but tight for high-quality room photography. Real properties supply 8–12 MB DSLR shots.
  • POST /process exists but the source map does not enumerate its semantics. Likely image-optimization (Sharp resize/webp conversion) per the platform's sharp external in esbuild config — verify.
  • No ordering / sequencing test for the gallery (the widget claims sequencing but no backend assertion exists). Drag-reorder UI behaviour is therefore untested.

Drift findings: (none — implementation matches story closely)

  • [SME-PROPOSED] Add AC: "Image upload cap SHALL be configurable per AssetType: 5 MB for LOGO / THUMBNAIL; 15 MB for ROOM_PHOTO / PROPERTY_HERO. The widget enforces the cap client-side; backend generateUploadUrl() enforces via S3 presigned-URL content-length-range condition."
  • [SME-PROPOSED] Add AC: "Gallery ordering SHALL be persisted via an order: Int column on the image row. PATCH /images/reorder accepts [{id, order}] array and writes the new sequence atomically."