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.mdfor the pattern. Source of truth for "% passing":hmp-docs/docs/platform-registry/2026-05-16-13-flow-demo-readiness.mduntil Task #12 re-measures.
Status legend
| Status | Meaning |
|---|---|
Built | Code exists + tests exist + tests pass |
Built-Untested | Code exists, no automated test coverage (or test exists but is a stub) |
Partial | Code exists for some acceptance criteria, gaps for others |
Stub | Code is a placeholder / TODO / returns mock data |
Not Started | No code |
Summary
| Metric | Count |
|---|---|
| Total stories | 14 |
| Catalog claims "Built" | 9 |
| Verified-Built | 7 (US-002-001, -002, -003, -004, -005, -006, -014) |
| Built-Untested | 1 (US-002-010 — covered only implicitly via rooms tests) |
| Partial | 4 (US-002-007, -008, -009, -013) |
| Stub | 1 (US-002-011 — uses PATCH /rooms/:id/status with no dedicated controller / no reason taxonomy) |
| Not Started | 1 (US-002-012) |
| Drift findings | 14 (D-001..D-014; 5 P0 / 7 P1 / 2 P2) |
| [SME-PROPOSED] refinements | 30 |
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 servicehmp-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/:idhmp-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 DTOhmp-property-service/src/properties/dto/property-policies.dto.ts— Policies DTOhmp-web-dashboard/src/app/(dashboard)/settings/property/page.tsx— Property settings pagehmp-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-datehmp-property-service/src/properties/properties.controller.spec.ts— 12 controller testshmp-property-service/src/properties/dto/property.dto.spec.ts— 11 GSTIN/PAN validator testshmp-property-service/src/properties/__tests__/properties.search.spec.ts— Search filter coveragehmp-property-service/src/properties/__tests__/property-cross-tenant-denial.spec.ts— Tenant isolationhmp-property-service/src/properties/property-create-no-tenant-stub.spec.ts— Tenant stub auto-creationhmp-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/propertyloading with seeded GSTIN/timezonehmp-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 smokehmp-web-dashboard/e2e/onboarding-wizard.spec.ts— full setup wizard UIhmp-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, defaultZliteral, 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 redundantcreatedAt. 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/propertiesreturns 201, the subsequentGET /api/propertiesfor 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
/themeto US-002-NEW-BRANDING,/activate+/onboardingto US-002-009,/business-dateto 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 byclass-validatorat 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 servicehmp-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/:idhmp-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 testshmp-property-service/src/room-types/room-types.controller.spec.ts— 13 controller testshmp-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/duplicateendpoint 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
codeformat only; amenity shape (free-formstring[]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 servicehmp-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, UpdateRoomNotesDtohmp-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 (coversisActivefilter,isOccupiedauto-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 CRUDhmp-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 onRoomsControllerbut 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/bulkis 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(writesRoomStatusLog)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 eventshmp-frontdesk-service/src/checkout/checkout.service.ts— Check-out cascade sets room status + emits Kafka eventshmp-web-dashboard/src/components/.../room-grid.tsx[CITED-BUT-NOT-FOUND]— front-desk room gridhmp-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()andgetRoomStatusSummary()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
CLEANwhile 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.
RoomStatusLogwrite 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
RoomStatusLogrow 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.mdmatches 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 servicehmp-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/restrictionshmp-property-service/src/rate-plans/dto/*— Rate plan DTOshmp-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 testshmp-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.inclusiveflag) 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
corporateAccountIdlinkage + per-account negotiated rates — today the model has nocorporateAccountIdfield, just acodediscriminator. 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 reconciliationto 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
pricingModeenum (GST_INCLUSIVE|GST_EXCLUSIVE) that MUST match the linkedTaxConfiguration.inclusiveflag. 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
CORPORATESHALL be linkable to aCorporateAccountentity (Day-2 model — surface as new storyUS-002-NEW-CORPORATE-ACCOUNTor 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.mdmatches the catalog. Task 6 P0 drift candidate (see D-007).
Code locations:
hmp-property-service/src/cancellation-policies/cancellation-policies.service.ts— Cancellation policies servicehmp-property-service/src/cancellation-policies/cancellation-policies.controller.ts:32-102— CRUD endpoints for cancellation policieshmp-property-service/src/cancellation-policies/dto/cancellation-policy.dto.ts— Policy DTOhmp-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 guardhmp-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
refundSlaHoursfield (default 168 = 7 days, max 720 = 30 days). Refund processor (payment-service) SHALL emitrefund.processedevent within the SLA window; missed SLA emitsrefund.sla.breachedfor 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.mdsays "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/:idhmp-property-service/src/seasons/seasons.service.ts— Seasons servicehmp-property-service/src/seasons/dto/season.dto.ts— Season DTOshmp-property-service/src/rates/bar-engine.service.ts— BAR rate enginehmp-property-service/src/rates/rate-calendar.controller.ts— Rate calendar controllerhmp-property-service/src/rates/rate-calendar.service.ts— Rate calendar servicehmp-property-service/src/rates/pricing-recommendation.controller.ts— Pricing recommendationshmp-property-service/src/pricing/dynamic-pricing.controller.ts— Dynamic pricing controllerhmp-property-service/src/season-templates/season-templates.controller.ts— Season templateshmp-web-dashboard/src/app/(dashboard)/settings/seasons/page.tsx— Seasons pagehmp-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 testshmp-property-service/src/rates/rate-calendar.service.spec.ts— Rate calendar testshmp-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 calendarhmp-web-dashboard/e2e/tests/rates-seasons.spec.ts— rate seasonshmp-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
SeasonTypeenum (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 upstreamhmp-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.tsin 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/:jobIdhmp-property-service/src/migration/migration.service.ts— Migration coordinatorhmp-property-service/src/migration/ezee-importer.service.ts— eZee importerhmp-property-service/src/migration/hotelogix-importer.service.ts— Hotelogix importerhmp-property-service/src/migration/opera-importer.service.ts— Opera importerhmp-property-service/src/migration/ota-remapper.service.ts— OTA remapperhmp-property-service/src/migration/reconciliation.service.ts— Reconciliation servicehmp-property-service/src/migration/migration-import.service.ts— Job-based import servicehmp-web-dashboard/src/app/(dashboard)/settings/migration/page.tsx— Migration settings pagehmp-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 testshmp-property-service/src/migration/ezee-importer.service.spec.ts— eZee importer testshmp-property-service/src/migration/hotelogix-importer.service.spec.ts— Hotelogix importer testshmp-property-service/src/migration/opera-importer.service.spec.ts— Opera importer testshmp-property-service/src/migration/ota-remapper.service.spec.ts— OTA remapper testshmp-property-service/src/migration/reconciliation.service.spec.ts— Reconciliation testshmp-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/rollbackendpoint reverts allProperty,Room,RoomType, andReservationrows created with the job'smigrationJobIdtag."
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')withassertTenantScope())hmp-auth-service/src/onboarding/onboarding.service.ts— Saga viaPropertyServiceClient(xPrisma removed per AP-T01-007)hmp-auth-service/src/onboarding/dto/onboarding.dto.ts— StartOnboardingDto, AddPropertyDto, ConfigureRoomsDto, ActivateSubscriptionDtohmp-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 servicehmp-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 hubhmp-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 testshmp-auth-service/src/onboarding/onboarding.controller.spec.ts— 4 tenant-scope testshmp-property-service/src/onboarding/onboarding.service.spec.ts— Property-side onboarding txhmp-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.tsexists 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 callPropertyServiceClient.seedDefaults(propertyId)after successful property create, which proxies toPOST /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/:idflow (no dedicated controller);isSmoking+features[]fields onCreateRoomDto/UpdateRoomDtohmp-property-service/src/rooms/dto/*—isSmoking,features[]field shape inCreateRoomDto/UpdateRoomDtohmp-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 noconnectingRoomIdfield; "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 aconnectingRoomIdFK to the paired room. The relation SHALL be bidirectional (A.connecting=B implies B.connecting=A) enforced byrooms.service.ts::updateConnectingPair()." - [SME-PROPOSED] Add AC: "
ACCESSIBLESHALL be replaced (or composed) by a structuredaccessibility[]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 toOUT_OF_ORDER/OUT_OF_SERVICE; reason logged toRoomStatusLog); no dedicated controllerhmp-property-service/src/rooms/rooms.service.ts—updateStatus()writes the RoomStatusLog rowhmp-web-dashboard/src/components/.../front-desk room grid[CITED-BUT-NOT-FOUND]— room status togglehmp-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
RoomBlockentity, 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(orPartial) and author a new storyUS-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
RoomBlockrow with(roomId, reason, expectedClearDate, workOrderId?, blockedByUserId, autoClearAt?). OptionalworkOrderIdSHALL FK tohmp-asset-service.WorkOrder. Cleared atexpectedClearDateby a daily cron unlessautoClearAt=null(operator-managed)." - [SME-PROPOSED] Add AC: "Only users with role
MAINTENANCE_STAFF,ADMIN,SUPER_ADMIN, orMANAGERMAY 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/posYpattern 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.buildingfield isString?on the Room modelhmp-property-service/src/rooms/rooms.controller.ts— GET/properties/:propertyId/rooms?building=Xfilter supported (no dedicatedBuildingentity)
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.buildingis a free-textString?. 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).
US-002-014 — Room Photos Gallery & Upload
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 URLhmp-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/:typehmp-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 consumerhmp-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 testshmp-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 /processexists but the source map does not enumerate its semantics. Likely image-optimization (Sharp resize/webp conversion) per the platform'ssharpexternal 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 forROOM_PHOTO/PROPERTY_HERO. The widget enforces the cap client-side; backendgenerateUploadUrl()enforces via S3 presigned-URLcontent-length-rangecondition." - [SME-PROPOSED] Add AC: "Gallery ordering SHALL be persisted via an
order: Intcolumn on the image row.PATCH /images/reorderaccepts[{id, order}]array and writes the new sequence atomically."