TPM Intelligence Engine — QA Report

Full-stack QA from scratch · April 6, 2026 · Conducted by Claude (QA Agent)

16
Tests Passed
12
Issues Found
3
Critical Issues
4
Warnings

1. Authentication (Supabase)

TestUserResultDetails
Email/Password loginadmin@tpm-test.comPassJWT role=admin ✓
Email/Password loginkam@tpm-test.comPassJWT role=kam ✓
Email/Password loginanalyst@tpm-test.comPassJWT role=analyst ✓
Email/Password loginexecutive@tpm-test.comPassJWT role=executive ✓
Login page loads (deployed)PassHTTP 200, form renders with email/password + magic link
Root (/) redirectPassClient-side redirect to /login (unauth) or /chat (auth)
JWT role in app_metadataAll 4 usersPassRoles correctly stored in JWT claims
Invalid credentialsinvalid tokenWarnBackend returns 500 instead of 401 for invalid tokens
MEDIUM   Login redirects all roles to /chat

After successful email/password login, login/page.tsx line 51 hardcodes window.location.href = '/chat'. This means executives land on /chat instead of their /executive dashboard, and admins land on /chat instead of /admin. Role-aware redirect should use ROLE_NAV_CONFIG[role].defaultRoute.

LOW   Register page is a stub

The /register page renders a placeholder with no actual registration form. Self-registration is non-functional.

2. Deployed Frontend (Cloudflare Pages)

RouteStatusNotes
/200Redirects via JS
/login200Renders correctly
/register200Stub page only
/callback404Magic link callback broken on deployed site
/chat200
/evaluate200
/plan200
/reports200
/anomalies200
/admin200
/analyst200
/executive200
/upload200
/dashboard404No standalone dashboard route (expected — navigated via sub-routes)
HIGH   /callback returns 404 on deployed site

The magic link auth callback at /callback returns 404 on tpm-intelligence-engine.pages.dev, despite existing in the local out/ build directory. The latest build has not been deployed, or Cloudflare Pages is serving a stale build. Magic link authentication is completely broken in production.

3. Backend API

3a. Infrastructure Status

EndpointStatusDetails
Cloud Run /health200tpm-core v0.1.0 healthy
Cloud Run /docs200FastAPI Swagger UI loads
Cloud Run /api/skills200Endpoint available
Cloudflare Worker /healthECONNREFUSEDWorker is down / not deployed
Cloudflare Worker /api/skillsECONNREFUSEDAll Worker endpoints unreachable
CRITICAL   Cloudflare Worker API is completely down

The frontend .env.local points to https://tpm-api.aiguildpartner.workers.dev (the Worker), which returns ECONNREFUSED on all endpoints. The Cloud Run backend at tpm-core-907519043541.asia-southeast1.run.app works fine. The frontend in local dev mode will fail to reach the API. The .env.production correctly points to Cloud Run.

3b. Authenticated API Endpoint Tests (via Cloud Run)

TestStatusResponse
KAM: POST /api/chat200Stub response — "Full AI capabilities coming soon"
Analyst: POST /api/upload200Accepted
KAM: POST /api/evaluate200Empty evaluations (initializing)
Admin: GET /api/admin/users200Endpoint available
Admin: GET /api/admin/feature-flags200Endpoint available
Exec: GET /api/executive/metrics200Endpoint available
KAM: GET /api/anomalies200Empty anomalies (initializing)
KAM: GET /api/reports200Endpoint available
KAM: POST /api/plan200Endpoint available

Note: Most endpoints return "Endpoint available" placeholders rather than real data or logic. The chat endpoint returns a hardcoded stub response.

4. Role-Based Access Control (RBAC)

4a. Backend RBAC (CRITICAL)

Test (should be DENIED)Actual StatusExpectedVerdict
Executive → /api/admin/users200403FAIL
Executive → /api/upload200403FAIL
KAM → /api/admin/users200403FAIL
KAM → /api/upload200403FAIL
Invalid token → /api/chat500401FAIL
CRITICAL   Backend has NO RBAC enforcement on 9 of 11 API modules

Only admin.py (2 routes) uses the require_role() dependency. The remaining 20+ routes in chat, evaluate, plan, report, anomalies, executive, corrections, consumers, and upload have zero role-based protection. Any authenticated user can access any endpoint regardless of their role.

Root cause: The JWTAuthMiddleware in app.py is a stub — in non-production mode it accepts any token. The require_role() dependency exists in rbac.py but is not wired into most route handlers. Additionally, corrections.py has docstrings claiming role enforcement that is NOT implemented.

HIGH   Backend RBAC role hierarchy mismatch with frontend

Frontend (rbac.ts): executive(1) < kam(2) < analyst(3) < admin(4)
Backend (rbac.py): viewer(1) < analyst(2) < kam(3) < admin(4)

The backend places KAM above analyst, while the frontend places analyst above KAM. It also uses "viewer" instead of "executive". When backend RBAC is enforced, analysts would be unable to access KAM-level endpoints, contradicting the frontend behavior where analysts have broader access.

4b. Frontend RBAC

PageProtection MechanismVerdict
/adminRoleGuard requiredRole="admin"Protected
/analystManual profile?.role checkPartial
/chatAuth only (useAuth)No role check
/evaluateAuth only (useAuth)No role check
/planAuth only (useAuth)No role check
/reportsAuth only (useAuth)No role check
/anomaliesAuth only (useAuth)No role check
/executiveAuth only (useAuth), "enforced by layout"Layout only
/uploadAuth only (useAuth)No role check

The DashboardShell layout does check ROLE_ALLOWED_PAGES and shows a 403 page if a user navigates outside their role. However, this is client-side only and can be bypassed by calling APIs directly. It also relies on pathname.startsWith() matching, which works because the static export produces flat routes.

5. Supabase Database

5a. Table Existence

TableStatusData
accountsExistsEmpty (0 rows)
productsExistsEmpty
promotionsExistsEmpty
time_seriesExistsEmpty
raw_filesExistsEmpty
baselinesExistsEmpty
format_registryExistsEmpty
vocabulary_mapExistsEmpty
correctionsExistsEmpty
audit_logExistsEmpty
dead_letter_queueExistsEmpty
tenant_configExistsEmpty
compute_resultsRLS Errorapp.current_tenant undefined
reportsRLS Errorapp.current_tenant undefined
anomaliesRLS Errorapp.current_tenant undefined
evaluation_resultsNot FoundTable does not exist
executive_metricsNot FoundHint: "Perhaps you meant monthly_metrics"
usersNot FoundNo users table (roles in JWT only)
mcp_consumersNot FoundHint: "Perhaps you meant api_consumers"
CRITICAL   RLS policies on 3 tables broken — app.current_tenant not set

The tables compute_results, reports, and anomalies have RLS policies that depend on current_setting('app.current_tenant'), but this PostgreSQL configuration parameter is never set by the application. Every query to these tables returns:

ERROR 42704: unrecognized configuration parameter "app.current_tenant"

Impact: The anomaly detection page, reports page, and evaluation results are completely non-functional from the database layer. No data can be read from or written to these tables.

MEDIUM   Table name mismatches between code and database

Several table names in the code don't match what exists in Supabase:

MEDIUM   All tables are empty — no seed data

Every table in the database has 0 rows. There is no seed data for testing or demonstration purposes. The frontend will render empty states for every view. You cannot QA functional workflows (evaluate, plan, report) without data.

6. Build & Test Infrastructure

CheckResultDetails
TypeScript compilationPasstsc --noEmit clean — zero errors
ESLintPassnext lint — zero warnings or errors
Static export (next build)WarnBuild exists in out/ but times out in sandbox (resource constrained)
Jest test suiteFailJest and test dependencies NOT installed in node_modules
Backend testsN/ACannot run (Python deps not installed in this environment)
MEDIUM   Jest and test dependencies not installed

package.json lists jest, @testing-library/react, @testing-library/jest-dom, and jest-environment-jsdom in devDependencies, but they are completely absent from node_modules/. Running npm test fails immediately. Either npm install was run with --production or these were never installed.

7. Additional Findings

HIGH   JWT auth middleware is a no-op in non-production

The JWTAuthMiddleware in app.py (line 82) does if env != "production": return await call_next(request) — meaning in development/staging, any request is accepted without authentication. Combined with the fact that TPM_ENV is typically not set (defaults to "development"), the backend is effectively completely open.

MEDIUM   RoleGuard component used inconsistently

A RoleGuard component exists in src/components/auth/RoleGuard.tsx but is only used by the admin page. The analyst page implements a manual role check. All other pages have no page-level role protection (relying solely on the DashboardShell layout check).

LOW   useRBAC hook defaults silently to 'kam'

If the JWT has no role in app_metadata, the useRBAC hook defaults to 'kam' with only a console.warn. This could grant unintended KAM-level access to malformed or migrated accounts.

LOW   Backend chat returns stub response, not AI

POST /api/chat returns: {"response":"TPM Engine is starting up. Full AI capabilities coming soon."}. The chat SSE streaming endpoint and Anthropic Claude integration exist in code but are not active — the primary chat endpoint returns a hardcoded response.

Issue Summary by Severity

Severity#Issues
CRITICAL 3 1. Backend RBAC not enforced on 9/11 API modules
2. Cloudflare Worker API down (ECONNREFUSED)
3. RLS policies broken on compute_results, reports, anomalies tables (app.current_tenant)
HIGH 3 4. /callback returns 404 (magic link auth broken in production)
5. Backend/frontend RBAC hierarchy mismatch (viewer vs executive, KAM vs analyst ordering)
6. JWT auth middleware is a complete no-op in non-production
MEDIUM 4 7. Login hardcodes redirect to /chat instead of role-appropriate route
8. Table name mismatches between code and database
9. All database tables empty — no seed data
10. Jest/test dependencies not installed
LOW 3 11. Register page is a stub placeholder
12. useRBAC defaults silently to 'kam' role
13. Chat endpoint returns stub instead of AI response

What Works Well

AreaStatus
Supabase authentication (email/password) for all 4 rolesSolid
JWT role assignment via app_metadataSolid
Frontend TypeScript — zero compilation errorsSolid
Frontend ESLint — zero warningsSolid
Cloud Run backend health, startup, and routingSolid
Frontend layout with role-based sidebar navigation (DashboardShell)Solid
Client-side 403 enforcement in DashboardShellSolid
Static export to Cloudflare Pages (all dashboard routes serve correctly)Solid
CORS configuration on Cloud RunSolid
Supabase client singleton pattern (prevents Web Locks contention)Solid
Frontend type system (auth.ts canonical, rbac.ts re-exports)Solid
Backend code structure (20 skills, engine/data separation)Solid

Recommended Next Steps (Priority Order)

#ActionSeverityEffort
1Fix app.current_tenant RLS — either set it via SET LOCAL before queries or rewrite policies to use auth.uid()CRITICAL~2h
2Wire require_role() into all API route handlers (chat, evaluate, plan, report, anomalies, executive, upload, corrections, consumers)CRITICAL~3h
3Fix or remove the Cloudflare Worker; update .env.local to point to Cloud Run for local devCRITICAL~30m
4Align backend RBAC hierarchy with frontend: executive(1) < kam(2) < analyst(3) < admin(4)HIGH~1h
5Redeploy frontend to Cloudflare Pages (fixes /callback 404)HIGH~15m
6Make login redirect role-aware using ROLE_NAV_CONFIGMEDIUM~30m
7Create seed data script for promotions, accounts, productsMEDIUM~2h
8Fix table name references (evaluation_results → compute_results, etc.)MEDIUM~1h
9Install test dependencies and verify test suite runsMEDIUM~30m
10Apply RoleGuard consistently across all protected pagesMEDIUM~1h