Skip to main content

KeyNova Sentinel — PMS Phase 1: state of the platform

Round: sentinel-2026-08-06-09-47 · TEST · freshly onboarded property (138 rooms, 3 types) Build: frontdesk 07f54d5, pod-verified before launch · --suite all --onboard --strict --measured-on


Read this first​

This is the third round. The first two could not be trusted, for reasons that are themselves findings.

  • Round 1 (05-46) ran on a half-seeded fixture. .env.test held a stale SUPER_ADMIN credential, and the onboard script consumed the failed login behind if (sa?.accessToken) with no else — so it printed a clean success block and carried on. Tenants never got ENTERPRISE and no super-admin existed. Specs failed at setup with Cannot assign role 'SUPER_ADMIN', which produced Accountant 0/3 and much of Front Office 1/14 on nothing real.
  • Round 2 was killed as compromised: 11 spec commits landed while it ran, so its isolated-retry pass re-read rewritten specs and its flip detection became meaningless.
  • Round 3 ran on a frozen tree, a verified credential, a seeded group, and a pod-confirmed build. It is the first round that measures the platform rather than the harness.

Headline​

Round 1Round 3
Staff journeys GREEN12 / 5314 / 53
Flows verifiable135 / 141 (96%)135 / 141 (96%)
Flow pass-rate63%62%
Isolated-retry flips45 of 56 (9%)
Evidence screens8889
Element review81 owed89 owed (7 verdicts rejected as stale-build)

By persona​

PersonaGREEN
Room Attendant1/1✅
Housekeeping Supervisor4/6strongest real coverage
Front Office Agent3/14up from 1/14 — round 1 understated it
Duty Manager2/7
Night Auditor2/7
Cashier1/6
Reservations Agent1/9
Accountant0/3⚠ still zero — not only the fixture

The 126 L1 failures are not 126 defects​

Classified by reading every failure message, not by matching substrings:

CountBucketWhat it means
57CascadeA scenario failing because an earlier one did. pos-folio-charge alone is 6 failures from ONE root: beforeAll did not complete the check-in.
31Env room contentionROOM_ALREADY_ASSIGNED on a shared/contended property
25Coverage gap — still asserts nothingBeyond the 17 fixed this session
13New gates firingThe de-vacuum work catching conditions that silently passed before
4Out-of-scope channelNo ResAvenue connection on a fresh tenant, by design

After collapsing cascades, 15 distinct signals remain — and five of those are the same sentence: "check-in did not surface an OPEN folio" (folio-quantity-reconciliation, folio-sharer-split-single-buyer, folio-split-category-complete, open-folio-resolve, folio-transfer-target-number). One root cause presenting five times.

Counting failures instead of causes inflates the defect count roughly fourfold. That error was made twice during this programme and corrected both times.


Why the red count barely moved (45 → 46)​

Seventeen previously-silent skips now fail loudly, so more reds were expected. The count stayed flat because 13 of the new gates fired — in pms-j-night-audit (3), brute-force-lockout (3), cash-refund (2), folio-discount (2), reservation-amend, folio-void-creditnote, tenant-isolation — roughly offsetting improvements elsewhere.

The platform did not get worse. The instrument got sharper. Those 13 conditions were passing silently in round 1.


Confirmed defects​

CardSeverityFinding
AB#11264P1Night audit: the pre-audit report says canProceed, and start() refuses with 400 — in the same run, on the same state. The wizard tells the operator the opposite of what will happen, on an irreversible financial operation.
AB#11267P1inspectRoom is a cross-tenant WRITE — no tenant predicate, never calls getRoom, while all three siblings in the same file do scope. A housekeeping user of tenant A can flip tenant B's room to INSPECTED. Also swallows a failed cross-service room update and returns 201.
AB#11271P2MvHkStaffProductivity.reworkCount is written only as the literal 0 and printed as "Rework Count" on a shipped, exportable operator report. Permanently zero for every attendant on every property.

Fixed and deployed this session: AB#11250 · 11251 · 11252 · 11254 · 11255 · 11256 · 11269 (bad dates → 400 not 500, mutation-verified) · 11268 (the KPI counters that had no writer — unblocked AB#11248's acceptance criteria).

Closed as not-reproducible: AB#11253. The stay-shorten credit posts, persists and reads back correctly — 26,250 → 10,500, conserving to the rupee. The original finding was a fixture artifact: every CHECKED_IN stay on TEST was historical, so a correct implementation credits zero, which is indistinguishable from a credit that failed.


The finding that matters most​

The certification could not fail for much of what it claimed to certify.

Seventeen cases across nine suites — every one a money path — were fixed this session. None was a missing assertion:

Failure modeCount
A skip disarms a downstream money invariant8
The case names the bug in a console message, then passes3
One observable, forgiving cause assumed (404 / bare catch)2
Tautology — the guard skips on exactly what it asserts1
Assertion too weak to fail (one-sided balance check)1
Value computed, then discarded1
Negative control skippable1

Three examples worth stating in full:

  • TC-702 detected currentBusinessDate still null after audit, wrote BUG-CANDIDATE to a console nobody reads, and returned green.
  • TC-694b asserted the GST split only when tax was already present — a room night posted with zero CGST and zero SGST, on a registered supplier, took the else branch and passed.
  • TC-708 asserted balanceAfter <= balanceBefore + TOL and called that "rollback voids the charges". A rollback that did nothing satisfies it.

The platform's most consequential nightly financial operation carried the most assertion-free certification.


Evidence​

89 screens captured, 89 element-reviews owed. Seven previously-recorded verdicts were rejected as stale — recorded against an older build — which is the --measured-on freshness check doing its job.

All 18 blank frames from round 1 are explained, and none was a broken capture: 7 were API-only stages that never declared themselves, and the rest fired a guard or a skip before navigating. A spec that stops before navigating has nothing to photograph; the blank frame was the honest signal.


What is not yet known​

  • Accountant 0/3. Round 1's zero was largely a fixture artifact; round 3's is not, and its cause is not yet established.
  • The five "check-in did not surface an OPEN folio" specs. The helper that swallowed the server's refusal was fixed this session, so the next round will name the actual cause instead of the symptom.
  • 25 cases still assert nothing — the sweep of 17 was not the whole population.
  • unregistered-supplier-zero-gst still fails with Cannot assign role 'SUPER_ADMIN' even after the credential fix, because the spec mints a SUPER_ADMIN using its tenant-admin token. A spec defect, not the fixture.

Verdict​

Not ready to certify, and the reason is measurement before product.

Fourteen of 53 staff journeys are GREEN. But the round's most important output is not that number — it is that a large share of what was previously counted as passing could not have failed. Until the remaining 25 vacuous cases are fixed and the element review is done, a higher GREEN count would not mean more confidence.

The correct next step is not to chase the score. It is to finish making the tests capable of failing, then measure again.