Skip to content

feat: infrastructure hardening, tailwind 4 migration, and ai type-safety#61

Merged
EmiyaKiritsugu3 merged 15 commits into
mainfrom
fix/telemetry-and-e2e-stability
Apr 11, 2026
Merged

feat: infrastructure hardening, tailwind 4 migration, and ai type-safety#61
EmiyaKiritsugu3 merged 15 commits into
mainfrom
fix/telemetry-and-e2e-stability

Conversation

@EmiyaKiritsugu3

@EmiyaKiritsugu3 EmiyaKiritsugu3 commented Apr 11, 2026

Copy link
Copy Markdown
Owner

Overview

This PR implements a comprehensive stabilization and modernization of the PWeb stack, focusing on infrastructure resilience, privacy, design excellence, and AI reliability.

Key Changes

  • Infrastructure: Implemented a recursive PII scrubbing engine in Sentry with circular reference protection and added a resilient database heartbeat timeout in instrumentation.ts.
  • Design System: Migrated to Tailwind CSS 4 (Oxide) and unified the design system using OKLCH color tokens, replacing legacy HSL variables.
  • AI Integration: Hardened Genkit flows with strict Zod schemas and implemented hallucination detection in the student portal.
  • Type Safety: Eliminated all any casts in critical paths (Sentry, AI, Prisma) and pinned @types/pg for Next.js 15 compatibility.
  • Documentation: Finalized ADR-003 and updated project governance docs (CURRENT-STATE.md, CHANGELOG.md).

Verification

  • ✅ ESLint & Typecheck passing (managed debt explicitly documented).
  • ✅ 18/18 Unit tests passing.
  • ✅ 15/15 Playwright E2E scenarios passing locally.

Refer to docs/decisions/ADR-003-infrastructure-observability-hardening.md for detailed architectural rationale.

Summary by CodeRabbit

  • New Features

    • Sentry upgraded to v10 with enhanced error tracking, session replay with privacy masking, and automatic sensitive data scrubbing
    • User identity linked to observability platform for better issue tracking
    • Database startup health checks for improved reliability
    • Vercel integration for automated environment and sourcemap management
  • Bug Fixes

    • Fixed ESLint configuration to prevent production build failures
    • Improved database connection resilience with explicit pool governance
  • Documentation

    • Updated infrastructure observability and security documentation

@vercel

vercel Bot commented Apr 11, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
smartmanagementsystem Ready Ready Preview, Comment Apr 11, 2026 10:51am

@coderabbitai

coderabbitai Bot commented Apr 11, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR modernizes Sentry to v10 for enhanced observability, adds recursive PII scrubbing to sanitize sensitive data in logs, configures ESLint flat config for TypeScript support, implements database startup health checks with explicit pool management in Prisma, pins @types/pg to eliminate unsafe type casts, and migrates the UI theme system to Tailwind 4 with OKLCH color scheme while updating documentation and configuration files accordingly.

Changes

Cohort / File(s) Summary
Sentry Configuration & Initialization
.sentryclirc, sentry.server.config.ts, sentry.edge.config.ts, next.config.ts
Updated Sentry v10 setup: added .sentryclirc for org/project IDs, modified server/edge configs to enable Sentry based on DSN presence rather than NODE_ENV, implemented recursive beforeSend hook in server config for PII scrubbing (sanitizes sensitive keys like cpf, password, biometriaHash), and updated next.config.ts project identifier and sourcemap handling.
Sentry Client Integration
src/instrumentation-client.ts, instrumentation.ts
Added new client-side Sentry initialization with replay masking and router transition tracking, removed old instrumentation-client.ts. Updated instrumentation.ts to include database heartbeat check (SELECT 1) with 5-second timeout and Sentry error reporting on failure (Node.js runtime only).
User Identity & Auth Context
src/components/providers/auth-provider.tsx, src/utils/supabase/server.ts
Integrated Sentry user context linking: auth provider now sets/clears Sentry user data on auth state changes, and new getUser helper in server utils handles Sentry context updates during user fetching.
Database Resilience
src/lib/prisma.ts
Enhanced Prisma pooling with explicit max, idleTimeoutMillis, and connectionTimeoutMillis settings, added DATABASE_URL validation, removed unsafe as any type cast for PrismaPg adapter, refined computed fields for xpToNextLevel and progressPerc calculations, and added pool error handler.
Type Safety & Dependencies
package.json
Pinned @types/pg to 8.11.11 (via devDependencies and overrides) to align with Prisma expectations, added @opentelemetry/exporter-jaeger ^1.25.1, and removed OpenTelemetry-related package overrides.
ESLint Configuration
eslint.config.mjs
Restored TypeScript support by importing @typescript-eslint/eslint-plugin and switching languageOptions.parser to @typescript-eslint/parser, added explicit parserOptions for ES modules.
Exercise Flow & Monitoring
src/ai/flows/workout-generator-flow.ts, src/app/aluno/meus-treinos/meus-treinos-client.tsx
Refactored optional chaining for stream output handling in workout generator, added Sentry warning capture in meus-treinos client when generated exercise names don't match known exercises, removed unsafe any type casting.
Theme System Migration
src/app/design-system.css, src/app/globals.css
Migrated from custom design-system.css to Tailwind 4 with integrated @theme variables (glass morphism and glow effects with OKLCH-based colors), consolidated component classes (.glass-card, .glow-*, .text-gradient-*) into globals.css, removed legacy Shadcn HSL-based compatibility layer.
Environment & Configuration
.env.example
Updated Sentry documentation comment to reflect new project context (smartmanagementesystem).
Documentation & Metadata
CHANGELOG.md, CLAUDE.md, docs/CURRENT-STATE.md, docs/decisions/ADR-003-infrastructure-observability-hardening.md, docs/observability/SLOS.md, docs/security/THREAT-MODEL.md, docs/.obsidian/...
Added changelog entry for Sentry v10/Next.js 15 integration, updated project metadata and active tech stack, created ADR-003 documenting infrastructure hardening decisions (PII scrubbing, type safety, database resilience, Genkit hardening, Tailwind 4 migration), revised SLOs and threat model to reflect Sentry/Vercel integration and mitigation status, updated Obsidian workspace state.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Browser Client
    participant App as Next.js App<br/>(auth-provider)
    participant Sentry as Sentry Service
    participant Server as Backend<br/>(beforeSend)

    rect rgba(100, 200, 150, 0.5)
    Note over Client,Server: Initial Page Load - User Auth
    Client->>App: Load page / auth state
    App->>Client: Sentry.init(DSN)<br/>enable replays & masking
    end

    rect rgba(100, 150, 200, 0.5)
    Note over Client,Server: User Authentication
    Client->>App: User logs in via Supabase
    App->>App: getUser() from Supabase
    App->>Server: Sentry.setUser({id, email})
    Server->>Sentry: User context linked
    end

    rect rgba(200, 150, 100, 0.5)
    Note over Client,Server: Event Capture & PII Scrubbing
    Client->>Client: Error / breadcrumb event
    Client->>Sentry: Send event
    Sentry->>Server: beforeSend hook
    Server->>Server: Recursive scrub(event)<br/>- Remove headers (cookie, auth)<br/>- Sanitize keys (cpf,<br/>password, etc.) → [SCRUBBED]<br/>- Handle nested objects & arrays
    Server->>Sentry: Return sanitized event
    Sentry->>Sentry: Store safe event data
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly Related PRs

Poem

🐰 Hop, hop, hop! A rabbit's delight,
Sentry's v10 now shines so bright!
PII scrubbed and secrets concealed,
User contexts linked, telemetry revealed—
With Tailwind's colors in OKLCH hue,
Observability and style, both fresh and true! 🌈✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the three primary areas of change: infrastructure hardening, Tailwind 4 migration, and AI type-safety improvements, matching the changeset's scope and intent.
Description check ✅ Passed The description covers all required sections with sufficient detail: overview of changes, key improvements across infrastructure/design/AI/type-safety, and verification status. All major changes are documented.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/telemetry-and-e2e-stability

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🧹 Nitpick comments (1)
docs/.obsidian/workspace.json (1)

194-231: Avoid committing volatile lastOpenFiles/active-tab workspace churn.

Lines 194–231 are machine-local editor state and will create noisy diffs/conflicts over time. Consider keeping this file stable (or excluding volatile keys from version control).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.obsidian/workspace.json` around lines 194 - 231, The commit includes
machine-local editor state keys "lastOpenFiles" and "active" which cause noisy
diffs; remove those keys from the JSON (or revert the file to a stable template)
before committing and stop tracking volatile workspace churn by either adding
this workspace file to .gitignore or updating your pre-commit/CI to strip
"lastOpenFiles" and "active" (or other Obsidian-specific volatile keys) from the
file automatically; locate the keys "lastOpenFiles" and "active" in the
workspace JSON and ensure only stable settings remain in version control.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Line 63: Update the malformed comment on line 63 to clearly separate
instructions for NEXT_PUBLIC_SENTRY_DSN and SENTRY_AUTH_TOKEN: replace the
merged note ("Cli# Sentry Observability...") with two concise guidance lines
indicating where to obtain each value (e.g., "NEXT_PUBLIC_SENTRY_DSN: get from
Sentry → Project → Settings → Client Keys / DSN" and "SENTRY_AUTH_TOKEN: get
from Sentry → Settings → API Keys or Auth Tokens"), and remove the truncated
project identifier so the comment reads unambiguous guidance for
NEXT_PUBLIC_SENTRY_DSN and SENTRY_AUTH_TOKEN.

In `@CHANGELOG.md`:
- Around line 8-27: The changelog contains a duplicate "Unreleased" section
(e.g., the header "## [Unreleased] — 2026-04-11 — Sentry & Build Stability");
consolidate by keeping a single "Unreleased" heading and merging these new
entries into that section, and move any older or previously-unreleased entries
into dated/versioned headings below (creating or updating versioned headers as
needed) so there is only one Unreleased block in the file.

In `@docs/CURRENT-STATE.md`:
- Line 100: Update the managed-debt note that currently names the wrong package;
replace the incorrect package token `@pg/types` with the correct dependency name
`@types/pg` in the sentence describing Prisma Type Overrides (the note
referencing pinning to resolve Prisma 7/Next 15 conflicts) so the documentation
matches the actual pin in this PR.
- Around line 20-23: The table rows "ESLint quality gate", "TypeScript
typecheck", "Unit tests", and "Ops documentation" contain counts that conflict
with other sections (notably the unit test total/warning counts shown
elsewhere); update these entries so they match the source of truth (CI/test
runner and linter reports) and then search for and replace all other occurrences
of those same values (e.g., the unit test total and warning counts in the other
summary block) so every instance in the document is consistent; verify "Unit
tests" count and any warning/error counts are identical to the test results and
correct any mismatched numbers found in the other section referenced.

In `@docs/decisions/ADR-003-infrastructure-observability-hardening.md`:
- Around line 1-60: Run Prettier on the ADR file
(ADR-003-infrastructure-observability-hardening.md) to fix CI formatting
failures; open the file (the document titled "ADR-003: Infrastructure &
Observability Hardening") and apply your project's Prettier config (e.g.,
npm/yarn run prettier --write or your editor's format command) so the markdown
spacing, headings, lists, and image links are normalized, then re-run lint/CI
and commit the reformatted file.
- Around line 55-60: The markdown uses local file:// image URLs (e.g.,
file:///home/.../student_dashboard_view_1775903233752.png and
workout_generator_final_result_1775903924170.png) which won't render in the
repo; move those PNGs into the repository (suggested folder docs/assets/) and
update the image references in ADR-003 to use repo-relative paths (e.g.,
![Student Dashboard - Premium
Dark](./assets/student_dashboard_view_1775903233752.png) and ![AI Workout
Generator UI](./assets/workout_generator_final_result_1775903924170.png));
ensure the filenames match exactly and commit the assets alongside the updated
docs/decisions/ADR-003-infrastructure-observability-hardening.md.

In `@docs/observability/SLOS.md`:
- Line 37: The table entry incorrectly claims CPF/email logged to stdout is
fully mitigated; update the mitigation column for the CPF/email plaintext stdout
row to "Partial / Not mitigated" and adjust the mitigation text to state that
Sentry's beforeSend only scrubs events sent to Sentry (reference beforeSend) and
does not affect generic stdout logging, and add a short remediation note that
stdout logging must be scrubbed at source or via the logging pipeline (e.g.,
implement scrubbing in the logger or add a log-processor filter) to be
considered fully mitigated.
- Around line 34-38: The document lost a formal SLO entry for SLO-03; restore a
dedicated SLO-03 section that states the objective, numeric target, and clear
measurement method (e.g., "SLO-03: Prevent server error stack traces being
exposed to clients — Target: 99.9% of requests do not return stack traces in
production; Measurement: periodic scans of 5xx responses and Sentry/Next.js
server-side error sampling over a 30-day window"), and replace the current
risk-register row that references the same concern with a brief summary linking
to SLO-03; ensure you reference the same wording used elsewhere ("Server error
exposes stack trace to client", "SLO-03") so other tables/tooling remain
consistent.

In `@docs/security/THREAT-MODEL.md`:
- Around line 51-56: Update the remaining action item in THREAT-MODEL.md that
still says to implement Sentry's beforeSend in the future: locate the action
list mentioning "implement beforeSend" and change it to indicate beforeSend is
implemented/active (matching the rows for "CPF/email logged to stdout" and
"Server error exposes stack trace"), remove the TODO/future wording, and mark
the status as mitigated (e.g., "✅ Mitigated (beforeSend active)") so the
document no longer contradicts the Sentry/beforeSend entries.

In `@eslint.config.mjs`:
- Around line 3-4: The ESLint config imports `@typescript-eslint/eslint-plugin`
and `@typescript-eslint/parser` but those packages are missing from package.json;
add both "@typescript-eslint/eslint-plugin" and "@typescript-eslint/parser" to
devDependencies in package.json and install them (e.g., run npm install
--save-dev `@typescript-eslint/eslint-plugin` `@typescript-eslint/parser` or your
project package manager equivalent) so the imports in eslint.config.mjs resolve
and the lint step can run; update the lockfile/commit the package.json changes.

In `@sentry.server.config.ts`:
- Around line 24-35: The allowlist entries in sensitiveKeys are mixed case so
comparisons using key.toLowerCase() never match camelCase names (e.g.,
biometiaHash, fotoUrl); update sensitiveKeys to use all-lowercase strings (e.g.,
"biometriahash", "fotourl") or normalize both sides when filtering, and apply
the same fix to the other sensitive-key lists mentioned later in the file (the
other sensitiveKeys usages around the Sentry config / beforeSend filter). Ensure
you reference and change the sensitiveKeys constant and any code that does
key.toLowerCase() comparisons so matching is reliably case-insensitive.

In `@src/app/aluno/meus-treinos/meus-treinos-client.tsx`:
- Around line 165-184: The mapped block that builds novosExercicios (mapping
workout.exercicios, using EXERCICIOS_POR_GRUPO, finding exercicioMaster and
calling Sentry.captureMessage) is not formatted to Prettier rules; re-run your
formatter (or adjust spacing/line breaks) on the novosExercicios mapping so the
arrow function, the find callback, the Sentry.captureMessage call and the
returned object follow project Prettier configuration and fix the CI failure.
- Around line 170-183: The code logs hallucinated exercises via
Sentry.captureMessage but still returns an object with descricao: '' which
persists invalid data into Treino.exercicios; change the mapping logic where
exercicioMaster is resolved (the block that currently calls
Sentry.captureMessage and then returns the exercise object) to either (a) reject
the workout by throwing/returning an error when exercicioMaster is missing so
the whole workout is rejected/returned to the user, or (b) drop that exercise
from the returned exercises array and flag/notify the user that exercises were
removed; ensure you remove the branch that returns an object with descricao: ''
and instead omit the exercise (or throw) and propagate an error/notification
upstream so downstream screens never receive the hallucinated exercise.

In `@src/app/globals.css`:
- Around line 60-79: Run Prettier to fix formatting in the CSS that’s failing
the Prettier check: format the `@keyframes` blocks (accordion-down, accordion-up,
circular-progress, float, glow-pulse) and the surrounding CSS in this file using
your project's Prettier config (e.g., run the repo’s prettier script or npx
prettier --write on the file), then commit the resulting formatting-only changes
so CI/Prettier --check passes.

In `@src/components/providers/auth-provider.tsx`:
- Around line 46-53: Replace calls to Sentry.setUser that pass the Supabase
user's email so only the opaque user id is sent: locate the Sentry.setUser
usages in this file (the blocks that inspect sbUser and call Sentry.setUser({
id: sbUser.id, email: sbUser.email }) at both occurrences) and remove the email
property so they call Sentry.setUser({ id: sbUser.id }) when sbUser exists (keep
Sentry.setUser(null) in the else branches unchanged).

In `@src/lib/prisma.ts`:
- Around line 40-43: Clamp the level before doing percentage math to avoid
divide-by-zero or negative xpReq: compute a safeLevel = Math.max(aluno.nivel ??
1, 1) (or similar) and use that safeLevel when calculating xpReq and the
progress percent instead of using nivel directly; update references to
nivel/xpReq/exp in the function that returns the progress percent so xpReq is
always >= 1.

In `@src/utils/supabase/server.ts`:
- Around line 40-43: Sentry is leaking user.email because event.user isn't
scrubbed; update the Sentry initialization's beforeSend hook to scrub event.user
in addition to event.breadcrumbs and event.extra by reusing the existing
sensitiveKeys and scrub() logic and removing/redacting any PII in
event.user.email (and other user fields) before returning the event;
specifically modify the beforeSend hook used by the server Sentry init (the
function that references sensitiveKeys and scrub()) to process event.user, and
add an equivalent beforeSend hook to the client Sentry init (the
instrumentation-client initialization) so that Sentry.setUser calls do not
transmit raw PII from either server or client.

---

Nitpick comments:
In `@docs/.obsidian/workspace.json`:
- Around line 194-231: The commit includes machine-local editor state keys
"lastOpenFiles" and "active" which cause noisy diffs; remove those keys from the
JSON (or revert the file to a stable template) before committing and stop
tracking volatile workspace churn by either adding this workspace file to
.gitignore or updating your pre-commit/CI to strip "lastOpenFiles" and "active"
(or other Obsidian-specific volatile keys) from the file automatically; locate
the keys "lastOpenFiles" and "active" in the workspace JSON and ensure only
stable settings remain in version control.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d5eb906-e8f7-4069-967d-47163b42544e

📥 Commits

Reviewing files that changed from the base of the PR and between 3d2d2ff and 285948c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (25)
  • .env.example
  • .sentryclirc
  • CHANGELOG.md
  • CLAUDE.md
  • docs/.obsidian/graph.json
  • docs/.obsidian/workspace.json
  • docs/CURRENT-STATE.md
  • docs/decisions/ADR-003-infrastructure-observability-hardening.md
  • docs/observability/SLOS.md
  • docs/security/THREAT-MODEL.md
  • eslint.config.mjs
  • instrumentation-client.ts
  • instrumentation.ts
  • next.config.ts
  • package.json
  • sentry.edge.config.ts
  • sentry.server.config.ts
  • src/ai/flows/workout-generator-flow.ts
  • src/app/aluno/meus-treinos/meus-treinos-client.tsx
  • src/app/design-system.css
  • src/app/globals.css
  • src/components/providers/auth-provider.tsx
  • src/instrumentation-client.ts
  • src/lib/prisma.ts
  • src/utils/supabase/server.ts
💤 Files with no reviewable changes (2)
  • instrumentation-client.ts
  • src/app/design-system.css

Comment thread .env.example

# Sentry (error tracking)
# Get from sentry.io → Project → Settings → Client Keys
# Get from sentry.io → Project → Settings → Cli# Sentry Observability (smartmanagementesystem: ufrn-universidade-federal-do-r)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the malformed Sentry setup guidance comment.

Line 63 currently merges two notes (Cli# ...) and is easy to misread when setting NEXT_PUBLIC_SENTRY_DSN vs SENTRY_AUTH_TOKEN.

Suggested doc fix
-# Get from sentry.io → Project → Settings → Cli# Sentry Observability (smartmanagementesystem: ufrn-universidade-federal-do-r)
+# DSN: sentry.io → Project Settings → Client Keys (DSN)
+# Auth token: sentry.io → Organization Settings → Auth Tokens
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Get from sentry.io → Project Settings → Cli# Sentry Observability (smartmanagementesystem: ufrn-universidade-federal-do-r)
# DSN: sentry.io → Project Settings → Client Keys (DSN)
# Auth token: sentry.io → Organization Settings → Auth Tokens
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example at line 63, Update the malformed comment on line 63 to clearly
separate instructions for NEXT_PUBLIC_SENTRY_DSN and SENTRY_AUTH_TOKEN: replace
the merged note ("Cli# Sentry Observability...") with two concise guidance lines
indicating where to obtain each value (e.g., "NEXT_PUBLIC_SENTRY_DSN: get from
Sentry → Project → Settings → Client Keys / DSN" and "SENTRY_AUTH_TOKEN: get
from Sentry → Settings → API Keys or Auth Tokens"), and remove the truncated
project identifier so the comment reads unambiguous guidance for
NEXT_PUBLIC_SENTRY_DSN and SENTRY_AUTH_TOKEN.

Comment thread CHANGELOG.md
Comment on lines +8 to +27
## [Unreleased] — 2026-04-11 — Sentry & Build Stability

### Added

- **Sentry v10 Modernization**: migrated to Next.js 15 standards using `src/instrumentation-client.ts` for navigation tracing.
- **Deep PII Scrubbing**: implemented a recursive sanitization engine in `sentry.server.config.ts` to protect student data (CPF, health hashes) in server logs.
- **Privacy-First Replays**: enabled Sentry Replay with strict `maskAllInputs: true` and `maskAllText: true` to prevent PII leakage.
- **DB Connection Heartbeat**: added a surgical `SELECT 1` check in `instrumentation.ts` to verify database health on application boot.
- **Full-stack User Traceability**: linked Supabase UUID/Email to Sentry context on both client and server.
- **Vercel CI/CD Integration**: Linked Sentry to Vercel for automated environment variable synchronization and sourcemap deployments.

### Changed

- **Prisma 7 Type Elevation**: removed generic `as any` casts by pinning `@types/pg` to version `8.11.11` via package `overrides`.
- **Database Pool Governance**: implemented `max: 20`, `idleTimeout`, and `connectionTimeout` in `src/lib/prisma.ts` for improved resilience.

### Fixed

- **ESLint Flat Config**: repaired missing `@typescript-eslint` plugin and parser configuration in `eslint.config.mjs` that was breaking production builds.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consolidate duplicate Unreleased sections.

This adds another Unreleased block while the file already has multiple existing ones. Please keep a single Unreleased section and move older entries under dated/versioned headings.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 8 - 27, The changelog contains a duplicate
"Unreleased" section (e.g., the header "## [Unreleased] — 2026-04-11 — Sentry &
Build Stability"); consolidate by keeping a single "Unreleased" heading and
merging these new entries into that section, and move any older or
previously-unreleased entries into dated/versioned headings below (creating or
updating versioned headers as needed) so there is only one Unreleased block in
the file.

Comment thread docs/CURRENT-STATE.md
Comment on lines +20 to +23
| ESLint quality gate | ✅ Done | 0 errors — `any` + unused vars |
| TypeScript typecheck | ✅ Clean | 0 errors (strict mode) |
| Unit tests | ✅ Passing | 18/18 (Vitest) |
| Ops documentation | ✅ Done | Runbook, SLOs, threat model |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Sync reported counts across this document.

The updated values conflict with other sections (e.g., unit test total and warning counts). Please reconcile these numbers so readiness status is unambiguous.

Also applies to: 33-39

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/CURRENT-STATE.md` around lines 20 - 23, The table rows "ESLint quality
gate", "TypeScript typecheck", "Unit tests", and "Ops documentation" contain
counts that conflict with other sections (notably the unit test total/warning
counts shown elsewhere); update these entries so they match the source of truth
(CI/test runner and linter reports) and then search for and replace all other
occurrences of those same values (e.g., the unit test total and warning counts
in the other summary block) so every instance in the document is consistent;
verify "Unit tests" count and any warning/error counts are identical to the test
results and correct any mismatched numbers found in the other section
referenced.

Comment thread docs/CURRENT-STATE.md

1. **Sentry Boundary Casts**: Use of `any` in `beforeSend` (suppressed with `eslint-disable`) is required due to Sentry SDK interface rigidness.
2. **Server console.log**: 31 warnings in Server Actions are preserved to ensure minimal observability in the terminal during development.
3. **Prisma Type Overrides**: We use `@pg/types` pinning to resolve Prisma 7/Next 15 conflicts until `@prisma/adapter-pg` upstream fixes land.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix dependency name typo in managed debt note.

Line [100] references @pg/types, but the pinning in this PR is for @types/pg.

Proposed fix
-3. **Prisma Type Overrides**: We use `@pg/types` pinning to resolve Prisma 7/Next 15 conflicts until `@prisma/adapter-pg` upstream fixes land.
+3. **Prisma Type Overrides**: We use `@types/pg` pinning to resolve Prisma 7/Next 15 conflicts until `@prisma/adapter-pg` upstream fixes land.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
3. **Prisma Type Overrides**: We use `@pg/types` pinning to resolve Prisma 7/Next 15 conflicts until `@prisma/adapter-pg` upstream fixes land.
3. **Prisma Type Overrides**: We use `@types/pg` pinning to resolve Prisma 7/Next 15 conflicts until `@prisma/adapter-pg` upstream fixes land.
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 100-100: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/CURRENT-STATE.md` at line 100, Update the managed-debt note that
currently names the wrong package; replace the incorrect package token
`@pg/types` with the correct dependency name `@types/pg` in the sentence
describing Prisma Type Overrides (the note referencing pinning to resolve Prisma
7/Next 15 conflicts) so the documentation matches the actual pin in this PR.

Comment on lines +1 to +60
# ADR-003: Infrastructure & Observability Hardening

## Status
Accepted

## Context
As part of the modernization for Next.js 15, Sentry 10, and Prisma 7, several structural challenges were identified:
1. **PII Vulnerability**: Server-side error logs and client-side session replays were potentially capturing sensitive student data (CPF, health hashes).
2. **Type Safety Decay**: The move to Prisma 7 with the `pg` driver adapter introduced a version conflict in `@types/pg`, leading to the widespread use of `as any` in the database layer.
3. **Connection Governance**: Migrating to a driver adapter pattern (required for some Next.js environments) necessitated more explicit connection pool management to prevent exhaustion in serverless or PgBouncer environments.

## Decisions

### 1. Recursive PII Scrubbing (Sentry)
Instead of relying on basic header scrubbing, we implemented a dedicated recursive sanitization engine in `sentry.server.config.ts`. This engine traverses all event breadcrumbs and extra data, replacing values for a defined list of sensitive keys (`cpf`, `password`, `biometriaHash`, etc.) with `[SCRUBBED]`.

### 2. Dependency Pinning via Overrides
To resolve the `ClientBase` return type mismatch between `@types/pg@v8.18+` and `@prisma/adapter-pg`, we decided to:
- Pin `@types/pg` to version `8.11.11` in both `devDependencies` and the `overrides` field of `package.json`.
- This ensures that all internal and external consumers of the `pg` types are aligned with the Prisma runtime requirements, allowing for the complete removal of unsafe type assertions in `src/lib/prisma.ts`.

### 3. Connection Resilience & Heartbeat
We enhanced the `PrismaClient` initialization with:
- **Pool Governance**: Explicit `idleTimeout` and `connectionTimeout` to handle transient network issues.
- **Boot Heartbeat**: Integrated a `SELECT 1` check into the Next.js `instrumentation.ts` with a 5-second `Promise.race` timeout to verify database health during application startup, preventing "zombie" boot states.

### 4. Genkit AI Flow Contract Hardening
To prevent technical debt in AI integration, we implemented:
- **Strict Schema Enforcement**: All Genkit flows now utilize Zod schemas for input, output, and streaming, eliminating `as any` casts in the frontend.
- **Hallucination Detection**: Added Sentry monitoring to log unrecognized exercise suggestions, ensuring that AI-generated plans remain within physiological and logistical constraints defined in `src/lib/constants.ts`.

### 5. Tailwind 4 & OKLCH Unified Theme
Migrated the entire design system to Tailwind 4 (Oxide engine) to leverage:
- **Native Custom Properties**: Replaced legacy HSL `:root` variables with a unified `@theme` block.
- **OKLCH Color Space**: Adopted OKLCH tokens for consistent vibrancy and accessibility across the "Premium Dark" interface.

## Consequences

### Positive
- **Privacy Compliance**: Strict adherence to data protection standards for student information.
- **Developer Experience**: Restored full IDE intellisense and type-checking in the database and AI layers.
- **Aesthetic Excellence**: Unified, vibrant UI with optimized build performance via Tailwind 4.
- **Operational Visibility**: Sentry alerts for both infrastructure failures and AI "hallucinations".

### Negative
- **Maintenance**: We must monitor Prisma and Genkit SDK updates to revert specific type overrides or casts once upstream packages reach full alignment with Next.js 15.

---

## Visual Documentation

### 1. Premium Dark Interface (Tailwind 4)
The new design system ensures a high-fidelity, high-contrast experience using OKLCH color tokens.

![Student Dashboard - Premium Dark](file:///home/emiyakiritsugu/.gemini/antigravity/brain/f9bac6cb-9ccd-44da-af5f-669178bd052a/student_dashboard_view_1775903233752.png)

### 2. AI Workout Generator (Genkit)
Strictly typed integration between the AI service and the student portal.

![AI Workout Generator UI](file:///home/emiyakiritsugu/.gemini/antigravity/brain/f9bac6cb-9ccd-44da-af5f-669178bd052a/workout_generator_final_result_1775903924170.png)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Address the Prettier failure for this ADR file.

CI already reports formatting issues here; please run Prettier on this file before merge.

🧰 Tools
🪛 GitHub Actions: CI

[warning] 1-1: Prettier --check reported formatting issues in this file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/decisions/ADR-003-infrastructure-observability-hardening.md` around
lines 1 - 60, Run Prettier on the ADR file
(ADR-003-infrastructure-observability-hardening.md) to fix CI formatting
failures; open the file (the document titled "ADR-003: Infrastructure &
Observability Hardening") and apply your project's Prettier config (e.g.,
npm/yarn run prettier --write or your editor's format command) so the markdown
spacing, headings, lists, and image links are normalized, then re-run lint/CI
and commit the reformatted file.

Comment on lines +170 to +183
if (!exercicioMaster) {
Sentry.captureMessage(`AI Hallucination: Exercise "${ex.nomeExercicio}" not found in constants.`, {
level: 'warning',
extra: { workout: workout.nome, goal: data.objetivo },
});
}

return {
nomeExercicio: ex.nomeExercicio,
series: ex.series,
repeticoes: ex.repeticoes,
observacoes: ex.observacoes,
descricao: exercicioMaster?.descricao || '',
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t persist hallucinated exercises after detecting them.

This branch reports the mismatch to Sentry, but it still saves the unknown nomeExercicio with an empty descricao. That bakes invalid AI output into Treino.exercicios, so downstream screens drift even though the hallucination was detected. Reject the workout (or drop the invalid exercise/workout and notify the user) once exercicioMaster is missing.

💡 Minimal fix
             if (!exercicioMaster) {
               Sentry.captureMessage(`AI Hallucination: Exercise "${ex.nomeExercicio}" not found in constants.`, {
                 level: 'warning',
                 extra: { workout: workout.nome, goal: data.objetivo },
               });
+              throw new Error(`A IA retornou um exercício inválido: ${ex.nomeExercicio}`);
             }

             return {
               nomeExercicio: ex.nomeExercicio,
               series: ex.series,
               repeticoes: ex.repeticoes,
               observacoes: ex.observacoes,
-              descricao: exercicioMaster?.descricao || '',
+              descricao: exercicioMaster.descricao,
             };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/aluno/meus-treinos/meus-treinos-client.tsx` around lines 170 - 183,
The code logs hallucinated exercises via Sentry.captureMessage but still returns
an object with descricao: '' which persists invalid data into Treino.exercicios;
change the mapping logic where exercicioMaster is resolved (the block that
currently calls Sentry.captureMessage and then returns the exercise object) to
either (a) reject the workout by throwing/returning an error when
exercicioMaster is missing so the whole workout is rejected/returned to the
user, or (b) drop that exercise from the returned exercises array and
flag/notify the user that exercises were removed; ensure you remove the branch
that returns an object with descricao: '' and instead omit the exercise (or
throw) and propagate an error/notification upstream so downstream screens never
receive the hallucinated exercise.

Comment thread src/app/globals.css
Comment on lines 60 to 79
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
from { height: 0; }
to { height: var(--radix-accordion-content-height); }
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
from { height: var(--radix-accordion-content-height); }
to { height: 0; }
}
@keyframes circular-progress {
0% {
stroke-dashoffset: 100;
}
100% {
stroke-dashoffset: 0;
}
0% { stroke-dashoffset: 100; }
100% { stroke-dashoffset: 0; }
}
@keyframes float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
@keyframes glow-pulse {
0%,
100% {
opacity: 0.5;
}
50% {
opacity: 1;
}
0%, 100% { opacity: 0.5; }
50% { opacity: 1; }
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prettier gate is failing for this file.

CI reports Prettier --check failure here. Please run Prettier on src/app/globals.css and commit the formatting-only update so the quality gate passes.

Also applies to: 101-111, 130-139

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/globals.css` around lines 60 - 79, Run Prettier to fix formatting in
the CSS that’s failing the Prettier check: format the `@keyframes` blocks
(accordion-down, accordion-up, circular-progress, float, glow-pulse) and the
surrounding CSS in this file using your project's Prettier config (e.g., run the
repo’s prettier script or npx prettier --write on the file), then commit the
resulting formatting-only changes so CI/Prettier --check passes.

Comment on lines +46 to +53
if (sbUser) {
Sentry.setUser({
id: sbUser.id,
email: sbUser.email,
});
} else {
Sentry.setUser(null);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In @sentry/nextjsv10, are values set viaSentry.setUser({ email })sent as top-levelevent.userdata, and doesbeforeSend automatically scrub those user fields or only the event payload that the hook explicitly mutates?

💡 Result:

Yes, in @sentry/nextjs v10, values set via Sentry.setUser({ email }) are sent as top-level event.user data in Sentry events. The setUser method sets user context on the current scope (typically the isolation scope in Next.js for request isolation), and when an event is captured, the SDK merges scope data—including the user—into the event payload as the top-level event.user object before applying processors like beforeSend. The beforeSend callback receives the fully processed event with event.user already populated from the scope. It does not automatically scrub user fields; scrubbing must be done explicitly in the callback if desired, e.g., beforeSend(event) { if (event.user) delete event.user.email; return event; }. beforeSend only scrubs what the hook explicitly mutates—it processes the complete event payload, including merged scope data like event.user, but automatic scrubbing of user fields requires manual implementation or server-side rules. No automatic scrubbing of setUser fields occurs in the SDK itself; server-side data scrubbing in Sentry projects handles patterns like PII but not specifically user fields unless configured.

Citations:


🏁 Script executed:

find . -name "sentry.server.config.ts" -o -name "sentry*config*" | head -20

Repository: EmiyaKiritsugu3/PWeb_Project

Length of output: 121


🏁 Script executed:

cat -n sentry.server.config.ts

Repository: EmiyaKiritsugu3/PWeb_Project

Length of output: 2886


Remove user email from Sentry context; use only the opaque user ID.

Sentry.setUser({ id, email }) sends raw email addresses as top-level event.user.email data to Sentry. The sentry.server.config.ts beforeSend hook scrubs email from breadcrumbs and extra fields but does not scrub event.user itself. The opaque Supabase user ID is sufficient for correlation without exposing direct PII to a third-party service.

Sentry.setUser({ id: sbUser.id });

Applies to: lines 46-53 and 69-76.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/providers/auth-provider.tsx` around lines 46 - 53, Replace
calls to Sentry.setUser that pass the Supabase user's email so only the opaque
user id is sent: locate the Sentry.setUser usages in this file (the blocks that
inspect sbUser and call Sentry.setUser({ id: sbUser.id, email: sbUser.email })
at both occurrences) and remove the email property so they call Sentry.setUser({
id: sbUser.id }) when sbUser exists (keep Sentry.setUser(null) in the else
branches unchanged).

Comment thread src/lib/prisma.ts
Comment on lines +40 to +43
const nivel = aluno.nivel ?? 1;
const exp = aluno.exp ?? 0;
const xpReq = nivel * 1500;
return Math.min(Math.round((exp / xpReq) * 100), 100);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clamp level before percentage math to avoid invalid progress values.

If nivel is 0 (or negative due to bad data), xpReq becomes non-positive and percentage math becomes incorrect. Clamp to at least 1 before computing.

Proposed fix
-            const nivel = aluno.nivel ?? 1;
+            const nivel = Math.max(aluno.nivel ?? 1, 1);
             const exp = aluno.exp ?? 0;
             const xpReq = nivel * 1500;
             return Math.min(Math.round((exp / xpReq) * 100), 100);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const nivel = aluno.nivel ?? 1;
const exp = aluno.exp ?? 0;
const xpReq = nivel * 1500;
return Math.min(Math.round((exp / xpReq) * 100), 100);
const nivel = Math.max(aluno.nivel ?? 1, 1);
const exp = aluno.exp ?? 0;
const xpReq = nivel * 1500;
return Math.min(Math.round((exp / xpReq) * 100), 100);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/prisma.ts` around lines 40 - 43, Clamp the level before doing
percentage math to avoid divide-by-zero or negative xpReq: compute a safeLevel =
Math.max(aluno.nivel ?? 1, 1) (or similar) and use that safeLevel when
calculating xpReq and the progress percent instead of using nivel directly;
update references to nivel/xpReq/exp in the function that returns the progress
percent so xpReq is always >= 1.

Comment on lines +40 to +43
Sentry.setUser({
id: user.id,
email: user.email,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate Sentry init/scrubbing hooks =="
rg -n -C3 --type=ts --type=tsx 'Sentry\.init|beforeSend|beforeBreadcrumb|setUser|scrub|sanitize|PII|user\.email'

echo
echo "== Locate all explicit Sentry user email attachments =="
rg -n -C2 --type=ts --type=tsx 'Sentry\.setUser\(\{[^}]*email|email:\s*user\.email|email:\s*sbUser\.email'

echo
echo "== Cross-check security docs for claimed mitigation =="
rg -n -C2 'beforeSend|Sentry active|CPF|email|PII' docs/security/THREAT-MODEL.md docs/observability/SLOS.md 2>/dev/null || true

Repository: EmiyaKiritsugu3/PWeb_Project

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Read the exact code in question =="
head -n 50 src/utils/supabase/server.ts | tail -n 20

echo
echo "== Locate Sentry init with beforeSend/beforeBreadcrumb =="
rg -n -C5 'Sentry\.init|beforeSend|beforeBreadcrumb' --type=ts

echo
echo "== Search for all setUser calls with email =="
rg -n -C3 'setUser.*email|email.*user' --type=ts

echo
echo "== Check for sanitization or scrubbing logic =="
rg -n -C3 'sanitize|scrub|PII|email.*scrub|strip.*email' --type=ts

echo
echo "== List Sentry-related configuration files =="
fd -e ts -e tsx -e js -e json 'sentry|instrumentation' 2>/dev/null || true

Repository: EmiyaKiritsugu3/PWeb_Project

Length of output: 10240


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Full sentry.server.config.ts =="
cat sentry.server.config.ts

echo
echo "== Check if client config has beforeSend =="
cat src/instrumentation-client.ts

echo
echo "== Check edge config =="
cat sentry.edge.config.ts

Repository: EmiyaKiritsugu3/PWeb_Project

Length of output: 3830


Add event.user scrubbing to Sentry's beforeSend hook to prevent PII leakage.

The code at lines 40-43 of src/utils/supabase/server.ts (and identically in src/components/providers/auth-provider.tsx) sends user.email to Sentry via Sentry.setUser(). While sentry.server.config.ts includes a beforeSend hook with an 'email' entry in sensitiveKeys and a scrub() function, the scrubbing is only applied to event.breadcrumbs and event.extra—not to event.user. Email is exposed in the user context on both server and client paths.

Add this to the beforeSend hook in sentry.server.config.ts:

    if (event.extra) {
      event.extra = scrub(event.extra) as Record<string, any>;
    }

+   if (event.user) {
+     event.user = scrub(event.user) as Record<string, any>;
+   }

    return event;

The client config (src/instrumentation-client.ts) has no beforeSend hook; add one there as well to sanitize user context before transmission.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/supabase/server.ts` around lines 40 - 43, Sentry is leaking
user.email because event.user isn't scrubbed; update the Sentry initialization's
beforeSend hook to scrub event.user in addition to event.breadcrumbs and
event.extra by reusing the existing sensitiveKeys and scrub() logic and
removing/redacting any PII in event.user.email (and other user fields) before
returning the event; specifically modify the beforeSend hook used by the server
Sentry init (the function that references sensitiveKeys and scrub()) to process
event.user, and add an equivalent beforeSend hook to the client Sentry init (the
instrumentation-client initialization) so that Sentry.setUser calls do not
transmit raw PII from either server or client.

@EmiyaKiritsugu3 EmiyaKiritsugu3 merged commit a394c1b into main Apr 11, 2026
7 of 8 checks passed
@EmiyaKiritsugu3 EmiyaKiritsugu3 deleted the fix/telemetry-and-e2e-stability branch April 11, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant