Group Folio Developer Package — Gap Audit vs Shipped HMP
Date: 2026-08-30
Subject: ~/Downloads/group folio developer package/ (CLAUDE.md, README.md, docs 01–08,
two single-file HTML prototypes)
Baseline: the full package spec including its own ⛔ backlog, per operator instruction.
Source of truth: local working trees for every code repo (verified master, 0 behind
origin/master); git show origin/master:<path> for hmp-infrastructure only — its checkout
sits on an audit branch 2104 commits behind master with 12 uncommitted files.
Method / limits. This is a source-level audit. No tests were executed, no environment was probed, no running build was inspected. Every claim below is anchored to a
file:linea reader can open. Where a capability was checked and not found, the probe is stated so the negative can be re-run. Nothing here has been verified against a deployed environment.
1. Verdict
HMP is substantially ahead of this package. The package is a prototype-grade UI/UX specification — two self-contained HTML files holding state in JS memory, explicitly described in its own doc 01 as "UI + logic demonstrations, not production code". HMP is a 17-service production platform whose group/folio surface alone spans ~28 source modules, 27 unit specs and 15 registered L1 flow specs.
The package's value to HMP is therefore not as a build spec. It is:
- a checklist that surfaced four genuine feature gaps (§5);
- a set of UI/UX patterns worth mining for the dashboard (§6);
- and — most valuably — a rule cross-check that found one place where the package encodes a rule that changed under it (§4, C2), and one place where HMP and the package hold the same open question with HMP already holding the answer surface (§4, C3).
Adopting the package's data model wholesale would be a regression. One of its stated non-negotiable invariants is a proposal HMP's SME has already evaluated and formally refused (§4, C1).
2. Evidence base
| Side | What was read |
|---|---|
| Package | CLAUDE.md, README.md, docs/01–docs/08, prototype structure (tabs, headings) |
| HMP backend | hmp-frontdesk-service/src/{groups,folios,night-audit,cform,...}, hmp-reservation-service/src, hmp-property-service/src/{tax,distribution,properties} |
| HMP shared | @hmp/tax, @hmp/constants, @hmp/compliance |
| HMP frontend | hmp-web-dashboard/src/components/modules/front-desk/{tape-chart-v2,tape-chart,group-folio}, src/app/(dashboard)/{front-desk,reservations} |
| HMP tests | hmp-infrastructure/certification/catalogue.mjs and testing/flow-tests/src/flows/ — both from origin/master |
3. Dimension-by-dimension summary
D1 · Backend logic + money rules — HMP ahead
Every capability in doc 06's "✅ Built" matrix has a HMP counterpart, generally a deeper one.
| Package capability | HMP counterpart |
|---|---|
| Group check-in / check-out | GroupCheckInOutModule — POST /front-desk/groups/:id/{check-in,check-out}, reuses CheckInService.batchCheckIn + CheckOutService.expressCheckOut (no fork) |
| Group invoice | GroupInvoiceService.resolveInvoiceMode + mintGroupInvoice — all-checked-out gate, owner-company buyerGstin, 422 B2B guard, gap-free idempotent mint, one EInvoiceJob |
| Posting master | GroupMasterFolioService.ensureMasterFolio under DistributedLock('group-master-folio:<owner>') |
| Routing (self / group) | group-routing-rule.ts — five typed kinds (ROOM_AND_TAX, ALL, NONE, NON_ROOM_TO_DESIGNATED, SPLIT_BY_CATEGORY) as a validated closed discriminated union |
| Deposit allocation | GroupPaymentService — OWNER / ROOM_WISE / MANUAL (see §5 G4 for a basis difference) |
| Link / de-link (pickup / washdown) | GroupLinkService |
| Per-charge void | group-void.service.ts, void-line.service.ts with a state×action matrix (DEPARTED.VOID='MANAGER', AUDITED.VOID='CREDIT_NOTE') |
| Meal-plan bifurcation (MPS) | meal-revenue-split.ts — EP-P18 / AB#10205, USALI room-vs-F&B split |
| Multiple payments / settlement | folios.service.ts payment path + §269ST window |
| Append-only ledger | enforced; voids divert to §34 credit-note reversal (folio-money-write-registry.ts:125) |
Capabilities HMP has that the package never mentions: waitlist, backdated stay with a filed-period guard, night audit, e-invoice/IRN, GST dead-zone policy, forex, cash drawer, petty cash, safe, smart lock, keycard, self check-in, guest portal, service recovery, upsell/upgrades, sharer check-out, split-by-room.
D2 · Dashboard UI — HMP ahead, package worth mining
HMP screens exist for all the package's principal views:
src/app/(dashboard)/front-desk/groups/[id]/folio/[reservationId]— group foliosrc/components/modules/front-desk/group-folio/— 15+ components (master ledger, rooms table, ops bar, void control, room-rate control, assign-room dialog, member-billed-to, manager elevation)src/components/modules/front-desk/tape-chart-v2/— 40+ modules
Tape chart drag-reassign is present, contrary to a first-pass probe: tape-chart-v2-shell.tsx:36-37
imports useDragReservation + useDragFromSidebar over @dnd-kit/core. HMP additionally supports
dragging an unassigned reservation in from a sidebar — which the package's prototype does not.
D3 · Statutory ruleset — HMP ahead; one package rule is superseded
| Doc 03 rule | HMP | Verdict |
|---|---|---|
| Slab 5% ≤₹7,500 / 18% above, per night | @hmp/tax, GstService.getRoomGstRate | ✅ aligned (GST 2.0) |
| Slab on contracted rate, not the retained amount | reservations.service.no-show-charge.spec.ts:224-246 (B-1) — slab keys off the booked ₹8,000/night → 18%, not the retention and not baseRate | ⚠ PARTIALLY aligned — see the correction below (AB#13139) |
| Slab basis: room-only vs full package (Q-18) | rate-resolver.service.ts:46 CA-RULING-PENDING | ⚠ shared open question — see C3 |
| Composite supply — one accommodation line | meal-revenue-split.ts (internal split, USALI) | ✅ aligned |
| Place of supply = property → CGST+SGST | invoice-place-of-supply.ts, Folio.placeOfSupply | ✅ aligned |
| Specified premises = any tariff > ₹7,500 | specified-premises-fy.ts — declared per-FY, effective-dated | ❌ package rule is superseded — see C2 |
| No-show / cancellation at accommodation slab | processNoShow + cancellation-settlement/ | ✅ aligned |
| §13(2) advance = time of supply on receipt | folios.service.ts:4989, folio.dto.ts:320 | ✅ aligned |
| §15 discount reduces taxable value pre-GST | discount-apportionment.util.ts:6 — §15(3)(a) | ✅ aligned |
| SAC codes | folio-tax-resolver.service.ts:842 (996311), :99 (996331) | ✅ aligned |
| §269ST — block single cash receipt ≥ ₹2,00,000 | payer-day-cash-window.ts | ✅ HMP exceeds — see §6 |
| §269ST structuring flag (package: OPEN) | per-payer-per-day aggregate, already built | ✅ HMP exceeds |
| §34 credit note | folios.module.ts:159, issueVoidReversalCreditNote | ✅ aligned |
| C-Form / FRRO | reporting-feeds.service.ts:683, src/cform/ | ✅ aligned |
| DPDP retention | app.module.ts:177 — 90-day sweep for GuestIdVerification | ✅ aligned |
⚠ CORRECTION (2026-08-31) — this table was too generous on doc 03 rule #1
The ✅ above was read off the no-show unit spec, which exercises the path through
resolveAccommodationGst. Running the package's own scenario against the live product found the rule holds on the paths that use that canonical helper and breaks on the generic charge endpoint:
FoliosService.postChargehanded the tax resolveramount × quantityas the slab band selector (folios.service.ts:3959). A ₹7,500/night room taxed at 18% instead of 5% once the line crossed the threshold — a 13-point over-collection from the guest. Measured on TEST:4,000 × 1 → 5%(correct),4,000 × 2 → 18%(wrong).Filed AB#13139 (P1/Critical), fixed, and pinned by a red-first L1 gate (
ab13139-accommodation-slab-bands-per-night.flow.spec.ts) whose S2/S3 pair posts the same line total at two different slabs, so the gate measures the band basis rather than a value.The general lesson for this audit: a source-level ✅ means "the rule is implemented somewhere it is reachable", not "every writer honours it". Doc 03's rules are only as strong as their least careful call site, and this platform had two implementations of the accommodation slab — one right, one wrong.
Two further defects surfaced the same way and are recorded here so the audit is not read as a clean bill of health:
- AB#13144 (P1/Critical) — group check-in was impossible on a default-configured property:
requireAadhaarAtCheckInis@default(true), the guard reads the ID off the request only, and the group path had no field to supply one. Every room returnedFAILED. This is the most likely explanation of the customer's standing "we cannot do group bookings" report.- AB#13141 —
certify.mjs --mode onboardreported the entire L1 layer as DID NOT RUN (globalSetup loaded no env), so onboard-mode certification had been measuring L2 only.
D4 · Test / certification coverage — HMP ahead
15 group flow specs registered in certification/catalogue.mjs under key groups (flow 4), and
all 15 exist on disk — no orphans in this set:
group-aggregate-invariant · group-arrival-and-folio · group-block-contract
group-bookings · group-checkin-preflight-execute · group-comp-room
group-folio-consolidated-money · group-folio-owner-rate · group-gst-place-of-supply
group-master-folio · group-member-vip-read-contract · group-money
group-ops-backend · group-single-invoice-party · group-single-invoice
Plus multiroom-not-group.flow.spec.ts under the group-change-regression-spine entry, and 27
unit specs in hmp-frontdesk-service/src/groups/.
4. Conflicts requiring an operator ruling
C1 — "Group is derived: rooms.length > 1. Never store a group flag."
Package: doc 04, stated as a data-model invariant.
HMP: first-class stored group entities.
Evidence: hmp-infrastructure/certification/catalogue.mjs:1235-1238 (on origin/master):
"RSV-MULTIROOM-NOT-GROUP pins the BOUNDARY the SME ruled on: the refused proposal was "rooms > 1 ⇒ group", and a 2-room family walk-in is 30-40% of volume. It is registered BEFORE the Phase 2 entry-point work lands, deliberately — a spec written afterwards would only encode whatever that change did. GREEN on TEST 2026-08-02 (3/3)."
HMP's SME already evaluated and formally refused this exact proposal, on the operational
ground that a two-room family walk-in is 30–40% of booking volume and must not be swept into
group handling. The refusal is pinned by a regression gate (multiroom-not-group.flow.spec.ts)
that was deliberately registered before the implementing change, so it could not be written to
rubber-stamp it.
Recommendation: HMP wins. Do not adopt. Adopting doc 04's invariant would re-open a closed SME ruling and break a green regression gate.
C2 — "Specified premises = any room-night tariff exceeds ₹7,500"
Package: doc 03 §1, "Specified premises (raises F&B GST)".
HMP: a declared, per-financial-year, effective-dated status.
Evidence: hmp-frontdesk-service/src/folios/specified-premises-fy.ts:1-45 —
"The 18%-with-ITC vs 5%-no-ITC F&B slab (Notification 05/2025-CTR) hinges on a DECLARED, per-financial-year, effective-dated fact — property-service's maker-checker
SpecifiedPremisesStatus. It is NEVER a function of the bill: not the line value, not the room rate, not the property's price list."
@hmp/tax states the same invariant at its own boundary (types.ts:18): "isSpecifiedPremises
comes from the FY status input, NEVER value > 7500."
The package's rule is the pre-1-April-2025 position. Notification 05/2025-CTR replaced the value-derived test with an opt-in/opt-out declaration regime (Annexures VII / VIII / IX). HMP additionally selects the declaration by the supply date's FY, not the newest row, so a backdated stay or a late-posted F&B chit is rated at its own tax point.
This is the highest-value finding of the audit. Doc 03 is the package's self-declared "most important document", opening with "Get one wrong and the invoice is legally wrong." This one is wrong, and in the direction that under-collects on a filed period — which under §32/§76 the hotel eats rather than the guest.
Recommendation: HMP wins. The package's doc 03 §1 needs correcting before anyone builds from it.
C3 — Slab basis (the package's Q-18) — not a conflict; a shared open question
Package: Q-18, listed as load-bearing and unresolved — does the ≤/>₹7,500 test read the
room component alone or the full package rate?
HMP: the same question, same wording, already instrumented.
Evidence: hmp-property-service/src/distribution/rate-resolver.service.ts:46-51:
// CA-RULING-PENDING: slab keys off charged rate (today's B-1); flip to
// roomComponent when CA confirms. The flip is a ONE-LINE change here —
// swap the `chargedRate` argument for `roomComponent` at the call site.
const gstSlabForCharged = (chargedRate: number): number => chargedRate <= 7500 ? 5 : 18;
And :296 — const roomComponent = round2(rate - mealSupplement);
So rate = room + meal supplement (full package rate) and roomComponent = room alone.
HMP's CA-RULING-PENDING is exactly Q-18. HMP currently answers full package rate, and has
pre-built the flip surface: every resolved night already exposes both roomComponent and
mealSupplement specifically so the change is one line.
Delta against the operator's "leave it parameterised" instruction: HMP's flip is a global one-line code change, not a per-property configuration. If Q-18 must be parameterised per property (as instructed), that is net-new work — a config surface plus a resolver read — not a flip of what exists.
5. Genuine gaps — capabilities HMP lacks
| # | Gap | Package reference | HMP state | Probe run |
|---|---|---|---|---|
| G1 | Per-room posting restriction — block F&B (noResto) | doc 04 "Posting rules"; doc 06 §B Routing | Absent. HMP's five routing rules decide a charge's destination, never its permission. Nothing refuses an F&B post to a nominated room. | grep -riE "noResto|blockFnB|noRestaurant|blockPosting" over frontdesk + dashboard → no match |
| G2 | Per-room credit cap ("post till limit") | doc 04 limit; doc 06 §B Routing | Partial. Company.creditLimit is enforced on the city-ledger path and on the GROUP_MASTER folio (AB#9947, group-master-credit-limit.spec.ts). There is no per-room / per-folio ceiling. | grep -rn "creditLimit" — all hits are company-grain |
| G3 | Folio-attached tasks / traces | doc 06 §B "Tasks" tab | Absent at folio grain. HMP has housekeeping tasks and work orders, but no OPERA-style trace pinned to a folio or reservation. | grep -riE "folioTask|folio.*reminder|traceTask" → no match |
| G4 | Deposit allocation basis | doc 04 depositMode='prorata' | Semantic difference, not absence. Package pro-rata splits by each room's share of the charge; HMP ROOM_WISE splits by each room's outstanding (group-payment.service.ts:28). Identical on a clean group; they diverge once any room has paid. | read |
G1 and G2 are standard OPERA "posting restrictions" / per-folio credit limits, so they are reasonable asks independent of this package.
6. Where HMP materially exceeds the package
- §269ST. The package blocks a single cash receipt ≥ ₹2,00,000 and lists structuring (several
sub-₹2L receipts from one party in a day) as an open question. HMP already implements the
per-payer-per-day aggregate (
payer-day-cash-window.ts), withCOALESCE(receiptDate, createdAt)semantics so a backdated receipt attributes to its true day rather than masking or manufacturing a breach; the manager override was deliberately removed (AB#8758); scoping defects were closed across three rounds of AB#12943 with dedicated specs. - Routing. Five typed rule kinds stored as a validated closed discriminated union, versus the package's boolean self/group plus two posting flags.
- Specified premises. Current-law FY declaration model with supply-date selection (see C2).
- Tape chart. Drag-reassign plus drag-from-sidebar for unassigned reservations.
- Breadth. Waitlist, night audit, backdated stay with filed-period guard, e-invoice/IRN aging, GST dead-zone policy, forex, cash drawer, safe, smart lock, self check-in — none of which the package covers.
7. Housekeeping defects found in passing
| Severity | Finding |
|---|---|
| Low | hmp-frontdesk-service/src/groups/CLAUDE.md is 69 bytes containing a shell command — ls src/groups/CLAUDE.md src/groups/claude-docs/ 2>/dev/null | head -3 — not documentation. Someone redirected a command into it. Root CLAUDE.md designates per-module CLAUDE.md as the authoritative state doc for that module, so this one is silently empty. |
| Low | @hmp/constants calculateGSTForRoom (packages/constants/src/index.ts:24-27) returns the superseded 12% band (if (tariff <= 7500) return 12;). Dead code today — the only references are its own spec and comments in night-audit.service.ts:374 recording the migration away from it — but it remains exported and callable, and its spec actively asserts 12. Recommend deleting or marking @deprecated. |
8. Recommendations
- Rule on C1 and C2. Both are recommended as "HMP wins". C2 additionally means the package's doc 03 §1 should be corrected before anyone builds from it — return that finding to whoever authored the package.
- Decide whether Q-18 (C3) must be per-property. HMP has a global one-line flip today. A per-property parameter is net-new work.
- Consider G1 + G2 on their own merits. Per-room posting restrictions and per-folio credit caps are OPERA-standard and independently justifiable; they do not depend on adopting anything else in this package.
- Do not port the package's data model, design system, or build conventions. Single-file HTML, in-memory state, and the warm-paper/Fraunces token set are prototype conventions; the dashboard target is Next.js + shadcn on KeyNova tokens with Satoshi.
- Fix the two housekeeping items — they are one-line changes.
9. Open items this audit did not settle
- No test was run and no environment was probed. Every "aligned" verdict above is a
source-level reading. If any of these need to be certified, that is a separate run through
hmp-infrastructure/certification/certify.mjs. - Whether the 15 registered group flow specs currently pass was not measured — only that they are registered and present on disk.
- Whether the package's UI patterns are worth adopting (rooming-list bulk update + quick-names grid, the OPERA two-panel reservation modal, the per-date rate grid, the Detailed/Summary invoice toggle) is a design judgement deliberately left to the operator; HMP has counterparts for most, of varying maturity, which were not evaluated for UX quality.