FOLIO-UNIFY operator-decision register
Programme: folio-unification ("one folio, N rooms") · Card: AB#13541 (QA-42)
Gate: tools/registry/src/linters/check-decision-register.ts, asserted by
tools/registry/__tests__/linters/check-decision-register.test.ts, written as GATE 3 of
.azuredevops/pipelines/registry-validation-gate.yml (registry check-decision-register).
Its wiring — reachable from the CLI, invoked by a step with no soft-skip — is asserted by
tools/registry/__tests__/handlers/check-decision-register.test.ts.
⚠ REACH, measured 2026-09-07: that lane is not registered with Azure DevOps, so nothing
starts it. The first version of this header said the gate ran on every pull request to
master; the enumeration of all 30 pipeline definitions in project KeyNova shows none points
under .azuredevops/pipelines/, and hmp-docs carries zero branch policies. Until an operator
performs the two steps in .azuredevops/pipelines/lane-registration.json, run it by hand:
npx --prefix tools/registry tsx tools/registry/src/cli.ts check-decision-register
npx --prefix tools/registry tsx tools/registry/src/cli.ts check-lane-registration
That correction is itself the point of this register: a claim nobody can check is exactly the thing §0.1 refuses, and it had landed inside the fix.
0. Why this file exists
Seventeen numbered operator decisions (D1–D17; D17 added 2026-09-12) govern this programme and shipped code cites them by number.
hmp-frontdesk-service/src/groups/group-payment.service.ts reads "Decision 16: an
aggregate money limit evaluates ONCE across the selection and BLOCKS it." (No line is
pinned, deliberately: this file first cited that comment at :109, and commit fe67e17
moved it to :111 69 minutes later. See §3.1 — a hand-pinned cross-repo anchor is a
claim nothing in this repo can check.) Until this file
existed, the authority that comment points at lived only in whatever each agent was told at
dispatch.
That cost real work inside one week, twice:
- AB#13527 — an agent could not check whether a numbered decision already covered a fail-open posture, because no register existed, and stopped.
- AB#13385 (SC-10) encoded decision 16 into the TYPE SYSTEM of
@hmp/api-contracts— disjoint guard-class unions that make an aggregate money limit written as a per-room skip fail to compile. Excellent work, and the agent said plainly on the card that it could not confirm the ratified record. The same gap is restated on AB#13386: "The three clauses and decision 16 came from the dispatch brief, not from the card, and no FOLIO-UNIFY spec exists under hmp-docs (grep returns nothing)."
A compiler constraint with no citable authority is one the next reader relaxes when it gets in the way. This is the mirror of the false-premise class this programme keeps hitting: not a wrong statement, but no statement at all, so every round re-derives the rule from memory and any drift is undetectable.
0.1 The rule this register is built on
A decision whose evidence could not be found is recorded as
UNSOURCED. It is never reconstructed.
An invented ruling is far worse than a gap. A gap stops the next agent and sends it to the
operator; an invention is indistinguishable from a ratified rule and gets built on. The gate
enforces this: STATUS_EVIDENCE_MISMATCH fires on any decision claiming RATIFIED while
carrying UNSOURCED evidence or no ratification date, and on any claiming UNSOURCED while
carrying real evidence. The two states cannot blur.
14 of the 17 numbered decisions below are UNSOURCED. That is the honest state of the
record as measured on 2026-09-06, not a gap in this file's research — §2 gives the searches.
(D17 was added on 2026-09-12 as RATIFIED, from the operator's ruling on AB#14103; the
fourteen UNSOURCED slots are unchanged.)
1. How to cite a decision — and why "decision 5" alone is ambiguous
At least three unrelated decision series are live on this platform and all three use the bare words "decision N":
| Series | Numbers in use | Where it is cited |
|---|---|---|
| Settings / sidebar IA (AB#9442) | 1–8 | hmp-web-dashboard/src/app/(dashboard)/settings/keynova.tsx:49, .../sidebar.tsx:328 |
| Tax-identity spine (AB#9905, EPIC AB#9896) | 1–2 | hmp-property-service/src/tax-identity/tax-identity.service.ts:583 and :586 |
| FOLIO-UNIFY (this file) | 13, 16 cited so far | hmp-frontdesk-service/src/groups/group-payment.service.ts, and further repos — derive with §3.1's command |
So an unqualified decision 1 in a code comment resolves to two different rulings depending
on which repo you are standing in. Cite as FOLIO-UNIFY decision N and name this file.
The existing bare Decision 16: comments predate this register; they are correct because 16
is unused by the other two series, and they are listed under Cited by on D16 so the binding
is discoverable from both ends.
2. What was searched to build this register
Every command below was run on 2026-09-06 against origin/master of each repo. Re-run them;
do not trust these outputs as current.
# 1. The card's own claim. NOTE: it is CASE-SENSITIVE and therefore understates.
grep -rn "DECISION 16\|guard classes" --include="*.ts" --include="*.md" . .claude # -> 1 unrelated hit
# 2. The same search, case-insensitive — the true on-disk citation population.
grep -rni "decision 16" --include="*.ts" --include="*.tsx" --include="*.md" --include="*.mjs" . .claude
# 3. Every FOLIO-UNIFY work item, its description, its acceptance criteria and its comments.
az boards query --org https://dev.azure.com/CentraLogicIndia --project KeyNova \
--wiql "SELECT [System.Id] FROM WorkItems WHERE [System.Tags] CONTAINS 'FOLIO-UNIFY'"
# then per id: /_apis/wit/workItems/<id>?api-version=7.1
# and /_apis/wit/workItems/<id>/comments?api-version=7.1-preview.3
# grep the lot for: \bdecision\s+(\d{1,2})\b and OPERATOR RULING|RULING RECORDED
# 4. Commit messages, all branches, across the seven programme repos.
git -C <repo> log --all --since=2026-08-25 --grep="decision [0-9]" -iE --oneline
What those searches returned on 2026-09-06:
-
Search 2: three citations, all in
hmp-frontdesk-service(src/groups/group-payment.service.ts:109,src/groups/mt7-group-cash-ceiling.spec.ts:13and:85), plus one statement indocs/superpowers/specs/2026-09-05-folio-unify-guard-class-carve-out-spec.md:4. ⚠ The spec is onhmp-docsorigin/master and is absent from the shared local checkout, which sits on another branch — which is part of why it reads as missing.⚠ CORRECTION, 2026-09-06 (AB#13541, same card, later pass). Search 2 above is a SUBSTANTIAL UNDERCOUNT and is left in place only so the error is visible. It is wrong for two independent reasons, both re-measured and both worth learning from:
- The recorded command is a filesystem
grep -rn, which by construction reads the WORKING COPY — notorigin/master. The shared local checkouts on this machine lag their remotes badly, and §2's own next bullet says so about the carve-out spec without drawing the conclusion for the sweep itself. Re-run the same search asgit grep … origin/masterand it returns many times more citations, across four repos rather than one. Every sweep recorded in this file must name the revision it read. - The recorded pattern
"decision 16"has a SPACE and therefore missesdecision-16. Three hyphenated citations exist onorigin/masterofhmp-frontdesk-service, andsrc/groups/ab13347-group-charge-partial-mutation.spec.ts:25carries only the hyphenated spelling — that file contains no spaced form at all, so the recorded search cannot see it. Use-E 'decision[ -]?16'.
A third trap, met while re-measuring:
git grep -Edoes not honour\b. A pattern ending in\breturns ZERO matches rather than erroring, which reads exactly like "no citations exist". §3.1's command is written without it.The corrected population, the four repos, and the two citation CONFLICTS this re-measure exposed are in §3.1.
- The recorded command is a filesystem
-
Search 3: 233 cards, 169 comments. Only decisions 13 and 16 are cited by number anywhere in Boards. 47 dated
OPERATOR RULINGblocks were recovered and are recorded in §4. -
Search 4: zero commits citing a numbered decision, in any of the seven repos.
Conclusion, stated as a limit and not as a finding about the decisions themselves: the rulings for D1–D12, D14 and D15 may well have been made — this register can only report that no artefact reachable from disk or from Azure Boards records them.
3. The numbered decision series
D1 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 1.
- Ruling: NOT RECORDED. Do not assume this slot is empty because no decision was made — assume only that none was written down. If your work needs it, ask the operator and record the answer here.
- Evidence: UNSOURCED
- Cited by:
D2 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 2.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D3 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 3.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D4 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 4.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D5 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 5.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D6 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 6.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D7 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 7.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D8 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 8.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D9 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 9.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D10 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 10.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D11 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 11.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D12 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 12.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D13 — An early-departing room in a SINGLE-invoice-mode group invoices itself
- Status: RATIFIED
- Ratified: 2026-09-05
- Question: In a group whose
invoiceModeis SINGLE, what happens when one room self-settles and departs before the family closes — does its charge sweep onto the group master at family close, or does that room mint its own fiscal document at its own departure? - Ruling: The room mints its OWN fiscal invoice at its departure and is EXCLUDED from
mintGroupInvoice's sweep. Before this, the sweep took every un-invoiced room's charges onto the master, so a guest who paid cash at early departure had those charges re-billed to the company — money on two documents, a GST Rule 46 / §31 exposure, unrecoverable once the second IRN is filed. The PER_ROOM prohibition on group-routed rooms is KEPT. ✅ The number↔ruling binding was CONFIRMED by the operator on 2026-09-12 (recorded on AB#14103). Until then it was INFERRED: AB#13449 carried the ruling text and its date but not the number; AB#13461's description carried the number ("Decision 13 invoices each self-settling room AT DEPARTURE while the family stays open") but not the full ruling. Extended by D17 the same day — the per-room invoice now applies to every member room holding un-routed charges, not only to an early departure. Consequence, recorded on AB#13461: "a charge arrives for an already-invoiced room" becomes the normal end-state of every early departure, which is whyfolio-invoiced-guard.ts'sTARGET_FOLIO_INVOICEDrefusal needed the reachable exit that ledger row R-13461 settles. - Evidence: AB#13449 · AB#13461 · AB#14103
- Cited by: AB#13461 (description) · AB#14103
D14 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 14.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D15 — UNRECOVERED
- Status: UNSOURCED
- Ratified: UNRECORDED
- Question: Unknown. No artefact reachable by the §2 searches names a FOLIO-UNIFY decision 15.
- Ruling: NOT RECORDED. See D1.
- Evidence: UNSOURCED
- Cited by:
D16 — Guard classes over a multi-room selection
- Status: RATIFIED
- Ratified: 2026-09-05
- Question: The programme lets an operator act on several rooms at once. Every such action meets guards written when the answer was always "one room". For each guard, is it evaluated ONCE over the whole selection and BLOCKS it, or PER ROOM with the failures skipped and named?
- Ruling: Two classes. (1A) AGGREGATE MONEY LIMITS — §269ST cash ceiling, discount approval thresholds, credit limits, any statutory or policy ceiling expressed as a total — evaluate ONCE across the selection and BLOCK it whole. They bind on a party, not a room; a partial application changes the very quantity being measured, so there is no honest "skip". Six rooms of ₹50,000 must not pass a ₹2,00,000 ceiling one room at a time. (1B) STATE AND AUTHORISATION GUARDS — void-on-checked-in, single-use manager approval code, channel acknowledgement, role/state,
If-Match, filed-GST-period, folio OPEN/settled state — evaluate PER ROOM and SKIP the failures, NAMING each with its reason. That room 3 is checked in says nothing about room 4. NEVER bulk-acknowledge on an operator's behalf. The single-use manager code is per-room and consumed atomically: one code authorises one room, and "select all and void" cannot be one click without changing what that authorisation means. A per-room guard returns{ applied, skipped: [{ roomId, reason }], total }and an emptyskippedon a partial application is a defect, not a tidy result. - Evidence: AB#13341 · docs/superpowers/specs/2026-09-05-folio-unify-guard-class-carve-out-spec.md
- Cited by: SEE §3.1 — the citation population is DERIVED, not listed here, because a hand-kept list went stale inside one day. Citations live in four repos (hmp-frontdesk-service, hmp-shared-packages, hmp-web-dashboard, hmp-docs); run §3.1's command. Load-bearing anchors: hmp-frontdesk-service/src/groups/group-payment.service.ts (the comment AB#13541 was raised over; the line MOVED from :109 to :111 in commit fe67e17, which is why a line number is not pinned here) ·
@hmp/api-contractspackages/api-contracts/src/selection-verdict.ts(AB#13385, SC-10 — the disjointPER_ROOM_GUARD_CLASSES/SELECTION_GUARD_CLASSESunions that make the wrong class fail to COMPILE; the most binding statement of D16 anywhere) ·hmp-web-dashboard/src/lib/hooks/use-room-selection.ts(AB#13386, SC-3 —whole/perRoomoff ONE array) · AB#13452 (SC-6, to encode the classes as a registry with a call-site gate) · ⚠ TWO CONFLICTING readings, unresolved, in §3.1
D16 has the fullest record of any decision here. The measured precedents behind it (MT-1's USD 3,000 cash tender measured as "3000" against a rupee ceiling; MT-11's room-scoped discount that applied to nothing and reported nothing; MT-20's two-sided filed-period axis), the five anti-patterns the programme actually hit, and the per-class test obligations are in the spec at
docs/superpowers/specs/2026-09-05-folio-unify-guard-class-carve-out-spec.md. Read it there; it is not restated here. One definition, cited — not copied.
D17 — In a SINGLE-invoice group, each line is invoiced to the payer its routing rule names
- Status: RATIFIED
- Ratified: 2026-09-12
- Question: D13 covers a room that departs EARLY. What about a member room that checks out with the group but still holds charges its routing rule does not send to the company — minibar, restaurant, laundry under ROOM_AND_TAX? Are those swept onto the consolidated group invoice, or invoiced to the guest?
- Ruling: Extends D13 to every member room. SINGLE means ONE invoice for the ROUTED supplies — it never means "no invoice for what is left on a room folio". A member folio holding at least one live taxable CHARGE gets its OWN per-room tax invoice at that member's checkout; lines the member's routing rule assigns to the company reach the GROUP_MASTER; guest lines are NEVER swept onto the company's invoice; a folio whose lines were all routed (or that holds only payments) gets no serial. By rule: ROOM_AND_TAX sends the ROOM category (including EXTRA_BED, which is accommodation) to the master and everything else to the guest; ALL sends everything to the master; NONE keeps everything on the room's own invoice. The invariant: every live CHARGE in a closed SINGLE group sits on exactly one numbered document, and that document's recipient is the payer the line's routing rule names. Measured need: the AB#14103 TEST probe (2026-09-11) — owner-first and owner-last checkouts left guest extras on NO tax invoice, and a master left OPEN billed guest-paid extras to the company a second time. Recorded by the operator together with the D13 confirmation; the hospitality-sme's AB#14103 rulings that implement it are ledger rows R-14103a–R-14103g.
- Evidence: AB#14103 · AB#13449
- Cited by: AB#14103
3.1 Citation drift on D16 — measured, and the two conflicts it exposes
This register's Cited by lists were hand-collected. Hand-collected lists go stale, and this
one went stale inside a day. Re-derive, never trust the list:
# Every citation of the numbered series, per repo, on origin/master.
# NOTE: git grep -E does NOT honour \b — a pattern ending in \b silently returns ZERO.
for r in hmp-frontdesk-service hmp-shared-packages hmp-web-dashboard hmp-docs; do
(cd "$r" && git fetch -q origin master && \
echo "$r $(git grep -niE 'decision[ -]?16' origin/master | wc -l)")
done
Run on 2026-09-06 that command found citations in four repos, not the one this file
originally recorded — hmp-frontdesk-service, hmp-shared-packages, hmp-web-dashboard and
hmp-docs itself. Measured against the frontdesk commit that was origin/master at the moment
this register was committed (31aa352, 2026-09-06 09:09 IST), that repo alone already carried
far more D16 citations than the three recorded below — including both of the two conflicting
readings named next, which were therefore on disk and unnoticed when D16 was first written.
No count is written into this file. Run the command.
The two conflicts — reported, NOT resolved
The card that created this register asked for exactly this: where two citations of the SAME decision assume DIFFERENT things, say so loudly. Neither conflict below is settled here. Settling either is an operator/domain ruling, and inventing the answer in this file is the one thing this register forbids.
CONFLICT A — a ruling about ROLE BUCKETS is attributed to D16.
hmp-frontdesk-service/src/folios/privilege/folio-privilege.config.spec.ts:113 (landed
2026-09-06 00:35, commit 3e4a668, AB#13390 UG-7) asserts:
// DECISION 16 (ratified): SUPER_ADMIN sits in the MANAGER bucket, OWNER has full folio authority.
D16 as recorded above says nothing whatever about SUPER_ADMIN, OWNER, or role buckets — it
is about whether a guard is evaluated once across a selection or per room. A reader who follows
that citation to this register finds no such ruling. Two readings are possible and this file
cannot choose between them:
- the role-bucket ruling is a different decision that was mis-numbered as 16, in which case it belongs in its own slot and the comment must be corrected; or
- D16 as ratified was broader than the guard-class carve-out that AB#13341's spec records, in which case D16's Ruling here is incomplete.
Do not "tidy" this by deleting the comment or by widening D16's Ruling. Ask the operator which it is, record the answer on a card, then cite that card here.
CONFLICT B — PER ROOM versus PER FOLIO, and whether an empty skipped is permitted.
D16 above states the class-1B guards evaluate "PER ROOM and SKIP the failures, NAMING each",
and that "an empty skipped on a partial application is a defect, not a tidy result". Two
shipped sites read the same decision at a different grain and claim an exemption from that
clause:
hmp-frontdesk-service/src/folios/folios.controller.ts:1889— "DECISION 16: a state+authorisation guard stays PER FOLIO. There are no individual skips here and that is not a bulk-acknowledge — a merge is atomic."hmp-frontdesk-service/src/folios/privilege/ug7-move-routes-enforce.spec.ts:200— "DECISION 16 — an authorisation guard is per folio, but a merge is atomic."
Against a third site that reaches the OPPOSITE posture from the same decision and reasons the gap out loud rather than assuming it — the well-behaved citation, and the model for the others:
hmp-frontdesk-service/src/folios/folio-posting-gate.ts:102— "WHY IT IS ALL-OR-NOTHING, AND WHY THAT IS NOT A DECISION-16 VIOLATION", which argues a bulk MOVE is N rows leaving ONE folio so there is no sibling room to hold back, and then explicitly records the residual question as UNVERIFIED on AB#13577 and routes it to the domain owner instead of guessing.
Since AB#13244 each room carries its own reservation limb and its own folio, so per room and
per folio coincide for a room folio — and do not coincide for a group master folio or
for a merged folio, which is precisely where these sites operate. Whether "atomic operation"
is a sanctioned carve-out from the named-skips clause, and at which grain, is a grain
question for the domain owner (hospitality-sme, or frontdesk-money-pod once it exists).
It is recorded here as open.
What this means for Cited by generally
A Cited by list in this file is a convenience for the reader, never evidence. The gate
resolves in-repo docs/ anchors only; it cannot see the other repos (see §6.3), so a stale
cross-repo anchor cannot be detected here. Re-derive with the command above before relying on
any of them.
4. Ratified operator-ruling ledger
Dated rulings recovered from FOLIO-UNIFY card comments by the §2 search. These are not numbered decisions — none of them carries a decision number in any artefact, and assigning one here would be exactly the invention §0.1 forbids. They are keyed by their card. When an operator confirms that one of these IS a numbered decision, move it into §3 and record the binding.
| Ruling | Card | Date | The ruling, as recorded |
|---|---|---|---|
| R-13342 | AB#13342 | 2026-09-05 | L1 flow lane runs N=4 jest workers in ONE ADO job (1 of 3 pool slots). |
| R-13343 | AB#13343 | 2026-09-05 | Lockfile-completeness detector wired ADVISORY, never blocking — no npm invocation repairs the lock, so a strict gate would red the lane for a condition nobody can fix. |
| R-13355 | AB#13355 | 2026-09-05 | On a room-type/occupancy change to a CHECKED_IN room, reprice the future un-audited nights; refuse only when the change would touch an already-audited or invoiced night. The manager gate applies only when the user OVERRIDES the repriced amount, not to the reprice itself — the override is submitted and pends approval rather than hard-blocking. |
| R-13357 | AB#13357 | 2026-09-05 | Undo-check-in revokes the key card first, then refuses only on a survivor. The guard still re-reads the DB, so a room is never released while a WORKING card exists; only a FAILED revoke refuses. |
| R-13358a | AB#13358 | 2026-09-05 | SUPERSEDED by R-13358b the same day. Read "PROSPECTIVE ONLY" — a post-booking tax-flag flip applies to new lines only. |
| R-13358b | AB#13358 | 2026-09-05 | SUPERSEDES R-13358a. Re-tax the un-invoiced, un-audited lines. The first ruling was proven unimplementable: folio-at-booking.service.ts pre-posts the ENTIRE stay on reservation.created, so by the time anyone can flip the flag there are no un-posted future nights left and "prospective only" would have applied to an EMPTY SET. A flip now VOIDS AND REPOSTS the posted-but-not-invoiced, not-audited room-night lines through the same engine RD-10 drives. The first ruling's INTENT — never touch a filed or audited night — is preserved unchanged. |
| R-13362 | AB#13362 | 2026-09-05 | A group check-in names the line and reports the skip rather than silently early-arriving anyone; a typed skip per room (ARRIVAL_IN_FUTURE / ARRIVAL_IN_PAST) instead of dropping the room. Explicitly "the decision-16 shape". |
| R-13366 | AB#13366 | 2026-09-05 | Grant all four live access gaps (folios/charge-codes FRONT_DESK, ar-aging-detail MANAGER, gstr1/gstr3b ACCOUNTS) — GSTR-1/3B were readable by MANAGER but not by the accounts team that files them. OWNER gets full authority (FRONT_DESK + MANAGER + ACCOUNTS); OWNER previously satisfied nothing, which was an omission and never a decision. BLOCKED / CREDIT_NOTE remain satisfied by nobody, OWNER included — no role re-opens a night-audited day. |
| R-13374 | AB#13374 | 2026-09-05 | ENFORCE capPerNight, in group-charge-routing.consumer.ts and nowhere else, as a per-night ceiling on the routed ROOM charge with the EXCESS remaining on the room's own folio — never dropped, never silently truncated. |
| R-13381 | AB#13381 | 2026-09-05 | Re-point "Open folio" at the canonical full folio screen (/front-desk/folio/:reservationId); keep in-place rail selection as the row-click behaviour. |
| R-13387 | AB#13387 | 2026-09-05 | Cap a room selection at 50, matching CheckInDto and the room-block staff routes — one number for room-set operations platform-wide. A block larger than 50 rooms PAGES; the cap is not raised. |
| R-13390 | AB#13390 | 2026-09-05 | Seed each new FolioAction column by INHERITING an existing one, and admit only actions that have (or gain in the same release) a real enforcement call site. Money-moving-between-folios (TRANSFER/SPLIT/MERGE/ROUTE) inherits VOID; state transitions (CHECK_IN/CHECK_OUT/CANCEL) inherit EDIT; the AUDITED stage stays BLOCKED/CREDIT_NOTE. |
| R-13393 | AB#13393 | 2026-09-05 | Replay only NON-MONEY writes through the one mapper. REFUSE every money-mutating queued action (payment, charge, void, discount) with an operator-visible message naming the room and the amount — never dropped silently. A money write queued pre-flip cannot be re-consented to at drain time. |
| R-13394 | AB#13394 | 2026-09-05 | Flip authority = the property's own ADMIN plus SUPER_ADMIN, the same authority CS-14's audited switch records. Certify the ENTIRE programme on TEST first, then flip ALL live properties in one go after initial checking — the canary-then-batches sequence applies to non-prod only. |
| R-13406 | AB#13406 | 2026-09-05 | The manager-approval token is NOT forwarded to the ADD_PAYMENT privilege gate: tokens are single-use and action-bound and a caller can supply exactly one, so a third consumer would starve the backdate gate. ADD_PAYMENT is satisfied by role alone and fails CLOSED. ADD_CHARGE has no competing consumer and does elevate on the header. |
| R-13424 | AB#13424 | 2026-09-05 | ELEVEN tabs, Audit stays its OWN tab (operator's amendment; the recommendation folded it into Notes): Ledger, Post charge, Payment, Discount, Routing, Guest folios, Invoices, Booking, Guest, Notes, Audit. Declared ONCE as an exported constant. ⚠ The card title says "ten" and is wrong. |
| R-13429 | AB#13429 | 2026-09-05 | Allocation edits refuse below pickup with a named error (ALLOCATION_BELOW_PICKUP) and gain an explicit removal semantic (delete, or quantity: 0 meaning released). |
| R-13430 | AB#13430 | 2026-09-05 | OMIT capPerNight from the block create/edit form for now; expose only kind. At EDIT, disclose that the block default is stamped AT PICKUP and does NOT retro-apply. |
| R-13438 | AB#13438 | 2026-09-05 | Persist a structured exemption GROUND plus a DOCUMENT REFERENCE, mandatory whenever isTaxExempt or isReverseChargeApplicable is turned ON, and carry it onto the invoice. Today's only evidence is a manager-role check at the moment of the click — which records who clicked, not why the supply is exempt. |
| R-13442 | AB#13442 | 2026-09-05 | Two INDEPENDENT derived axes, and change no guard: settlementState = SETTLED | OUTSTANDING | ROUTED_AWAY and invoiceState = NONE | PENDING_FAMILY | ISSUED. A room routing to the group master reports ROUTED_AWAY, not OUTSTANDING. This is a reporting ruling — it must not relax the checkout guards. |
| R-13449 | AB#13449 | 2026-09-05 | See D13. |
| R-13453 | AB#13453 | 2026-09-05 | Remove the role laundering, keep the validate-once token. Delete the synthesised { role: 'MANAGER' } so each per-line void is evaluated under the caller's ACTUAL role and that room's own state; ONE group-scoped token is still validated ONCE at the boundary. The defect was never the single validation — it was that clearing one group gate handed MANAGER authority over lines the caller could not have voided individually. |
| R-13457 | AB#13457 | 2026-09-05 | Discount approval threshold = Σ(discount) / Σ(selected folio balances) — ONE ratio over the whole selection, so the amount and percent limbs cannot disagree about what "the selection" is. The refusal must NAME which limb tripped. |
| R-13458 | AB#13458 | 2026-09-05 | §269ST payer for a multi-room selection = company first, else the booking's primary guest, evaluated ONCE over the whole selection and blocking it whole (explicitly "decision 16"). Payer and tenant come from server-side booking data, never a caller header (AB#12943). |
| R-13460a | AB#13460 | 2026-09-05 | The privilege matrix's MinRole is canonical. Map the canonical UserRole onto the safeguard buckets at the frontdesk enforcement sites, NOT by re-keying @hmp/infra's SafeguardConfig. An unrecognised role FAILS CLOSED to the tightest limit. |
| R-13460b | AB#13460 | 2026-09-05 | SUPER_ADMIN maps to the manager safeguard bucket, NOT admin. The matrix tops out at MANAGER. Mapping it to admin would have moved SUPER_ADMIN's charge-posting ceiling from ₹10,000 to ₹999,999,999 — a money widening no ruling authorised, arriving disguised as a vocabulary reconcile. The resulting ceiling must be PINNED by a spec. |
| R-13461 | AB#13461 | 2026-09-05 | ACCEPT THE MANUAL EXIT for this release. Do NOT pull AB#10627's §34(3) debit-note instrument into folio-unification — it is a full compliance workstream and AB#10627 is still Proposed. |
| R-13489 | AB#13489 | 2026-09-05 | Group cash ceiling: tenant is resolved off the GROUP (not the caller's header — the gateway forwards a client-chosen x-property-id when the JWT carries no claim, AB#12943), and the payer is the company if present, else the owner guest. Aggregate money limit ⇒ evaluates once, blocks whole. |
| R-13490 | AB#13490 | 2026-09-05 | Unattributed cash: BOTH — return the outcome in the postPayment response for immediate disclosure AND write a durable marker in the same transaction as the folio + drawer legs. The payment must NOT fail closed on a missing drawer shift (that earlier ruling stands). |
| R-13494 | AB#13494 | 2026-09-05 | THREE document kinds, not two. (1) REGISTERED supplier + fully-exempt/nil-rated settled folio → BILL OF SUPPLY (Rule 49), no GST columns, own gapless series, never consuming the tax-invoice serial. (2) UNREGISTERED supplier → plain commercial INVOICE on a third non-GST series — calling it a Bill of Supply asserts a registration the property does not hold. (3) Registered + taxable → TAX INVOICE. |
| R-13495 | AB#13495 | 2026-09-05 | Foreign-currency tender: refuse and re-disclose on FX drift. Server returns {rate, rateId/asOf, inrEquivalent}; the client posts back the rate REFERENCE, never the number; the server revalidates and 409s naming the new figure. |
| R-13500 | AB#13500 | 2026-09-05 | Flip-gate freshness binds to the BUILD, not the clock: a green counts only if it ran against the exact image SHA now serving that property's environment — read from the kustomize overlay newTag, never inferred from /api/health — and is the most recent run for that property's key. |
| R-13501 | AB#13501 | 2026-09-05 | BOTH, split by layer, recorded as DATA not prose. The flip gate's green comes from a full L1+L2 run against a representative TEST property mirroring the live one; the LIVE property gets a read-only probe subset only. The money-moving specs must be an explicit exclusion list the runner READS, never a prose caveat. |
| R-13503 | AB#13503 | 2026-09-05 | Enforce the flip gate in the CAMPAIGN SCRIPT only — operator's decision, against the recommendation to enforce it in hmp-property-service. Recorded consequence, so it is a known and accepted trade: PATCH /properties/:id stays directly callable and the gate protects the campaign path, not the endpoint. Revisit if a property is ever found flipped without a matching certification record. |
| R-13505 | AB#13505 | 2026-09-05 | Split the campaign by environment. Dev/QA/staging: flip autonomously behind the CS-18 evidence gate, recording BEFORE/AFTER inventory per batch. Production: named operator approval recorded on the card before the flip, and the deliberate rollback exercise happens in STAGING, never prod. |
| R-13506 | AB#13506 | 2026-09-05 | Invert the column default to ON and the reader's fallback to ON, and KEEP the switch. Delete it later inside CS-19 — CS-23's own acceptance requires a CS-22 inventory re-run that READS the switch, so deleting it now would make that criterion unmeasurable. |
| R-13517 | AB#13517 | 2026-09-05 | KEEP Reservation.totalAmount and the folio DIVERGENT. totalAmount is the CONTRACTED (sold) value; the folio is the BILLED value. An in-stay upgrade legitimately separates them. The ruling is not "do nothing" — it creates the obligation to name which surfaces may read totalAmount and to point revenue reporting at the folio. |
| R-13520a | AB#13520 | 2026-09-05 | State-code mismatch: NORMALISE AT COMPARISON + REFUSE AT WRITE. Map alpha→numeric where coversState is called, and refuse at write time when a code cannot be mapped — turning a SILENT failure into one the operator sees immediately, without a data migration. |
| R-13520b | AB#13520 | 2026-09-06 | DATA FIRST, THEN CODE. SUPERSEDES the earlier same-day "ship fix + dedupe together". Order: (1) DELETE or RE-BAND the unbanded KA/ROOM/996311 rows — not dedupe, because one unbanded row wins exactly as decisively as six; (2) fix itcEligible (post-22.09.2025 low band is 5% WITHOUT ITC); (3) bound effectiveFrom/effectiveTo so the repair cannot restate correct historical invoices; (4) THEN ship the coversState fix; (5) THEN add the schema guard. Shipping the code before the data CREATES a tax defect that does not exist today. |
| R-13520c | AB#13520 | 2026-09-06 | VERIFY EXPOSURE ON TEST; PRODUCTION IS THE OPERATOR'S. Establish inertness EMPIRICALLY by reading the rate actually STAMPED on issued invoices, explicitly NOT by inferring it from the resolver. The TEST read is authorised; the production read is reserved to the operator. |
| R-13564 | AB#13564 | 2026-09-06 | "Revenue for a realised stay" = ROOM REVENUE. The reservation.checked_out field's basis is CORRECT and the field is merely STALE, so repoint it at the ACTUAL room revenue realised — do NOT repoint it at folio.totalCharges, which double-counts POS that pos.consumer and folio.consumer already book independently. The CommissionBase enum was a strong prior, not a decision, which is why this was put to the operator. |
| R-14103a | AB#14103 | 2026-09-12 | D13's number↔ruling binding CONFIRMED, and D17 ratified — every member room holding un-routed charges gets its own per-room tax invoice at checkout; only company-routed lines reach the group invoice. See D13 and D17. |
| R-14103b | AB#14103 | 2026-09-12 | Posting-master lifecycle (hospitality-sme ruling 3, recorded by the operator as the AB#14103 design basis): the GROUP_MASTER belongs to the GROUP, not to the owner's room, so no room checkout settles it. A deliberate Group Close freezes postings, then in ONE transaction numbers the master, applies advances and posts the remaining debit to the company's city ledger. Never number an OPEN master. |
| R-14103c | AB#14103 | 2026-09-12 | Never move a charge off a SETTLED, numbered or paid folio — enforced ONCE, as a non-OPEN-source refusal in transferCharge, so it covers the mint sweep, "Transfer all to owner", moveExisting and reactive routing alike. The sweep honours each member's routing rule; a company-payable line that cannot reach the master makes the mint fail closed, named per D16 — never a silent omission. |
| R-14103d | AB#14103 | 2026-09-12 | Probe S3's ₹840: the guest owed it and paid it; the company was over-billed. If no tax document went out (no IRN filed, nothing delivered, serial not in a filed GSTR-1), reverse the moved lines as an audited manager correction and finalise the master under the same serial. Otherwise a §34 credit note to the company, plus a per-room invoice (or a manual debit note under R-13461) to the guest. Refunding the guest instead is a human decision, never an automatic one. |
| R-14103e | AB#14103 | 2026-09-12 | The consolidated group invoice is a deliberate Group Close by the Front Office Manager or Accounts — at the last checkout the platform creates a close task carrying the Rule 47 due date. A property opt-in auto-issue is allowed only behind the full precondition list recorded on AB#14105, which is BLOCKED-BY AB#14103: repairing the broken auto-mint on its own makes billing worse. |
| R-14103f | AB#14103 | 2026-09-12 | A SINGLE group carries a mandatory group-level billing instruction, ROOM_AND_TAX pre-selected, stamped onto each room at pickup (never applied at read time). A per-room override is allowed and applies going forward only; a SINGLE member with no routing rule is refused at checkout until the instruction is set. |
| R-14103g | AB#14103 | 2026-09-12 | PROD read-only classification probe AUTHORISED before the 11 Oct GSTR-1 (classes a1 · a2 · b · c · d, including group 5ed3fa04): the coordinator runs a SELECT-only script inside a READ ONLY transaction and the operator approves the permission prompt. An interim advisory to properties is drafted for the operator to send. |
| R-14103h | AB#14103 | 2026-09-12 | Operator values for Group Close. Late-charge window 2 hours after the last departure — configurable per property 0–24 h, a SOFT gate: the Front Office Manager or Accounts may close earlier with a reason code, and later charges go by debit note. Orphan-scan threshold 24 hours after the last departure, on the next night audit's exceptions list (Rule 47 reminders at day 20 and 25 unchanged). One-time audited routing stamp APPROVED, previewed: each property's FOM confirms each in-house SINGLE group's contract rule; unconfirmed groups get ROOM_AND_TAX at go-live, one audit row per member. |
| R-14103i | AB#14103 | 2026-09-12 | The group invoice-mode lock keys on the CONSOLIDATION, not on a member's serial (hospitality-sme, operator-confirmed): no switch away from SINGLE once the GROUP_MASTER holds any live charge or an invoice number; any change refused once the master is numbered; a room's own per-room serial no longer locks; the comparison is against the EFFECTIVE mode. Closes a P0 under-block — today a routed SINGLE group can be flipped to PER_ROOM with the master's charges stranded on no invoice. |
| R-14103j | AB#14103 | 2026-09-12 | A group's effective billing mode is STAMPED onto the group at creation (downgraded to PER_ROOM when a SINGLE group's company has no GSTIN), with a one-time audited backfill of unset in-flight groups at go-live; a later property-default change applies only to NEW groups. Computed once from the existing cascade — never a second copy of the resolution logic. |
| R-14101a | AB#14101 | 2026-09-12 | The SERVER publishes each group's EFFECTIVE billing mode (the group's own invoiceMode, else Property.defaultGroupInvoiceMode) through a frontdesk read that lands before AB#14101's dashboard change, and the Group invoice panel treats a group with no mode of its own exactly like a SINGLE or PER_ROOM group. The dashboard still never computes the mode itself (the AB#13486 ruling stands); a failed read is "unknown" — Generate hidden — never a silent PER_ROOM. |
| R-14103k | AB#14103 | 2026-09-12 | The payer of a line is the payer of the folio its routing rule names as that line's target — for all five rule kinds (hospitality-sme, applying D17's ratified principle to the two kinds D17's text does not list). ROOM_AND_TAX: room to the master (company), extras to the guest. ALL: both to the master. NONE (and NULL or unknown): both to the guest. NON_ROOM_TO_DESIGNATED: the room stays with the guest and the non-room lines go to the designated folio, or to the master when none is designated — the inverse of ROOM_AND_TAX. SPLIT_BY_CATEGORY: to that reservation's own accommodation and incidentals folios, never the master. The Group Close sweep and the checkout routing gate move a line to its rule's TARGET through ONE resolver shared with the routing consumer — never "the rule routes this category, therefore the master". |
| R-14103l | AB#14103 | 2026-09-12 | A row that changes the value of a supply belongs on the same document as that supply (hospitality-sme; CGST s.15(3)(a)). The state-levy TAX row, the DISCOUNT negative CHARGE and a shortened-stay credit for a routed night follow their parent line, and the movers carry parent and linked rows in one transaction. Settlement rows — rounding, TDS, refunds, credit-note lines, payments — stay with the folio whose balance they settle and are outside the invariant. The group invariants therefore count live supply CHARGE rows, excluding DISCOUNT and TRANSFER. Compliance confirmation is outstanding on the goodwill post-tax adjustment. |
| R-14103m | AB#14103 | 2026-09-12 | A GROUP_MASTER left in credit is REFUNDED BEFORE the group closes (operator, from options: refund before close · build a company on-account credit first). Group Close refuses while the master's balance is below −₹0.005, and its checklist offers one action — refund the excess advance to the company, with a reason code and a Rule 51 refund voucher, through the existing refund primitive. Verified first: the company city ledger cannot hold an unapplied credit and a payment cannot be moved between folios. No FROZEN status is introduced and the numbered-document invariants keep no exception. "Hold on the company's account" is a P2 follow-up, not this card. |
| R-14103n | AB#14103 | 2026-09-12 | A no-show or cancellation retention fee is billed to whoever the room's own routing rule names (operator, from options: follow the rule · always the company · always the guest). Retention lines are ROOM category, so they are company-payable under ROOM_AND_TAX or ALL and reach the group invoice, and guest-payable under NONE, where that member's folio is settled and numbered, or the fee is waived with a reason. Group Close enumerates members in every status and fails closed on a live supply line that is neither numbered nor on the master; a VOID member holding live charges blocks the close as a data anomaly. |
| R-14103o | AB#14103 | 2026-09-12 | At go-live, a SINGLE group with no company GSTIN is confirmed by the Front Office Manager inside the preview window, and any group left unconfirmed is downgraded to PER_ROOM with one audit row (operator, from options: FOM-confirmed downgrade · automatic downgrade). This extends R-14103j's backfill from unset groups to stored-SINGLE groups whose company has no GSTIN. Measured on PROD 2026-09-12: 3 of 3 stored-SINGLE groups have no company at all, and the routing-completeness gate would otherwise block every checkout in them. |
| R-14103p | AB#14103 | 2026-09-12 | A missing tax invoice is issued IN THE PRODUCT by the property, never minted for it by KeyNova (operator, in their own words: "In the platform we should have Issue Invoice if it is not already issued, we will push the current build to PROD tonight so Live properties on the PROD can issue the invoices where it is pending"). An explicit, audited Issue tax invoice action (AB#14103 action item, Boards #14144) is built for a folio that is SETTLED, holds supply lines and has no invoice number — FOM or Accounts, with a reason, dated the day of issue and never backdated, refusing a GROUP_MASTER, a GROUP_DEPOSIT and a TRANSFERRED folio. It ships in the same release as AB#14101, and AB#14101's removal of the read-time mint lands with or after it. Target for the 5 PROD folios found by the R-14103g probe (₹18,773.66): issued before 30 Sep 2026, so the tax and the invoice fall in the same September GSTR-1. |
| R-15202a | AB#15202 | 2026-09-25 | R26 — Undo Check-In keeps the folio and every charge and payment on it; only the check-in status reverts; a re-check-in reuses the same folio (the Product Manager requirement on the card, adopted exactly). SUPERSEDES, for undo-check-in ONLY: operator decision #26 "STRICT empty-folio undo", AB#9803 "undo voids the folio" and AB#11971 "money taken blocks undo" — their text is kept at its sources as history (§6 rule 4). AB#9803's general rule (a VOID folio contributes ₹0) is unchanged for merge and reservation void. |
| R-15202b | AB#15202 | 2026-09-25 | R27 — the reservation-service POST /reservations/:id/undo-check-in primitive is service-account (internal) only. Staff always undo through the front-desk orchestrator (POST /check-in/:id/undo); the primitive keeps its id + propertyId + property.tenantId re-scope because a service-account token accepts a client x-property-id. |
| R-15202c | AB#15202 | 2026-09-25 | R28 — already-affected stays (voided by the old undo) get a READ-ONLY inventory only. The hotel decides and makes any correction; no data write is in scope. |
4.1 Rulings that carry a compliance caveat
- R-13520b / R-13520c sit downstream of a compliance review that recorded its own sourcing
limit verbatim: "primary statutory text could NOT be retrieved (CBIC notification portal
returns 401 to unauthenticated callers; legacy static paths 404; GST Council list is
client-rendered). This is professional recall, not gazette text... Two items are MEDIUM
confidence and need a CA." Do not treat the GST rate timeline on AB#13520 as citable
statutory authority. Route statutory questions to
compliance-checker/ a CA.
5. Rulings relayed but recorded NOWHERE — the live instance of this defect class
AB#13548 (SCOPE-1). The card's description is headed "OPERATOR RULING NEEDED — SCOPE" and sets out the four gap-audit gaps G1–G4 with a recommendation. Its single comment (2026-09-06) is a close-out readiness assessment. Neither contains a ruling.
A ruling was nonetheless relayed to this card's own author in the QA-42 dispatch brief on 2026-09-06, in these terms: "all four gap-audit gaps G1–G4 are IN SCOPE; C3/Q-18 bundled into the MT-28 CA pass."
It is recorded here as RELAYED, NOT RATIFIED ON THE CARD, and deliberately not entered in §3 or §4:
- It is precisely the failure mode AB#13541 exists to end — a ruling that lives in a dispatch prompt. Entering it in the ledger as if it were sourced would reproduce, inside the register, the defect the register was built to close.
- Action for the operator: post it as a comment on AB#13548, then move it into §4 citing that comment. Until then, an agent working G1–G4 has no citable authority for their scope.
The register's related measured facts, for whoever posts that ruling:
| Gap | Audit finding, verbatim from docs/platform-registry/2026-08-30-group-folio-developer-package-gap-audit.md §5 | Card |
|---|---|---|
| G1 | PER-ROOM POSTING RESTRICTION (block F&B, noResto). ABSENT — HMP's five routing rules decide a charge's DESTINATION, never its PERMISSION. | none |
| G2 | PER-ROOM / PER-FOLIO CREDIT CAP ("post till limit"). PARTIAL — every creditLimit hit is company-grain. | none |
| G3 | FOLIO-ATTACHED TASKS / TRACES. ABSENT at folio grain. | none |
| G4 | DEPOSIT ALLOCATION BASIS. SEMANTIC DIVERGENCE, not an absence — the package's pro-rata splits by each room's share of the CHARGE; HMP's ROOM_WISE splits by each room's OUTSTANDING. Identical on a clean group; they diverge once any room has paid. | none |
6. Adding, citing and superseding
- Adding a numbered decision. Add a
### D<n> — <title>block with all six fields. The series must stay contiguous from D1 — a hole is exactly how a citation of "decision 7" resolves to nothing and gets re-litigated from memory, so record an unknown slot asUNSOURCEDrather than skipping it. - Sourcing one of D1–D12 / D14 / D15. Post the ruling as a comment on its card first,
then fill the block citing that comment.
Status: RATIFIEDrequires a realEvidenceanchor and an ISORatifieddate; the gate refuses the pair otherwise. - Citing from code. Write
FOLIO-UNIFY decision Nand name this file. Cross-repo anchors are recorded for the human and are not resolved by the gate — hmp-docs CI checks out only hmp-docs and hmp-infrastructure, so a cross-repo check here would be a green no-op. Because of that blindness, do NOT keep a hand-written cross-repoCited bylist: write the derivation command instead (§3.1). The first such list was stale in a day and its one pinned line number in 69 minutes. ⚠ Never pin a cross-repo:line. Name the file and the identifier; a line number is a claim nothing in this repo can check, and it decays on the next unrelated edit above it. - Superseding. Never edit a ruling in place. Set the old block's
Status: SUPERSEDED, leave its text intact, and add the new one — R-13358a/b and R-13520a/b in §4 are the worked example, and the reason is on AB#13358: the superseding ruling exists because the first was proven unimplementable, and deleting it would delete the evidence of why. - Never copy a ruling between files. Cite it. The money-conservation sentence was copied between two agent files on this platform and every copy inherited the same too-narrow scope.