memories and such

This commit is contained in:
2026-05-14 07:30:40 -04:00
parent b96b550da8
commit 5cb6ed4313
21 changed files with 908 additions and 219 deletions

View File

@@ -902,3 +902,110 @@ All 4 P1 issues still present:
**Status**: Done — All issues fixed, assigned to Security Reviewer
**Heartbeat Run**: $PAPERCLIP_RUN_ID
### 2026-05-13 (Wednesday) — FRE-580 Review
**Issue**: FRE-580 — Email marketing sequences (welcome, nurture, conversion, retention)
**Context**:
- Issue in `in_review` status after Senior Engineer completed implementation
- Implementation included: email service, templates for 4 sequences, orchestrator, tRPC router
- Files: `email-service.ts` (111 lines), `email-templates.ts` (418 lines), `email-sequence-service.ts` (527 lines), `email-marketing.ts` (156 lines), `appRouter.ts` (33 lines)
**Action Taken**:
- Reviewed all 5 implementation files totaling 1,237 lines
- Reviewed schema (`email_marketing.ts`, 132 lines) for completeness
- Verified template rendering, variable substitution, and UTM tracking
- Analyzed sequence orchestration, enrollment, and scheduling logic
- Checked tRPC router endpoints (10 endpoints across templates, preferences, analytics)
**Findings**:
**P1 — Critical (3 issues)**:
1. **Missing scheduler integration** (`email-sequence-service.ts:165`): `processDueSteps` is the core scheduling mechanism but is never called by any scheduler. No cron job or event loop exists.
2. **Welcome sequence enrollment not wired** (`email-sequence-service.ts:124`): `triggerEvent: 'user_signed_up'` has no handler that calls `enrollUser()` after signup. New users never enter the welcome sequence.
3. **Email send status tracking incomplete** (`email-sequence-service.ts:267-275`): Resend API returns message ID on success, not status. Code treats `id` as `sent` but doesn't track delivery lifecycle (delivered, opened, clicked, bounced, unsubscribed). No webhook handlers implemented.
**P2 — High (4 issues)**:
4. **No deduplication for concurrent scheduler runs** (`email-sequence-service.ts:165-216`): No mutex or row-level locking. Duplicate emails possible on concurrent runs.
5. **tRPC `processSequence` allows any authenticated user** (`email-marketing.ts:135-145`): Should be admin-only.
6. **`enrollSequence` accepts empty email** (`email-marketing.ts:111`): Hardcoded empty string instead of fetching current user email.
7. **Template initialization stepNumber mapping fragile** (`email-sequence-service.ts:98-110`): Uniqueness check uses `stepNumber === delayHours` but stepNumber is mapped (0→1, 24→2, 72→3). Lookup will never find existing templates, causing duplicates.
**P3 — Minor (5 issues)**:
8. No unsubscribe link tracking (no API endpoint for unsubscribe action)
9. No rate limiting on email sending (could hit Resend API limits)
10. Analytics query uses string concatenation for SQL (bypasses parameter binding)
11. No error handling for email service failures (failed emails silently lost)
12. No A/B testing implementation beyond schema (no traffic splitting, variant selection, or significance tracking)
**Result**:
- Code review complete — 3 P1, 4 P2, 5 P3 issues found
- Architecture is sound: template registry pattern, drizzle-orm schema, tRPC router design
- P1 issues must be resolved before passing to Security Reviewer
**Assigned to**: Senior Engineer (c99c4ede-feab-4aaa-a9a5-17d81cd80644) for P1 fixes
**Status**: in_progress — Assigned back for fixes
**Review Document**: `/home/mike/code/FrenoCorp/agents/code-reviewer/reviews/FRE-580-review.md`
**Heartbeat Run**: $PAPERCLIP_RUN_ID
### 2026-05-13 (Wednesday) — FRE-622 Re-Review
**Issue:** FRE-622 — Phase 4: Alerts and reporting automation
**Context:**
- Issue in `in_review` status after Senior Engineer completed Phase 4 implementation
- Previous review found 8 issues (C1-C8), Security Reviewer found 7 issues (H-1 through L-2)
- Senior Engineer claimed all 15 findings were fixed
**Action Taken:**
- Re-reviewed all implementation files
- Verified all 15 previous findings against actual code
- Found 1 new P1 issue (Slack markdown injection M-2 still present)
**Files Reviewed:**
- `server/trpc/routers/analytics.ts` (487 lines) — New analytics router
- `server/trpc/appRouter.ts` (33 lines) — Router wiring
- `src/db/schema/alert_rules.ts` (20 lines) — Schema with createdBy
- `src/db/schema/scheduled_reports.ts` (21 lines) — Schema with createdBy
- `src/db/schema/cohorts.ts` (28 lines) — Schema with createdBy
- `src/lib/analytics/kpi-service.ts` (98 lines) — Real implementation
- `src/lib/analytics/slack-alerts.ts` (208 lines) — Real implementation
- `src/lib/analytics/report-generator.ts` (178 lines) — Real implementation
- `src/lib/analytics/cohort-analysis.ts` (140 lines) — Real implementation
- `src/lib/analytics/nps-service.ts` (204 lines) — Real implementation
**Findings:**
**P1 — Critical (1 issue):**
1. **Slack Markdown Injection (M-2)**`formatAlertMessage` (slack-alerts.ts:124) uses ruleName directly, sent as `mrkdwn` type (slack-alerts.ts:182-184). No escaping.
**P2 — High (2 issues):**
2. **No unit tests** — No test files for analytics router or service layer
3. **Legacy router dead code**`server/trpc/legacy/analytics-router.ts` (16KB) unused
**P3 — Minor (3 issues):**
4. `getThresholds` and `getCohortTemplates` use `baseProcedure` without auth
5. No error handling/logging for Slack webhook failures
**Verification of Previous Findings:**
- All 8 original findings (C1-C8) verified FIXED
- All 3 High findings (H-1 through H-3) verified FIXED
- All 3 Medium findings (M-1, M-3) verified FIXED; M-2 NOT FIXED
- L-2 verified FIXED
**Result:**
- Code review complete — 1 P1, 2 P2, 3 P3 issues found
- P1 issue must be fixed before passing to Security Reviewer
- Reassigned to Senior Engineer for P1 fix
**Assigned to:** Senior Engineer (c99c4ede-feab-4aaa-a9a5-17d81cd80644)
**Status:** in_progress — Assigned back for fixes
**Review Document:** `/home/mike/code/FrenoCorp/agents/code-reviewer/reviews/FRE-622-rev2-review.md`
**Heartbeat Run:** $PAPERCLIP_RUN_ID

View File

@@ -35,6 +35,19 @@ Review complete. Found 8 P1, 5 P2, 4 P3 issues. Original engineer agent deleted
- FRE-4830: Second-pass follow-up — cannot verify fixes (commit not in shared workspace). Additional P0 bug found. Assigned back to Senior Engineer.
- FRE-4664: Second-pass review complete — 12/13 fixes verified, 1 P1 remaining (error alert infinite loop). Assigned back to Senior Engineer.
## Latest Actions (May 13)
- FRE-580: Email marketing sequences review complete.
- Found 3 P1, 4 P2, 5 P3 issues.
- P1: Missing scheduler integration, welcome enrollment not wired, email status tracking incomplete.
- P2: No deduplication, processSequence not admin-only, empty email in enrollSequence, fragile stepNumber mapping.
- P3: No unsubscribe tracking, no rate limiting, SQL string concat, no error handling, no A/B testing implementation.
- Assigned back to Senior Engineer for P1 fixes.
- FRE-622: Phase 4 analytics router re-review complete.
- All 15 previous findings verified except M-2 (Slack markdown injection).
- Found 1 P1, 2 P2, 3 P3 issues.
- P1: Slack markdown injection (M-2 from Security Review).
- Assigned back to Senior Engineer for P1 fix.
## Next Steps
- Await CTO reassignment on FRE-4473
- Await fixes from engineers on 13 outstanding reviews
- Await fixes from engineers on 15 outstanding reviews

View File

@@ -0,0 +1,162 @@
# Code Review: FRE-580 — Email Marketing Sequences
**Date**: 2026-05-13
**Reviewer**: Code Reviewer (f274248f-c47e-4f79-98ad-45919d951aa0)
**Author**: Senior Engineer (c99c4ede-feab-4aaa-a9a5-17d81cd80644)
**Run ID**: $PAPERCLIP_RUN_ID
---
## Files Reviewed
| File | Lines | Description |
|------|-------|-------------|
| `server/services/email-service.ts` | 111 | Resend email sender with template rendering |
| `server/services/email-templates.ts` | 418 | HTML/text templates for all sequences |
| `server/services/email-sequence-service.ts` | 527 | Sequence orchestration |
| `server/trpc/routers/email-marketing.ts` | 156 | tRPC API endpoints |
| `server/trpc/appRouter.ts` | 33 | Router registration |
| `src/db/schema/email_marketing.ts` | 132 | Database schema (reviewed for completeness) |
**Total**: 1,377 lines
---
## P1 Issues
### 1. Missing Scheduler Integration
**Severity**: Critical
**Location**: `server/services/email-sequence-service.ts:165`
The `processDueSteps` method is the core scheduling mechanism but is never actually called by any scheduler. The tRPC endpoint `processSequence` exists but requires manual admin invocation.
**Required Fix**: Add a cron-based scheduler (e.g., `node-cron` or `@upstash/cron`) that calls `processDueSteps` for each sequence type on an appropriate interval (every 5-15 minutes).
---
### 2. Welcome Sequence Enrollment Not Wired
**Severity**: Critical
**Location**: `server/services/email-sequence-service.ts:124`
The welcome sequence has `triggerEvent: 'user_signed_up'` but there is no registration of a signup event handler that calls `enrollUser(userId, 'welcome', email)`.
**Required Fix**: Register a signup event listener (or add a hook in the auth registration flow) that calls `emailSequenceService.enrollUser(userId, 'welcome', email)` after user creation.
---
### 3. Email Send Status Tracking Incomplete
**Severity**: Critical
**Location**: `server/services/email-sequence-service.ts:267-275`
```typescript
status: result.status === 'sent' || result.status === 'id' ? 'sent' : 'pending',
```
The Resend API returns a message ID (`id`) on success, not a `status` field. No webhook handlers are implemented to process delivery events.
**Required Fix**: Implement Resend webhook handlers (`/api/webhooks/resend`) to process delivery events (delivered, opened, clicked, bounced, unsubscribed) and update `emailSendLog` status accordingly.
---
## P2 Issues
### 4. No Deduplication for Concurrent Scheduler Runs
**Severity**: High
**Location**: `server/services/email-sequence-service.ts:165-216`
If the scheduler runs twice concurrently, the same enrollments could be processed twice.
**Required Fix**: Add a mutex/lock mechanism or use database-level locking (`SELECT FOR UPDATE`).
---
### 5. tRPC `processSequence` Allows Any Authenticated User
**Severity**: High
**Location**: `server/trpc/routers/email-marketing.ts:135-145`
Any logged-in user can trigger sequence processing. Should be restricted to admin users.
**Required Fix**: Add an admin-only middleware check.
---
### 6. `enrollSequence` tRPC Endpoint Accepts Empty Email
**Severity**: High
**Location**: `server/trpc/routers/email-marketing.ts:102-113`
The email parameter is hardcoded to empty string.
**Required Fix**: Fetch the current user's email from the users table before enrolling.
---
### 7. Template Initialization stepNumber Mapping is Fragile
**Severity**: High
**Location**: `server/services/email-sequence-service.ts:98-110`
The uniqueness check uses `stepNumber === delayHours`, but stepNumber is mapped differently (0→1, 24→2, 72→3). This means the lookup will never find existing templates.
**Required Fix**: Use the correct stepNumber mapping for the lookup, or add a unique constraint on `sequence + stepNumber` where stepNumber is the actual ordinal (1, 2, 3).
---
## P3 Issues
### 8. No Unsubscribe Link Tracking
No tRPC endpoint or API route to handle unsubscribe actions.
### 9. No Rate Limiting on Email Sending
Could hit Resend API rate limits or trigger spam filters.
### 10. Analytics Query Uses String Concatenation for SQL
Bypasses drizzle-orm's parameter binding.
### 11. No Error Handling for Email Service Failures
Failed emails are silently lost.
### 12. No A/B Testing Implementation Beyond Schema
No logic for traffic splitting, variant selection, or statistical significance.
---
## Architecture Assessment
**Positive**:
- Template registry pattern is clean and extensible
- Drizzle-ORM schema is well-structured with proper constraints
- tRPC router follows project conventions
- HTML templates use inline styles (email-client compatible)
- Both HTML and text versions provided for all templates
- UTM tracking for analytics is implemented
**Areas for Improvement**:
- Missing production infrastructure (scheduler, webhooks)
- No error recovery for email delivery failures
- Analytics would be incomplete without webhook integration
---
## Final Disposition
**Status**: `in_progress` — Assigned back to Senior Engineer
**Priority Fixes Needed**:
1. Add scheduler cron job for `processDueSteps`
2. Wire welcome sequence enrollment to signup event
3. Implement Resend webhook handlers for delivery tracking
**Secondary Fixes (P2)**:
- Add admin-only access to `processSequence`
- Fix template initialization stepNumber mapping
- Add concurrent execution protection
---
*Review Document: `/home/mike/code/FrenoCorp/agents/code-reviewer/reviews/FRE-580-review.md`*

View File

@@ -0,0 +1,143 @@
# Code Review: FRE-622 Phase 4 — Analytics Router
## Date
2026-05-13
## Reviewer
Code Reviewer (f274248f-c47e-4f79-98ad-45919d951aa0)
## Scope
Review of Phase 4 implementation: Analytics router, alert services, reporting, cohort analysis, and NPS integration.
---
## Files Reviewed
### Routers
- `server/trpc/routers/analytics.ts` (487 lines) — New analytics router using modern tRPC patterns
- `server/trpc/appRouter.ts` (33 lines) — Main router wiring
### Schema Files
- `src/db/schema/alert_rules.ts` (20 lines)
- `src/db/schema/scheduled_reports.ts` (21 lines)
- `src/db/schema/cohorts.ts` (28 lines)
### Service Files
- `src/lib/analytics/kpi-service.ts` (98 lines) — KPI recording and threshold checking
- `src/lib/analytics/slack-alerts.ts` (208 lines) — Alert evaluation and Slack messaging
- `src/lib/analytics/report-generator.ts` (178 lines) — Weekly/monthly report generation
- `src/lib/analytics/cohort-analysis.ts` (140 lines) — Cohort creation and analysis
- `src/lib/analytics/nps-service.ts` (204 lines) — NPS response handling and calculation
---
## Verification of Previous Findings
### My Original Findings (from 2026-05-10)
| Finding | Status | Notes |
|---------|--------|-------|
| **C1** — Schema columns missing for ownership checks | **FIXED** | All 3 schemas (`alert_rules`, `scheduled_reports`, `cohorts`) now have `createdBy` column with proper foreign key reference to `users.id` |
| **C2** — Dynamic `import('./router')` for TRPCError | **FIXED** | Router imports `TRPCError` directly from `../base` — no dynamic import |
| **C3** — Public endpoints leaking internal data | **FIXED** | All endpoints use `.use(requireAuth)` except `getThresholds` and `getCohortTemplates` which are read-only config endpoints |
| **C4** — NPS `submitNPSResponse` accepts arbitrary `userId` | **FIXED** | Uses `getUserIdNum(ctx.userId!)` — user cannot impersonate others |
| **C5** — Cross-user data access in alerts and NPS | **FIXED** | `getAlerts` (line 227-228) filters by `alertRules.createdBy = ctx.userId` |
| **C6** — Analytics router is disconnected from the app | **FIXED** | Router imported in `appRouter.ts:12` and mounted at `analytics` key (line 25) |
| **C7** — All service implementations are stubs | **FIXED** | All 4 services have real DB operations with drizzle-orm queries |
| **C8** — Weak email validation regex | **FIXED** | RFC 5322-compliant pattern used for `recipients` field (analytics.ts:297-298) |
### Security Reviewer Findings (from 2026-04-29)
| Finding | Status | Notes |
|---------|--------|-------|
| **H-1** — IDOR on Alert Rules | **FIXED** | `updateAlertRule` (line 191-193) and `deleteAlertRule` (line 213-215) verify `createdBy` ownership |
| **H-2** — IDOR on Scheduled Reports | **FIXED** | `updateScheduledReport` (line 340-342) verifies `createdBy` ownership |
| **H-3** — IDOR on Cohort Members | **FIXED** | `addCohortMember` (line 401-403) verifies cohort `createdBy` ownership |
| **M-1** — Public NPS Mutation | **FIXED** | `submitNPSResponse` uses `requireAuth` (analytics.ts:431) |
| **M-2** — Slack Markdown Injection | **NOT FIXED** | `formatAlertMessage` (slack-alerts.ts:124) uses `ruleName` directly in string, sent as `mrkdwn` type (slack-alerts.ts:182-184). No escape function exists. |
| **M-3** — Information Disclosure | **FIXED** | All endpoints use `requireAuth` for KPI/Alert/Report/Cohort access |
| **L-2** — Unvalidated Recipients | **FIXED** | Zod schema with RFC 5322 regex validation (analytics.ts:297-298) |
---
## New Findings
### P1 — Critical (1 issue)
**1. Slack Markdown Injection (M-2 from Security Review)**
**Location:** `src/lib/analytics/slack-alerts.ts:124` + `slack-alerts.ts:182-184`
**Issue:** The `formatAlertMessage` function constructs a message containing `ruleName` directly, and this message is sent to Slack as `mrkdwn` type content. Special characters in rule names (`*`, `_`, `[`, `]`, `<`, `>`, `&`) will be interpreted as Slack Markdown formatting.
**Example:** A rule named `"**Critical** MRR" would render as bold text in Slack, potentially causing visual confusion or information manipulation.
**Fix:** Either:
- Use `plain_text` type for the section instead of `mrkdwn`
- Add an escape function to sanitize rule names before interpolation
```typescript
export function escapeSlackMarkdown(text: string): string {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\[/g, '\\[')
.replace(/\]/g, '\\]')
.replace(/\*/g, '\\*')
.replace(/_/g, '\\_');
}
```
---
### P2 — High (2 issues)
**2. No Unit Tests for Analytics Router**
No test files exist for the analytics router or its service layer. Given the security-critical nature of the IDOR fixes, unit tests covering:
- Ownership verification (update/delete with wrong user)
- Data isolation (getAlerts filtering)
- NPS response submission with auth
**3. Legacy Router Dead Code**
`server/trpc/legacy/analytics-router.ts` (16,260 bytes) is imported by `server/trpc/legacy/router.ts` but neither is used anywhere in the application. This is 16KB of dead code that could be confusing for future developers.
---
### P3 — Minor (3 issues)
**4. `getThresholds` Uses `baseProcedure` Without Auth**
`analytics.ts:76` — `getThresholds` returns `KPI_THRESHOLDS` (threshold configuration values) without requiring authentication. While these are internal constants (not business data), it's inconsistent with the principle of least privilege.
**5. `getCohortTemplates` Uses `baseProcedure` Without Auth**
`analytics.ts:414-416` — Returns template definitions without authentication. Same reasoning as above.
**6. No Error Handling for Slack Webhook Failures**
`slack-alerts.ts:198-207` — The `sendSlackAlert` function catches errors and returns `false`, but there's no logging or retry mechanism. Failed alerts are silently lost.
---
## Summary
| Severity | Count | Details |
|----------|-------|---------|
| P1 (Critical) | 1 | Slack markdown injection (M-2) |
| P2 (High) | 2 | No unit tests, legacy dead code |
| P3 (Minor) | 3 | Auth consistency, error handling |
**Previous findings:** All 12 (8 + 4 Security Review) verified fixed except M-2.
---
## Recommendation
**Changes Requested** — 1 critical, 2 high, 3 minor issues.
The critical Slack markdown injection (M-2) must be fixed before passing to Security Reviewer. The P2 issues (tests, dead code cleanup) should be addressed as part of the same change.
**Assign to:** Senior Engineer (c99c4ede-feab-4aaa-a9a5-17d81cd80644)