--- description: Structured code review workflow for Verkada Backend PRs. Builds context from PR diff, Linear ticket, and vcerberus docs, then runs 8 parallel review tracks with sub-agents. category: verkada --- flowchart TD _HEADER_["
Verkada PR Review
Structured code review workflow for Verkada Backend PRs. Builds context from PR diff, Linear ticket, and vcerberus docs, then runs 8 parallel review tracks with sub-agents.
"]:::headerStyle classDef headerStyle fill:none,stroke:none subgraph _MAIN_[" "] subgraph Context["Phase 1: Build Context"] START[Start Review] --> DIFF[Read PR Diff
gh pr diff] START --> COMMENTS[Read PR Comments
and Description] START --> LINEAR[Read Linear Ticket] START --> DOCS[Read vcerberus/docs
Related to PR Topic] DIFF --> CONTEXT_DONE[Context Built] COMMENTS --> CONTEXT_DONE LINEAR --> CONTEXT_DONE DOCS --> CONTEXT_DONE end subgraph Review["Phase 2: Parallel Review Tracks"] CONTEXT_DONE --> TESTING[Testing
Coverage + Style] CONTEXT_DONE --> INTEG[Integration Tests
Style Guide Compliance] CONTEXT_DONE --> MIGRATIONS[Database
Migrations] CONTEXT_DONE --> CLEAN[Cleanliness
Imports, Logs, Comments] CONTEXT_DONE --> FORGOT[Nothing Forgotten
TODOs, Docstrings, Etags] CONTEXT_DONE --> FORMAT[Formatting
Parameterized Logging] CONTEXT_DONE --> SA20[SQLAlchemy 2.0
Patterns] CONTEXT_DONE --> LOGIC[Logic Errors
Edge Cases, None Checks] end subgraph TestDetail["Testing Details"] TESTING --> TEST_COV{Test Coverage
Exists?} TEST_COV -->|Yes| TEST_TIGHT[Tests Tight
and Compact?] TEST_COV -->|No| TEST_MISSING[Flag: Missing
Test Coverage] TEST_TIGHT --> TEST_FILES{Unnecessary
New Test Files?} TEST_FILES -->|Yes| TEST_CONSOLIDATE[Flag: Consolidate
Into Existing File] TEST_FILES -->|No| TEST_OK[Tests OK] end subgraph IntegDetail["Integration Test Details"] INTEG --> INTEG_NEEDED{Integration
Tests Present?} INTEG_NEEDED -->|No| INTEG_SKIP[Skip] INTEG_NEEDED -->|Yes| INTEG_SETUP[Check setUp/tearDown
Calls super] INTEG_SETUP --> INTEG_ENTITIES[Universal Entities
in setUp Only] INTEG_ENTITIES --> INTEG_ENDPOINTS[Uses _vcerberus_server
Helper Methods with Docstrings] INTEG_ENDPOINTS --> INTEG_PERMS[Permission Tests
as_user + retry_session] INTEG_PERMS --> INTEG_MIXINS[Correct MixIns
and Inheritance] INTEG_MIXINS --> INTEG_OK[Integration OK] end subgraph DBDetail["Database Details"] MIGRATIONS --> MIG_PRESENT{Migration
Files?} MIG_PRESENT -->|No| MIG_SKIP[Skip] MIG_PRESENT -->|Yes| MIG_DOWN[Downgrade Reverts
Everything Including Indexes] MIG_DOWN --> MIG_DETACH[No Detached Objects
After Commits/Deletes] MIG_DETACH --> MIG_OK[Migrations OK] end subgraph SA2Detail["SA2.0 Details"] SA20 --> SA2_NEW{New Code
Only?} SA2_NEW -->|Existing| SA2_SKIP[Skip] SA2_NEW -->|New| SA2_SELECT[select + session.execute
Not session.query] SA2_SELECT --> SA2_MODEL[mapped_column + Mapped
Not bare Column] SA2_MODEL --> SA2_REL[back_populates
Not backref] SA2_REL --> SA2_LOADER[Class-bound Loader Options
Not Strings] SA2_LOADER --> SA2_OK[SA2.0 OK] end subgraph LogicDetail["Logic Details"] LOGIC --> LOGIC_ARRAY[Array Edge Cases] LOGIC_ARRAY --> LOGIC_NONE[is not None
vs Boolean Check] LOGIC_NONE --> LOGIC_SILENT[No Silent Failures
Empty Loops] LOGIC_SILENT --> LOGIC_SCHED{Schedule
Handling?} LOGIC_SCHED -->|No| LOGIC_OK[Logic OK] LOGIC_SCHED -->|Yes| LOGIC_SCHED_TYPES[All Schedule Types
door, user, exception, visit] LOGIC_SCHED_TYPES --> LOGIC_INHERIT[Access Level
Inheritance Respected] LOGIC_INHERIT --> LOGIC_OK end subgraph Outcome["Phase 3: Results"] TEST_OK --> COLLECT[Collect All
Review Findings] TEST_MISSING --> COLLECT TEST_CONSOLIDATE --> COLLECT INTEG_OK --> COLLECT INTEG_SKIP --> COLLECT MIG_OK --> COLLECT MIG_SKIP --> COLLECT CLEAN --> COLLECT FORGOT --> COLLECT FORMAT --> COLLECT SA2_OK --> COLLECT SA2_SKIP --> COLLECT LOGIC_OK --> COLLECT COLLECT --> SUMMARY[Review Summary
with Action Items] end click START "#" "**Start Review**\nBegin structured PR review.\nAll 4 context steps run in parallel." click DIFF "#" "**Read PR Diff**\n`gh pr diff --name-only` for file list\n`gh pr diff` for full changes\nIdentifies scope and affected areas." click COMMENTS "#" "**Read PR Comments**\nRead all inline comments, review threads,\nand the PR description.\nCaptures reviewer feedback and author intent." click LINEAR "#" "**Read Linear Ticket**\nUse `linear-cli` to fetch the associated ticket.\nUnderstand requirements, acceptance criteria,\nand design decisions." click DOCS "#" "**Read vcerberus/docs**\nFind and read docs related to the PR topic.\nExamples: permissions doc for RBAC changes,\nintegration-test-style-guide for test PRs,\napple-wallet.md for wallet changes." click CONTEXT_DONE "#" "**Context Built**\nAll 4 parallel context streams merged.\nReviewer now has full understanding of:\n- What changed (diff)\n- Why it changed (ticket + description)\n- How it should work (docs)\n- What others said (comments)" click TESTING "#" "**Testing Track**\nVerify test coverage exists for edited code.\nEnsure tests are tight, compact, not repetitive.\nCheck for unnecessary new test files --\nprefer adding to existing thematically-matching files." click INTEG "#" "**Integration Test Track**\nIf integration tests are present, verify they\nfollow vcerberus/docs/integration-test-style-guide.md" click MIGRATIONS "#" "**Database Migrations Track**\nCheck alembic migration files if present.\nVerify downgrade path and object lifecycle." click CLEAN "#" "**Cleanliness Track**\n- No temporary markdown notes in PR\n- Move inline imports to top of file\n- Comments only where code is not self-explanatory\n- Use logging, not print\n- logger.debug for generic debug (not logger.info)\n- Use shared common utils, no reinvention" click FORGOT "#" "**Nothing Forgotten Track**\n- No unresolved TODO: items\n- Docstrings updated for changed endpoints/functions\n- Fix spelling mistakes\n- Etag headers on etag'd endpoints\n- PR description and title up to date\n- Title starts with [ticket-id]" click FORMAT "#" "**Formatting Track**\nEnsure logger statements use parameterized logging:\n`logger.info('msg %s', var)` not `logger.info(f'msg {var}')`\nThis enables proper log aggregation in Datadog." click SA20 "#" "**SQLAlchemy 2.0 Track**\nNew code only -- do not flag existing patterns.\nReference: `.claude/rules/vcerberus/sqlalchemy.md`" click LOGIC "#" "**Logic Errors Track**\nCheck for common logic bugs:\n- Array edge cases (empty, single, boundary)\n- `is not None` vs plain boolean check\n- Silent failures (empty for-loops, swallowed exceptions)" click TEST_COV "#" "**Test Coverage Check**\nIs the code being changed covered by tests?\nBoth unit and integration tests count." click TEST_TIGHT "#" "**Tests Tight and Compact?**\nNo tests performing basically the same thing\nover and over. Each test should cover a distinct case." click TEST_MISSING "#" "**Missing Test Coverage**\nFlag that tests need to be added\nfor the changed code paths." click TEST_FILES "#" "**Unnecessary New Test Files?**\nIf a thematically-matching test file already exists,\nnew tests should be added there instead of\ncreating a new file." click TEST_CONSOLIDATE "#" "**Consolidate Tests**\nFlag: tests should be moved into\nan existing test file." click TEST_OK "#" "**Tests Pass Review**\nCoverage exists, tests are compact,\nno unnecessary files." click INTEG_NEEDED "#" "**Integration Tests Present?**\nCheck if the PR includes integration test changes.\nIf not, skip this track." click INTEG_SKIP "#" "**Skip Integration Review**\nNo integration tests in this PR." click INTEG_SETUP "#" "**setUp/tearDown Pattern**\nMust call `super().setUp()` and `super().tearDown()`.\nNo lazy loading patterns." click INTEG_ENTITIES "#" "**Entity Creation**\nUniversal entities (Controller, User, site) in setUp.\nTest-specific entities only in individual tests.\nFollow reserved terminology: `self.user`, `self.admin`,\n`self.organization_id`, etc." click INTEG_ENDPOINTS "#" "**Endpoint Access**\nUse `self._vcerberus_server` for URLs.\nNot `self.config.get('web_host')`.\nEndpoint calls via helper methods with docstrings,\nnot inline http calls." click INTEG_PERMS "#" "**Permission Testing**\nCreate users with `User()` + `created_logged_in_session(use_prev=True)`.\nUnified permission tests (not separate positive/negative).\nUse helpers with `as_user` parameter.\nAll sessions via `retry_session()`." click INTEG_MIXINS "#" "**Correct MixIns**\nInherit from `IntegrationTestBase, VCerberusIntegrationTestBase`.\nUse relevant MixIns: CreateUserGroupMixin,\nTestSchedulesMixin, etc." click INTEG_OK "#" "**Integration Tests Pass Review**\nAll patterns follow the style guide." click MIG_PRESENT "#" "**Migration Files Present?**\nCheck for alembic migration files in the PR diff." click MIG_SKIP "#" "**Skip Migration Review**\nNo migration files in this PR." click MIG_DOWN "#" "**Downgrade Path**\nThe downgrade function must revert everything\nthe upgrade creates, including indexes." click MIG_DETACH "#" "**No Detached Objects**\nEnsure no database objects become detached\nas a result of commits or deletes in the migration\nor related code." click MIG_OK "#" "**Migrations Pass Review**\nDowngrade is complete, no detachment risks." click SA2_NEW "#" "**New Code Only**\nSA2.0 patterns only apply to newly written code.\nDo not flag existing legacy patterns." click SA2_SKIP "#" "**Skip SA2.0 Review**\nNo new ORM code in this PR." click SA2_SELECT "#" "**select() Pattern**\nNew queries: `select()` + `session.execute()`\nNot `session.query()`.\nBulk ops: `update()`/`delete()` constructs.\nPK lookups: `session.get(Model, pk)`." click SA2_MODEL "#" "**Model Declarations**\n`mapped_column()` with `Mapped[]` type annotations.\nNot bare `Column()`.\nUse `DeclarativeBase`, not `declarative_base()`." click SA2_REL "#" "**Relationships**\nExplicit `back_populates=` on both sides.\nNot `backref=`." click SA2_LOADER "#" "**Loader Options**\nClass-bound attributes:\n`selectinload(User.addresses)`\nNot string-based: `selectinload('addresses')`.\nRaw SQL wrapped in `text()`." click SA2_OK "#" "**SA2.0 Patterns Pass Review**\nAll new code follows SQLAlchemy 2.0 style." click LOGIC_ARRAY "#" "**Array Edge Cases**\nCheck empty arrays, single-element arrays,\nand boundary conditions in loops and indexing." click LOGIC_NONE "#" "**None Checks**\nUse `is not None` / `is None` for None comparisons.\nNot plain boolean checks that conflate None with\nFalse, 0, or empty string." click LOGIC_SILENT "#" "**Silent Failures**\nNo empty for-loops that silently do nothing.\nNo swallowed exceptions without logging." click LOGIC_SCHED "#" "**Schedule Handling?**\nDoes this PR touch schedule-related code?" click LOGIC_SCHED_TYPES "#" "**All Schedule Types**\nEnsure all types are handled:\ndoor, user, exception, visit, etc.\nMissing a type is a common source of bugs." click LOGIC_INHERIT "#" "**Access Level Inheritance**\nSchedule inheritance via parent relationships\nmust be respected. Check access level\nschedule parent chains." click LOGIC_OK "#" "**Logic Pass Review**\nNo edge case issues found." click COLLECT "#" "**Collect Findings**\nAll 8 parallel review tracks report back.\nFindings categorized as: must-fix, should-fix, nit." click SUMMARY "#" "**Review Summary**\nStructured summary with:\n- Blocking issues (must-fix before merge)\n- Suggestions (should-fix, not blocking)\n- Nits (style, optional)\nPosted as PR review or reported to user." classDef context fill:#d1ecf1,stroke:#7ec8d8 classDef track fill:#e8daef,stroke:#b07cc6 classDef detail fill:#ffeaa7,stroke:#e0c040 classDef decision fill:#fff3cd,stroke:#f0c040 classDef success fill:#d4edda,stroke:#5cb85c classDef failure fill:#f8d7da,stroke:#e06070 classDef skip fill:#e2e8f0,stroke:#94a3b8 class START,DIFF,COMMENTS,LINEAR,DOCS,CONTEXT_DONE context class TESTING,INTEG,MIGRATIONS,CLEAN,FORGOT,FORMAT,SA20,LOGIC track class TEST_TIGHT,TEST_FILES,INTEG_SETUP,INTEG_ENTITIES,INTEG_ENDPOINTS,INTEG_PERMS,INTEG_MIXINS,MIG_DOWN,MIG_DETACH,SA2_SELECT,SA2_MODEL,SA2_REL,SA2_LOADER,LOGIC_ARRAY,LOGIC_NONE,LOGIC_SILENT,LOGIC_SCHED_TYPES,LOGIC_INHERIT detail class TEST_COV,INTEG_NEEDED,MIG_PRESENT,SA2_NEW,LOGIC_SCHED decision class TEST_OK,INTEG_OK,MIG_OK,SA2_OK,LOGIC_OK,COLLECT,SUMMARY success class TEST_MISSING,TEST_CONSOLIDATE failure class INTEG_SKIP,MIG_SKIP,SA2_SKIP skip end style _MAIN_ fill:none,stroke:none,padding:0 _HEADER_ ~~~ _MAIN_