Known Issues & Discrepancies
Version 1 · Initial known-issues page: discrepancies against the original design doc plus what's intentionally not built yet — derived from docs/PROJECT_DOCUMENTATION.md §11-12
GateKeeper — Known Issues & Discrepancies
Found by comparing the running code against entitlement-system-design.md and the seed data. None block local development, but worth knowing before building on top of this. See Architecture for the systems these refer to.
DataType is a hybrid, not a pure JSON key
The design doc states dimension codes are "just JSON keys ... rather than a database lookup table." In practice data_type got a real data_types table so the AdminUI can offer a managed multi-select when building package rules — but there's still no FK from packages.rules/customer_overrides.constraints back to it, since those remain JSON. Practical effect: deleting a DataType row doesn't check or block packages that still reference its code in their JSON rules — the package just stops offering that value in the AdminUI dropdown, it doesn't lose the underlying grant.
Cache TTL behavior differs from the design doc
The design doc described a 3–5 s L1 range and a sliding 5-minute Redis TTL (refreshed on read). The shipped implementation uses a fixed 4 s L1 TTL with no sliding refresh on hits in either tier; the Redis TTL, previously hardcoded at 5 minutes, is now configurable (Cache:TokenTtlMinutes / Cache:EntitlementTtlMinutes, default 5, 0 = no expiry — see Architecture). Freshness is still achieved via push invalidation regardless of TTL, so this mostly affects idle-customer cache-warmth, not correctness.
Seed script references customers that don't exist
gatekeeper-schema.sql's customer_subscriptions and customer_overrides INSERTs use customer ids 101, 205, 310 — the illustrative ids from the design doc's worked examples — but the same file only creates customers 401 (CWS) and 402 (WSB). Both tables have FOREIGN KEY ... REFERENCES customers(id), so running the script unmodified against a clean database raises a foreign-key violation on those INSERTs (or silently no-ops if 101/205/310 already exist from a previous run). Action needed: either remove those seed rows or repoint them at 401/402 before relying on this script as a clean-install path.
Related: package 3 (RESULTS)'s rules reference data_type: "results", but no results row is seeded into data_types — harmless (JSON isn't FK-checked), but results won't appear in the Packages page's picklist until someone adds it via Data Types.
A real credential is committed in a Development config file
Both GateKeeper.Api/appsettings.Development.json and GateKeeper.AdminUI/appsettings.Development.json contain a live-looking external MySQL host, username, and plaintext password — not a placeholder like every other config file in the repo. Action needed: rotate that password and move Development config to user-secrets or environment variables, consistent with the .env.example pattern used for other environments. Do not paste the actual value into chat, a commit message, or this KB — only the fact that it needs rotating.
HTTP API and AdminUI aren't fully symmetric
DataType CRUD, UpdateSubscriptionDatesAsync, and UpdateApiHitLimitAsync are only reachable through the Blazor AdminUI (direct in-process calls to GateKeeperAdminService) — no /admin HTTP route exists for any of them. Fine today since the AdminUI is the only consumer; worth knowing if a future integration needs to drive those operations over HTTP.
What's intentionally not built yet
- EF Core migrations — the
DbContextis mapped to matchgatekeeper-schema.sqlby hand; run that script directly instead ofdotnet ef database update. /v1/racecards,/v1/speedmaps— same shape as the meetings endpoint, not yet duplicated.Customer.ApiHitLimitMonthlyenforcement — the field and its AdminUI editor exist, but nothing currently enforces it against incoming request volume.- Unit tests —
EntitlementResolverandEntitlementScope.Matches/EffectiveEntitlement.IsAllowedare pure functions with no I/O, making them the highest-value place to start.