Instruction file imported from davideagosti-dev/CodeFoundex (
.cursor/rules/core.mdc). Copyright stays with the author.
CodeFoundex — CORE RULES
Last Updated: February 2026
#####################################################################
PRIME DIRECTIVE
#####################################################################
- Keep the system GREEN at all times.
- Implement in small, reversible steps.
- Never break: multi-tenant, roles/RBAC, ROI approval loop, repo lifecycle, FE auth wall.
- If something is already implemented: DO NOT re-implement; only harden + certify.
- Think before you code. Prefer correctness over speed.
- Treat this file as the single source of truth for how to work in this repo.
- Before editing, scan the relevant files (open them) and confirm contracts (routes, DTOs, events, models).
- Always work in small, reviewable commits:
- add/adjust tests (or a runnable manual checklist)
- implement
- run quality gate commands
- summarize deltas + risks
Change management
- If changing API response shape: update DTOs + FE types + tests together.
- If changing auth/tenant semantics: add a regression test that proves:
- missing tenant header returns 4xx (not 500)
- wrong-tenant access returns 403
- correct tenant works
#####################################################################
IMMUTABLE INVARIANTS (DO NOT BREAK)
#####################################################################
Multi-tenant
- Any tenant-scoped endpoint MUST enforce tenant context using
X-Tenant-Id. - Backend MUST validate tenant membership for
X-Tenant-Id(FE is not security). - If tenant is missing where required → return 400/401/403 (never silent defaults).
- EVERY database query on tenant-scoped data MUST filter by tenant_id.
- NEVER query across tenant boundaries. Data leaks = P0 security incidents.
- FE console MUST attach BOTH
Authorization: Bearer <JWT>andX-Tenant-Id. - Marketing routes MUST NOT attach tenant/token headers.
RBAC
- Role hierarchy: viewer < developer < admin < owner
- platform_admin is a SEPARATE flag on User model (not a tenant role)
- EVERY protected route MUST use
require_role()from auth/dependencies.py - Pattern:
_: Any = Depends(require_role("developer")) - Destructive ops (delete) → require_role("admin") minimum
- Tenant delete → require_role("owner") only
- Auth routes (/login, /sso/start, /sso/callback) are PUBLIC — no RBAC
- WebSocket: validate JWT during handshake, check role before streaming
RBAC coverage gaps (MUST FIX before go-live)
- task_routes.py → developer for CRUD, admin for delete
- snapshot_routes.py → developer for create/restore, admin for delete
- tenant_routes.py → admin for update, owner for delete
- metrics_routes.py → viewer for read endpoints
- websocket_routes.py → JWT validation in WS handshake + role check
- l5_routes.py → audit and add missing checks
- l5_preview_routes.py → developer minimum
Event sourcing
- ALL state changes MUST produce a TaskEvent record (implemented as ThreadEvent in code for thread-scoped events)
- TaskEvents/ThreadEvents are IMMUTABLE — NEVER update, NEVER delete
- ThreadEvent for thread-scoped events, AuditEvent for security/compliance
- Avoid committing inside helper functions unexpectedly
ROI governance
- "Pause on ROI block" must be real and observable
- LLM run can BLOCK → UI shows plan + Approve/Reject
- Approve must RESUME and continue to completion
- Reject must TERMINATE cleanly and reflect state in UI
- Any new feature that can create cost must be behind ROI evaluation gates.
Repo lifecycle
- Repo create/ensure must remain idempotent PER TENANT using
Idempotency-Key - First create returns 201; replay returns 200 with existing repo
- Repo delete must update UI state immediately
Policy engine
- policy.yaml governs "Ensure" and "Delete" operations
- NEVER bypass policy checks for destructive operations
- Always emit audit event when policy blocks an action
#####################################################################
NON-NEGOTIABLE QUALITY GATES
#####################################################################
Backend
pytest -q→ MUST passpytest -q tests/test_repo_idempotency.pypytest -q tests/test_admin_tenants.pypytest -q tests/test_auth.py tests/test_sso_routes.pypytest -q tests/test_tenant_isolation.py- No debug prints/log files committed.
- No new 500s for known/valid user errors.
Frontend
npm run lint→ MUST pass (0 errors)npm run build→ MUST pass- Routes must remain stable.
Definition of Done
A change is DONE only when ALL apply:
pytest -qis greennpm run lint+npm run buildare green- No new warnings/errors introduced
- User-visible behavior matches acceptance criteria
- No secrets committed; no debug prints left behind
#####################################################################
CRITICAL SECURITY WARNINGS
#####################################################################
- JWT_SECRET_KEY defaults to 'dev-secret-key' → add startup check that REJECTS this in production
- NEVER log tokens, passwords, API keys, or secrets
- NEVER return hashed_password in any API response
- NEVER expose internal error stack traces to client
- IMPORTANT: Do NOT paste secrets/tokens into chat. Use env vars only.
#####################################################################
KNOWN BUGS (fix before go-live)
#####################################################################
- backend/app.py: runtime_routes and grokking_routes registered TWICE — remove duplicates
- runtime_routes.py is 67KB — needs refactoring when touching it
#####################################################################
ERROR HANDLING + HTTP SEMANTICS
#####################################################################
- 400/422 validation errors
- 401 auth missing/invalid
- 403 tenant access denied / insufficient role
- 404 resource missing
- 409 conflict
- 200 OK for idempotent replay
- 201 Created for new resources
- 204 No Content for successful deletes
- 501 Not Implemented (SSO stub — remove when implementing)
- 503 SSO misconfigured
- Do not return 500 for predictable user mistakes.
- RBAC denial → 403 with detail "Requires {role} role or higher"
- Emit audit event for every RBAC denial (type: 'rbac.denied')
#####################################################################
STANDARD OUTPUT FORMAT (REQUIRED for every change)
#####################################################################
- Scope summary (what and why)
- Files changed (file-by-file with brief description)
- Behavioral changes (what user sees differently)
- Tests run + results
- Follow-ups / risks / known limitations
#####################################################################
PATTERN TRASVERSALI
#####################################################################
Pattern: derivare repo_id
SEMPRE: repo_id_from_workspace_path(workspace_root) [backend/utils/workspace_utils.py]
MAI: getattr(thread, "repo_id", None) [Thread non ha repo_id]
MAI: plan.get("repo_id") [quasi mai presente nel plan]
Pattern: ROI event payload
Struttura in DB/WebSocket: { "roi": roi_result, "mode": ..., "inputs_summary": ... }
Per leggere score/decision/breakdown: sempre estrarre payload["roi"] prima
Helper backend: _normalize_roi_for_frontend(payload)
Helper frontend: normalizeRoiPayload(payload)
Pattern: metrics aggregation
repo_id opzionale → aggrega tenant-wide se omesso
tenant_id sempre obbligatorio → isolamento multi-tenant
Frontend: chiamare sempre l'API, passare repo_id solo se disponibile
Pattern: valori monetari GBP
SEMPRE: formatGbp(value) [frontend/src/lib/formatGbp.ts]
MAI: value.toFixed(2) [tronca valori < £0.01 a £0.00]
#####################################################################
BUG SCAN SHORTCUTS
##################################################################### When asked "rbac scan": check ALL route files for missing require_role() When asked "tenant scan": check ALL queries for missing tenant_id filter When asked "event scan": verify ALL state changes emit TaskEvent/ThreadEvent When asked "beta scan": run full Beta Certification procedure When asked "security scan": check JWT secret, password exposure, stack traces, secret logging When asked "quick health": run pytest -q + npm run lint + npm run build and report results