Review: CRM tab (admin): business pipeline with search and promotion (re-review)

review-1826-2026-07-18-v2 Review

review approved

Verdict: APPROVED

Re-review after body refinements. All five previously flagged [BODY] issues are resolved. Ticket scope is solid and implementable.

Template Completeness

  • [x] Type — Feature
  • [x] Lineage — replaces #14
  • [x] Repo — ldraney/intelligentstaffingsystems
  • [x] User Story — As the admin, I want a searchable pipeline with filtering and promotion
  • [x] Context — modeled after landscaping-assistant "today tab"
  • [x] Dependencies — #54 soft dependency documented with graceful handling
  • [x] File Targets — 8 files listed (including Gemfile for PaperTrail) plus exclusions
  • [x] Feature Flag — none (core admin functionality)
  • [x] Acceptance Criteria — 13 items
  • [x] Test Expectations — 10 items plus run command
  • [x] Constraints — 8 constraints listed including realm-role-mapping specifics
  • [x] Checklist — standard 3-item
  • [x] Related — references project and related issues

Traceability

  • [x] story:crm label — documented in repo docs/user-stories.md as Epic 7: CRM (Admin), covering US-7.1, US-7.2, US-7.3. Sprint roadmap maps #56 to Epic 7.
  • [ ] story note on project page — [SCOPE] project-iss user-stories table uses key "admin" (Epic 6) but repo distinguishes Epic 6 (Account) from Epic 7 (CRM). Add "crm" row to project-iss user-stories table mapping to Epic 7.
  • [ ] arch note MISSING — [SCOPE] No arch-rails note exists in pal-e-docs. Systemic gap (many board items use arch:rails). Create architecture note arch-rails.
  • [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/intelligentstaffingsystems/issues/56, state: open

File Targets

  • [x] app/controllers/crm_controller.rb — verified: EXISTS as stub with require_role :admin. Matches top-level tab controller convention (catalog, communications, messages).
  • [x] app/views/crm/index.html.erb — verified: EXISTS as stub view (empty-state placeholder).
  • [x] app/views/crm/_business_card.html.erb — to be created; correct path under existing crm/ view directory.
  • [x] app/views/crm/show.html.erb — to be created; correct path.
  • [x] app/assets/stylesheets/crm.css — to be created; matches existing convention (catalog.css, communications.css, dashboard.css, etc.).
  • [x] app/services/keycloak_admin_service.rb — verified: EXISTS with get_user and update_user. Ticket specifies adding promote_to_client using realm-role-mapping API (POST /admin/realms/{realm}/users/{id}/role-mappings/realm). Needs POST method added to make_request.
  • [x] config/routes.rb — verified: EXISTS with stub route at line 37 (get "/crm"). Needs show and promote action routes added.
  • [x] Gemfile — verified: EXISTS. PaperTrail NOT present (correct — ticket says to add it).

Repo Placement

OK — issue filed on ldraney/intelligentstaffingsystems, all work targets that repo. Keycloak admin API calls go outbound to the Keycloak server, no cross-repo changes needed.

Dependencies

  • #6 (Keycloak OIDC auth) — DONE. Provides KeycloakAdminService foundation and session role extraction.
  • #51 (Projects tab) — DONE. ProjectRequest model exists for "project requests visible on business detail" AC.
  • Messaging subsystem — DONE. Message model and threads exist for "message thread link" AC.
  • #54 (Appointment scheduling) — NOT landed. Ticket explicitly handles this: "If #54 has not landed, show last activity from messages and project requests only; appointment data is additive, not blocking." AC #5 uses conditional "(+ last appointment if #54 has landed)". Graceful.
  • Lead.search scope — does not exist yet. Will be created as part of this ticket.
  • promoted_at column — EXISTS in schema (line 80). Role column EXISTS (line 82, default "lead").
  • PaperTrail gem — NOT installed. Explicitly listed as Gemfile file target to add.

Acceptance Criteria

13 acceptance criteria. All are testable by an agent:
  • AC #1-4 (access control, search, filter, card display) — standard controller/view testing with session stubbing per testing-strategy.md.
  • AC #5 (last activity) — aggregates across messages and project_requests (both exist). Appointment is conditional. Clear and implementable.
  • AC #6 (business detail) — show view with related data. Straightforward.
  • AC #7-8 (promote button, confirmation) — UI elements, testable via view rendering.
  • AC #9 (Keycloak role update) — NOW PRECISELY SPECIFIED: realm-role-mapping API, POST endpoint given. KeycloakAdminService can be extended cleanly.
  • AC #10-11 (mirror to Lead.role, set promoted_at) — model update; columns already exist in schema.
  • AC #12 (PaperTrail audit) — gem addition now in file targets. Standard Rails gem integration.
  • AC #13 (project requests in detail) — ProjectRequest model exists, belongs_to :lead.

Blast Radius

  • Tab bar helper (app/helpers/navigation_helper.rb) already references CRM path for admin — no route rename needed (stub route stays at /crm).
  • KeycloakAdminService: adding promote_to_client method with POST support extends the service without modifying existing get_user/update_user methods. Safe.
  • PaperTrail installation adds a versions table and initializer — standard Rails concern, isolated to new model integration.
  • No search scope exists anywhere in the app — Lead.search will be the first. No conflicting patterns.

Decomposition Assessment

Per project convention ("Stop decomposing; write inclusive tickets, review them whole"), this 8-point inclusive ticket is accepted as-is. The 13 ACs form a cohesive feature (CRM pipeline) rather than unrelated concerns. An agent can implement search/filter, detail view, and promotion as a single coherent pass since they share the same controller, model scope, and view directory.

Previous Review Issues — Resolution Status

  • File target namespace mismatch — FIXED. All paths now use top-level convention matching existing stub.
  • PaperTrail not in Gemfile — FIXED. Explicitly listed in File Targets.
  • Appointments model dependency — FIXED. Dependencies section added with graceful handling. ACs use conditional language.
  • Keycloak promotion API — FIXED. Constraints and file targets now specify realm-role-mapping endpoint precisely.
  • Decomposition suggested — NOT decomposing per project convention. Accepted.

Recommendation

  • [SCOPE] Add "crm" row to project-iss user-stories table: Key=crm, Backing=Epic 7 (US-7.1–7.3), Role=Admin, Success metric="Searchable pipeline; lead→client promotion with audit"
  • [SCOPE] Create architecture note arch-rails (systemic gap — affects many board items, not just this ticket)
Neither [SCOPE] item blocks implementation. The ticket body is complete and actionable.