Review: In-app registration: post-Keycloak flow, phone validation, LeadsController reconciliation (R3)

review-1831-2026-07-17-r3 Review

review approved

Verdict: APPROVED

R3 re-review of board item #1831. Both remaining issues from R2 (review-1831-2026-07-17-r2) are resolved. Ticket is ready for development.

Previous Issues -- Resolved

  • [x] [SCOPE] Architecture note arch-auth-iss now exists in pal-e-docs (id: 2569, note_type: architecture, project: iss). Covers Keycloak OIDC flow, OmniAuth, SessionsController, roles, and key decisions. Resolves the traceability gap carried from R1 through R2.
  • [x] [BODY] Blast radius filename corrected: issue body now reads app/helpers/session_helper.rb (singular). Verified: file exists at that path with module SessionHelper. The plural sessions_helper.rb does not exist.

Template Completeness

  • [x] Type (Feature)
  • [x] Lineage (Related to pal-e-services#184)
  • [x] Repo (ldraney/intelligentstaffingsystems)
  • [x] User Story (As a prospective ISS client...)
  • [x] Context (post-Keycloak self-registration wiring)
  • [x] File Targets (6 files)
  • [x] Feature Flag (none -- no docs/feature-flags.md in project, acceptable)
  • [x] Acceptance Criteria (6 items)
  • [x] Test Expectations (2 unit + 1 integration + run command)
  • [x] Constraints (4 items including schema facts and design tokens)
  • [x] Blast Radius (2 files identified)
  • [x] Blockers (pal-e-services#184 prerequisite)
  • [x] Checklist
  • [x] Related

Traceability

  • [x] story:registration label -- Epic 1 (US-1.2--1.4), Visitor/Lead
  • [x] story note verified -- found in project-iss user-stories section (key: registration, backing: Epic 1)
  • [x] arch:auth label -- authentication component
  • [x] arch note verified -- arch-auth-iss exists in pal-e-docs (architecture note, project: iss, created 2026-07-17). Covers Keycloak OIDC sequence diagram, component table, key decisions, and roles matrix.
  • [x] Forgejo issue -- ldraney/intelligentstaffingsystems#59, open

File Targets

  • [x] app/controllers/sessions_controller.rb -- verified: OmniAuth callback with 3-path lead resolution. Walk-in path (line 101) creates Lead with phone: "Not provided". Correctly scoped for phone extraction from Keycloak token.
  • [x] app/controllers/leads_controller.rb -- verified: public registration form with skip_before_action :authenticate_user!. Permits phone in lead_params.
  • [x] app/views/leads/new.html.erb -- verified: full registration form with phone telephone_field (line 42).
  • [x] app/models/lead.rb -- verified: has validates :phone, presence: true (line 12) but no format validation. Ticket correctly scopes adding format validation.
  • [x] config/routes.rb -- verified: resources :leads, only: %i[new create] (line 3), booking route (line 7), OmniAuth callback routes (lines 31-32).
  • [x] test/controllers/sessions_controller_test.rb -- verified: 20 test definitions covering all 3 lead resolution paths.

Repo Placement

Correct. All 6 file targets are in ldraney/intelligentstaffingsystems. Keycloak realm configuration properly split to pal-e-services#184 as a separate board item (#1864) with matching sprint:A label.

Dependencies

  • #1864 (pal-e-services#184, "Keycloak ISS realm: self-registration, phone field, SMTP") -- sprint:A, backlog. Prerequisite for this ticket. Documented as blocker. Correct.
  • #1786 (#6 Keycloak OIDC auth) -- done. Prerequisite satisfied.
  • #1788 (#8 Lead model + registration form) -- done. Prerequisite satisfied.
  • #1796 (#16 Postmark email) -- explicitly NOT a dependency. Correct: Keycloak handles its own SMTP.
  • No undocumented blockers found.

Acceptance Criteria

6 acceptance criteria. All testable by an agent. Assessment:
  • AC #1 (post-registration redirect to dashboard) -- existing code at sessions_controller.rb already redirects to dashboard_path. Enhancement: ensure Keycloak post-registration flow reaches this callback. Depends on pal-e-services#184.
  • AC #2 (phone validation) -- clear: add format validation to existing validates :phone, presence: true.
  • AC #3 (LeadsController reconciliation) -- appropriate agent flexibility: "either repurposed or removed if redundant."
  • AC #4 (OmniAuth callback for first-login) -- walk-in path exists (line 101). Enhancement for phone sync.
  • AC #5 (phone sync from Keycloak) -- clear and testable. Requires Keycloak phone in token (pal-e-services#184).
  • AC #6 (tests) -- test file exists with good coverage. Incremental additions needed.

Blast Radius

  • [x] app/controllers/application_controller.rb -- authenticate_user! checks session[:user] (line 41). Compatible with self-registered users via existing walk-in path. No changes needed.
  • [x] app/helpers/session_helper.rb -- exists (singular, module SessionHelper). Filename now correct in issue body.
  • Downstream: profile_controller.rb permits phone and profile/show.html.erb displays phone with "Not provided" fallback -- both compatible with this change. No blast radius concern.

Decomposition Assessment

6 file targets in 1 repo. 6 ACs (slightly over 5-AC threshold). However, AC #1 and #4 are incremental enhancements to existing code, AC #6 is a meta-criterion. Effective new work is ~3 discrete changes (phone format validation, phone sync from Keycloak token, LeadsController reconciliation). Single agent pass under 5 minutes. No decomposition needed.

Recommendations

No action needed. All previous recommendations have been addressed across R1 and R2 cycles.