REVIEW.md — RaR-IT SaaS Kernel & Verticals
This file guides Claude Code Review. Unlike CLAUDE.md (general project
context, violations surface only as nits), everything here is read
directly by every finding and verification agent — treat these as the
project's actual merge standards, sourced from the SaaS Architecture
Policy and Annex A.
Reference: /docs/policies/saas-architecture/policy and /annex-a on
the governance site. Cite the section number when flagging a violation
so the author can look it up.
Check against the PR's story and acceptance scenarios
The PR description's "Story" and "Acceptance Scenarios (Given-When-Then)"
sections (see .github/PULL_REQUEST_TEMPLATE.md) are the source of truth
for what this change is supposed to do — read them before reviewing the
diff, not just after.
- If a listed acceptance scenario has no code path in the diff that appears to satisfy it, flag this as a Missing scenario coverage finding (Important) — name the specific scenario, not a generic "add more tests" comment.
- If the diff implements behavior that contradicts a listed scenario (e.g. a Given-When-Then implies a validation error should be returned, but the code silently accepts the case), flag as Important and quote the scenario it conflicts with.
- If the Story/Acceptance Scenarios section is empty or missing entirely, report this once, as a nit, rather than skipping the intent-check — don't guess at intent from the diff alone.
- Do not flag scenarios that are explicitly out of scope per the PR's "Notes for the reviewer" section (e.g. deliberately deferred to a follow-up PR) — check there before flagging a scenario as missing.
Escalate to Important
-
Rejected DB-access patterns (Policy §6, A.10). Any
fastify.decorate('db', ...), or a raw DB client / transaction object passed as an explicit function parameter, instead ofgetDb()/withTenantScope()from@rarit-kernel/platform-core. This is a formally rejected pattern, not a style preference. -
Direct vertical-to-vertical imports (Policy §4). Any import from one vertical into another (e.g.
verticals/tourismimporting fromverticals/amaar), in either direction — even with no cycle.import/no-cyclein CI cannot catch this one-way case; only review can. Correct fix: promote the shared logic to a@rarit-kernel/*package (see Annex A.8). -
New engine-to-engine import that isn't already one-directional (Policy §4, A.7). If a change adds an import from Engine A into Engine B, check whether B already imports A anywhere in the workspace. If so, this is a reintroduction of the exact IAM↔Notifications shape A.7 exists to prevent — flag it even if CI's
no-cyclejob hasn't run yet or the cycle is indirect (routed through a third package). -
RLS enabled without FORCE (Annex A.11.3). Any new
ENABLE ROW LEVEL SECURITYon a tenant-scoped table not paired withFORCE ROW LEVEL SECURITY.ENABLEalone does not protect against a table owner or superuser role bypassing the policy — this is the exact anti-pattern documented in A.11.3 from the old system. -
Cross-tenant reads outside
withPlatformAdminScope()(A.11.2 / ACT-001). Any query that appears to read across tenants (missing an implicit tenant filter, a rawSELECTwith noWHEREon a multi-tenant table) that isn't going through the approvedwithPlatformAdminScope(actor, reason, work)function with a loggedreason. -
Route handlers doing DB access directly (
engine_structure.md). A DB query insideroutes/, or aservices/function that acceptsrequest/reply. Routes → services → repositories is the mandatory three-layer split; this is a layering violation, not a style nit. -
Payment, Audit, or IAM changes with no accompanying audit write (A.1, A.10). Any mutation to Payment or IAM state (subscriptions, role grants, transactions) that doesn't write an Audit record in the same transaction as the triggering change.
Report as-is (default severity)
- New API routes without an integration test.
- Manifest or event-definition changes placed under
v1/instead ofcore/— a version-axis mixup perCLAUDE.md's file-structure notes, not a REST contract change. - New cross-engine references implemented as enforced DB-level foreign
keys rather than logical FKs (Entitlements → IAM's
tenants.idshould stay a logical reference, per the ownership note in project memory).
Nit only (CLAUDE.md-level, don't escalate)
- General
CLAUDE.mdfootguns already covered there (brace-expansion mkdir bug,declare module "fastify"shadow-module trap, etc.) — these already surface as nits automatically; no need to duplicate here. - Naming/formatting inconsistencies with no functional effect.
Skip
*.md,docs/**,signed-records/**— documentation and controlled copies, not code.- Generated files:
openapi.json, anything underdist/orbuild/.
Nit volume
Report at most five nits per review; mention any remainder as a count in the summary rather than listing them all.
Notes for reviewers
Findings here are informational — Code Review does not approve or block PRs. This is intentional and matches the team's current trust-based merge process (see Actions ACT-005). If that changes later (e.g. GitHub plan upgraded to enable required status checks), this file doesn't need to change — only the branch-protection configuration does.