chore(tooling): pre-commit, component/a11y tests, bundle budget, dead-code + gated error tracking#623
chore(tooling): pre-commit, component/a11y tests, bundle budget, dead-code + gated error tracking#623BigSimmo wants to merge 4 commits into
Conversation
…dead-code + error tracking Adds eight developer-productivity tools, each inert / advisory / baseline- calibrated so none can fail CI on pre-existing state: - husky + lint-staged: pre-commit prettier --write + eslint --fix (prepare guarded with `husky || true` so `npm ci --omit=dev` stays safe). - .editorconfig + .github/CODEOWNERS: editor consistency and review routing on high-risk paths (supabase, env, RAG/retrieval, governance, workflows). - jsdom + @testing-library/react: interactive component tier as a SEPARATE vitest project (tests/**/*.dom.test.tsx); the existing node suite is unchanged. - @axe-core/playwright: automated WCAG scans in ui-accessibility.spec.ts (advisory lane; zero critical/serious violations at baseline). - size-limit: 1.3 MB client-JS budget wired into the CI build job and verify:release (current 1.18 MB). - knip: advisory dead-code / unused-dependency detection (not wired into any gate; false positives suppressed in knip.json). - Sentry (@sentry/nextjs): gated same-origin tunnel (/api/monitoring, DSN-match validated), onRequestError + error-boundary capture, and a PII scrubber that reuses logger.ts SENSITIVE_KEY. Fully no-op until a DSN is set; the SDK is tree-shaken out of the client bundle via a DefinePlugin __SENTRY_ENABLED__ literal (typeof-guarded for Turbopack dev), so the CSP is untouched. Verified: 214 test files / 1960 tests pass (both vitest projects, 0 failures), typecheck/lint/format clean, production build exit 0 with bundle 1.18 MB < budget, axe 5/5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
📝 WalkthroughWalkthroughAdds optional Sentry browser observability with privacy scrubbing and a validated monitoring tunnel, introduces jsdom-based Vitest projects and UI tests, and adds repository editor, ownership, linting, static-analysis, bundle-size, and release-validation controls. ChangesSentry observability
Test infrastructure and UI validation
Repository controls and validation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant InstrumentationClient
participant MonitoringRoute
participant SentryIngest
Browser->>InstrumentationClient: capture exception or router transition
InstrumentationClient->>MonitoringRoute: POST Sentry envelope
MonitoringRoute->>SentryIngest: validate DSN and forward envelope
SentryIngest-->>MonitoringRoute: return upstream response
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 12cf134988
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| delete event.request.query_string; | ||
| delete event.request.data; |
There was a problem hiding this comment.
Strip query strings from Sentry request URLs
When an error is captured on a route whose URL contains user text (this repo already accepts q/query search params on clinical flows such as the differentials pages), deleting request.query_string is not enough because Sentry's request.url commonly contains the full URL, and the current scrubber leaves it untouched. That can send raw clinical queries or auth callback parameters to Sentry despite this privacy gate; scrub event.request.url to path-only or remove its search component, and consider redacting referer the same way.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Summary
- Added URL path-only scrubbing for Sentry request URLs, so query strings and hash fragments are removed before events leave the app. src/lib/observability/sentry-scrub.tsL19-L27 src/lib/observability/sentry-scrub.tsL67-L77
- Added request-header scrubbing for both
refererandreferrer, preserving existing sensitive-key redaction while stripping URL query strings from those headers. src/lib/observability/sentry-scrub.tsL29-L42 - Added focused test coverage proving clinical query text is removed from
request.url,referer, andreferrer. tests/sentry-observability.test.tsL26-L40
Commit / PR
- Committed changes on the current branch with commit
d30a609(fix(observability): scrub sentry request urls). - Created PR record:
fix(observability): scrub Sentry request URL query strings.
Testing
⚠️ npm run test -- --run tests/sentry-observability.test.ts— initial run could not start becausenode_modules/vitest/vitest.mjswas missing under the default Node 20 environment.- ✅
npm run test -- --run tests/sentry-observability.test.ts - ✅
npm run typecheck - ✅
npm run lint
|
@copilot resolve the merge conflicts on this branch. |
Head branch was pushed to by a user without write access
|
@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. After fixing or dispositioning a thread, reply in that thread with as the first line, followed by a concise summary; that marker authorizes the workflow to close that exact thread. If human input or new authorization is required, do not use the marker and leave the thread open with the blocker. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
… instrumentation.ts, env.ts, a11y spec Conflict resolution summary: - .github/workflows/ci.yml: keep main's check:bundle-budget (advisory warn-only gate) - package.json scripts: keep main's postinstall/githooks system; drop husky prepare - package.json deps: keep both @sentry/nextjs (client) and @sentry/node (server) - src/instrumentation.ts: adopt main's typed onRequestError + @sentry/node init; remove the superseded initSentryServer() helper (replaced by inline init in register()) - src/lib/env.ts: use main's z.preprocess SENTRY_DSN; retain NEXT_PUBLIC_SENTRY_* vars used by instrumentation-client.ts - tests/ui-accessibility.spec.ts: adopt main's testInfo-attached axe helper; remove the standalone axe test.describe (axe scans are now in the media smoke suite) - .githooks/pre-commit: migrate lint-staged hook from .husky to .githooks so both pre-commit (lint-staged) and pre-push (guard-push) live under core.hooksPath - .husky/pre-commit: removed (superseded by .githooks/pre-commit) - package-lock.json: regenerated via npm install to include @sentry/nextjs
Head branch was pushed to by a user without write access
Resolved in commit
Verified: lint ✅, typecheck ✅, 2273 unit tests ✅ |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
tests/setup/jsdom.setup.ts (1)
12-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDedupe the matchMedia/scrollIntoView stub and fix the one-time
scrollIntoViewguard. The browser-API stub is copy-pasted between the two files, and thescrollIntoViewpolyfill'sif (!Element.prototype.scrollIntoView)guard means it's assigned only once as a plainvi.fn(). In Vitest 4,vi.restoreAllMocks()only restores spies created withvi.spyOn, so this mock's call history is never reset between tests — a latent isolation gap for any future test that asserts onscrollIntoViewcalls.
tests/setup/jsdom.setup.ts#L12-L40: ExportinstallMatchMediaStubfor reuse, and replace the guarded assignment withvi.spyOn(Element.prototype, "scrollIntoView").mockImplementation(() => {})(or an explicitmockClear()) inbeforeEachso it resets consistently every test.tests/accessible-table.dom.test.tsx#L11-L26: Import and reuse the shared stub fromtests/setup/jsdom.setup.tsinstead of re-declaringsetMatchMedialocally.♻️ Proposed fix
--- a/tests/setup/jsdom.setup.ts +++ b/tests/setup/jsdom.setup.ts @@ -function installMatchMediaStub(matches = false) { +export function installMatchMediaStub(matches = false) { Object.defineProperty(window, "matchMedia", { @@ beforeEach(() => { installMatchMediaStub(false); - // jsdom does not implement scrollIntoView; components call it on focus/expand. - if (!Element.prototype.scrollIntoView) { - Element.prototype.scrollIntoView = vi.fn(); - } + // jsdom does not implement scrollIntoView; components call it on focus/expand. + // Re-spy every test so history/implementation resets under Vitest 4's + // restoreAllMocks (which only restores vi.spyOn spies, not plain vi.fn()s). + if (!vi.isMockFunction(Element.prototype.scrollIntoView)) { + Element.prototype.scrollIntoView = () => {}; + } + vi.spyOn(Element.prototype, "scrollIntoView").mockImplementation(() => {}); });--- a/tests/accessible-table.dom.test.tsx +++ b/tests/accessible-table.dom.test.tsx @@ -import { render, screen } from "`@testing-library/react`"; -import userEvent from "`@testing-library/user-event`"; -import { describe, expect, it, vi } from "vitest"; -import { AccessibleTable } from "`@/components/AccessibleTable`"; - -function setMatchMedia(matches: boolean) { - Object.defineProperty(window, "matchMedia", { - writable: true, - configurable: true, - value: vi.fn().mockImplementation((query: string) => ({ - matches, - media: query, - onchange: null, - addEventListener: vi.fn(), - removeEventListener: vi.fn(), - addListener: vi.fn(), - removeListener: vi.fn(), - dispatchEvent: vi.fn(), - })), - }); -} +import { render, screen } from "`@testing-library/react`"; +import userEvent from "`@testing-library/user-event`"; +import { describe, expect, it } from "vitest"; +import { AccessibleTable } from "`@/components/AccessibleTable`"; +import { installMatchMediaStub as setMatchMedia } from "./setup/jsdom.setup";Based on learnings, this relies on the coding guideline for
**/*.{js,jsx,ts,tsx,mjs,cjs}: "Automatic reviews may emit at most three inline findings, and inline comments are limited to P0/P1 issues; P2 context belongs in one summary."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/setup/jsdom.setup.ts` around lines 12 - 40, Deduplicate the browser stubs by exporting installMatchMediaStub from tests/setup/jsdom.setup.ts and updating its beforeEach setup to spy on Element.prototype.scrollIntoView with a resettable mock on every test instead of the one-time guarded assignment. In tests/setup/jsdom.setup.ts#L12-L40, make the shared stub and per-test scrollIntoView reset change; in tests/accessible-table.dom.test.tsx#L11-L26, remove the local setMatchMedia declaration and import and reuse installMatchMediaStub, with no other behavior changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 197-203: Resolve the conflict in the bundle-size CI step by
removing all conflict markers and retaining the intended bundle budget checks,
including both check:bundle-size and check:bundle-budget if both are required.
Confirm and preserve their intended gating or warn-only behavior so the
resulting workflow is valid YAML.
In @.husky/pre-commit:
- Line 1: Update the pre-commit hook command to invoke the project’s locally
installed lint-staged binary directly instead of using npx. Preserve the
existing lint-staged arguments and ensure the hook fails when the local
dependency is unavailable.
In @.lintstagedrc.json:
- Around line 2-3: Update the second formatting glob in the lint-staged
configuration to include scss alongside css, ensuring staged SCSS files are
processed by Prettier while preserving the existing JSON, Markdown, YAML, and
CSS coverage.
In `@src/app/api/monitoring/route.ts`:
- Around line 61-65: Update the upstream Sentry relay fetch in the monitoring
route to enforce a finite timeout, using an AbortSignal or equivalent request
cancellation mechanism and the existing timeout conventions if available. Ensure
the request is aborted when the timeout expires while preserving the current
POST payload and headers.
In `@src/instrumentation.ts`:
- Around line 1-34: Resolve all remaining merge conflicts by removing every
conflict marker and retaining the intended combined implementation. In
src/instrumentation.ts lines 1-34 and 91-119, merge the Sentry initialization
with the Next.js instrumentation flow; cleanly merge the conflicting environment
definitions in src/lib/env.ts lines 19-39; and resolve both
dependency/configuration conflicts in package.json lines 13-21 and 148-152,
ensuring each file remains valid and builds successfully.
In `@src/lib/observability/sentry-scrub.ts`:
- Around line 1-69: Update scrubSentryEvent to remove event.breadcrumbs along
with the other high-risk request and identity fields, ensuring client-side
console, fetch, and XHR breadcrumb data cannot be sent to Sentry. Do not alter
the existing redaction and truncation behavior.
In `@tests/ui-accessibility.spec.ts`:
- Around line 2-6: Resolve all remaining merge conflicts in
tests/ui-accessibility.spec.ts: reconcile the Playwright import to include the
required types, retain a single expectNoBlockingAxeViolations implementation,
and update every caller in lines 78-148 to pass the TestInfo value required by
that helper’s attachment contract.
---
Nitpick comments:
In `@tests/setup/jsdom.setup.ts`:
- Around line 12-40: Deduplicate the browser stubs by exporting
installMatchMediaStub from tests/setup/jsdom.setup.ts and updating its
beforeEach setup to spy on Element.prototype.scrollIntoView with a resettable
mock on every test instead of the one-time guarded assignment. In
tests/setup/jsdom.setup.ts#L12-L40, make the shared stub and per-test
scrollIntoView reset change; in tests/accessible-table.dom.test.tsx#L11-L26,
remove the local setMatchMedia declaration and import and reuse
installMatchMediaStub, with no other behavior changes.
🪄 Autofix (Beta)
✅ Autofix completed
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f4c31514-083e-4010-b071-daafdfcad7c2
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (26)
.editorconfig.github/CODEOWNERS.github/workflows/ci.yml.husky/pre-commit.lintstagedrc.json.size-limit.jsondocs/site-map.mdknip.jsonnext.config.tspackage.jsonscripts/check-client-bundle-secrets.mjssrc/app/api/monitoring/route.tssrc/app/global-error.tsxsrc/components/route-error-boundary.tsxsrc/instrumentation-client.tssrc/instrumentation.tssrc/lib/env.tssrc/lib/logger.tssrc/lib/observability/sentry-client.tssrc/lib/observability/sentry-scrub.tstests/accessible-table.dom.test.tsxtests/sentry-observability.test.tstests/setup/jsdom.setup.tstests/ui-accessibility.spec.tstests/use-event-callback.dom.test.tsxvitest.config.mts
| <<<<<<< HEAD | ||
| - name: Client bundle size budget | ||
| run: npm run check:bundle-size | ||
| ======= | ||
| - name: Bundle budget (warn-only until a baseline is enforced) | ||
| run: npm run check:bundle-budget | ||
| >>>>>>> origin/main |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Resolve the merge conflict before merging.
The literal conflict markers make .github/workflows/ci.yml invalid YAML, so the CI workflow cannot load. Remove the markers and retain the intended bundle-budget steps—likely both checks—after confirming their desired gating behavior.
Proposed fix
-<<<<<<< HEAD
- name: Client bundle size budget
run: npm run check:bundle-size
-=======
- name: Bundle budget (warn-only until a baseline is enforced)
run: npm run check:bundle-budget
->>>>>>> origin/main📝 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.
| <<<<<<< HEAD | |
| - name: Client bundle size budget | |
| run: npm run check:bundle-size | |
| ======= | |
| - name: Bundle budget (warn-only until a baseline is enforced) | |
| run: npm run check:bundle-budget | |
| >>>>>>> origin/main | |
| - name: Client bundle size budget | |
| run: npm run check:bundle-size | |
| - name: Bundle budget (warn-only until a baseline is enforced) | |
| run: npm run check:bundle-budget |
🧰 Tools
🪛 actionlint (1.7.12)
[error] 197-197: could not parse as YAML: could not find expected ':'
(syntax-check)
🪛 YAMLlint (1.37.1)
[error] 198-198: syntax error: could not find expected ':'
(syntax)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ci.yml around lines 197 - 203, Resolve the conflict in the
bundle-size CI step by removing all conflict markers and retaining the intended
bundle budget checks, including both check:bundle-size and check:bundle-budget
if both are required. Confirm and preserve their intended gating or warn-only
behavior so the resulting workflow is valid YAML.
Source: Linters/SAST tools
| @@ -0,0 +1 @@ | |||
| npx lint-staged | |||
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n[.husky/pre-commit]\n'
cat -n .husky/pre-commit
printf '\n[package.json packageManager/deps]\n'
python3 - <<'PY'
import json
from pathlib import Path
p = Path('package.json')
if p.exists():
data = json.loads(p.read_text())
for k in ('packageManager', 'devDependencies', 'dependencies'):
if k in data:
print(f'\n{k}:')
v = data[k]
if isinstance(v, dict):
for name in sorted(v):
if name == 'lint-staged' or k == 'packageManager':
print(f' {name}: {v[name]}')
else:
print(v)
else:
print('package.json not found')
PY
printf '\n[npx help/version]\n'
npx --version || true
npm --version || trueRepository: BigSimmo/Database
Length of output: 879
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n[repo root files of interest]\n'
git ls-files .husky/pre-commit package.json pnpm-lock.yaml package-lock.json yarn.lock npm-shrinkwrap.json .npmrc .node-version .nvmrc | cat
printf '\n[package.json excerpt]\n'
cat -n package.json | sed -n '1,120p'
printf '\n[husky hook]\n'
cat -n .husky/pre-commitRepository: BigSimmo/Database
Length of output: 10682
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n[npm/npx help]\n'
npm exec --help | sed -n '1,120p' || true
printf '\n'
npx --help | sed -n '1,120p' || true
printf '\n[lint-staged in lockfile/package]\n'
rg -n '"lint-staged"|lint-staged' package.json package-lock.json | sed -n '1,120p'Repository: BigSimmo/Database
Length of output: 2867
🌐 Web query:
npm 11 npx --no-install deprecated alias npm exec local remote package install fallback documentation
💡 Result:
In npm 11, the --no-install option is deprecated and will be converted to --no [1][2]. The npx binary itself is a wrapper that uses the npm exec command, and this deprecation is part of the ongoing evolution of npx functionality within the npm CLI [1][3]. Key details regarding this deprecation and related functionality include: 1. --no-install Deprecation: This option is deprecated and will be converted to --no [1][4]. This change aligns with the broader removal of older, less advisable npx features [1]. 2. Shell Fallback: Shell fallback functionality has been removed, as it is considered insecure and not advisable [1][2]. 3. npx vs npm exec: The npx binary is now maintained as part of the npm CLI [1][5]. While npx provides some backward compatibility, npm exec is the primary command for executing local or remote packages [3][4]. When using npm exec, the use of a double-hyphen (--) is recommended to explicitly stop npm from parsing command-line options and ensure they are passed to the executed command instead [3][4]. 4. Package Installation: npm exec (and the npx wrapper) will attempt to install missing packages by default to execute them, unless configured otherwise or when restricted by specific CLI options [1][3]. The --no-install flag (now deprecated) was previously used to prevent this behavior [5][4]. For current and future use, it is recommended to use the npm exec command directly or the npx binary as intended by modern npm versions, while transitioning away from deprecated flags like --no-install [1][3].
Citations:
- 1: https://docs.npmjs.com/cli/v11/commands/npx/
- 2: https://docs.npmjs.com/cli/commands/npx/
- 3: https://cdn.jsdelivr.net/npm/npm@11.6.1/docs/output/commands/npm-exec.html
- 4: https://unpkg.com/npm@11.1.0/docs/output/commands/npm-exec.html
- 5: https://github.com/npm/npx
🌐 Web query:
npm exec --no npx --no-install docs npm 11 prevent install missing package
💡 Result:
In npm 11, the --no-install option is deprecated and converted to --no [1][2]. This flag does not explicitly exist to "prevent install of a missing package" in the way that it disables the download feature; rather, npm exec is designed to execute packages that are either locally available or fetched remotely [2][3]. If you are attempting to prevent npm from automatically downloading a package that is not present in your local dependencies, there is no direct --no-install flag that serves this purpose [1][2]. Key behaviors to note: 1. Deprecation: The --no-install option has been deprecated for several versions and is automatically converted to --no [4][5][6]. 2. Execution Context: npm exec will attempt to use locally installed packages first. If the requested package is not found locally, it will attempt to fetch it remotely [2][3]. 3. Prompts: To prevent unexpected installations, npm provides prompts before installing missing packages. You can suppress these prompts using --yes or --no, but these flags control the interactive prompt itself rather than disabling the installation mechanism entirely [4][5][6]. If you want to ensure that only locally available binaries are executed, you should ensure the dependencies are already present in your project, or check that your environment constraints (such as running in a non-TTY environment or a CI setting) influence how npm handles missing packages [5][2]. In CI environments or when standard input is not a TTY, npm often defaults to --yes (auto-installing) [5]. To avoid unintended remote fetches, you should rely on local installation via npm install prior to execution [2].
Citations:
- 1: https://docs.npmjs.com/cli/npm-exec/
- 2: https://unpkg.com/npm@11.1.0/docs/output/commands/npm-exec.html
- 3: https://docs.npmjs.com/cli/v11/commands/npx/
- 4: https://docs.npmjs.com/cli/v8/commands/npm-exec/
- 5: https://docs.npmjs.com/cli/v9/commands/npm-exec/
- 6: https://docs.npmjs.com/cli/v7/commands/npm-exec/
Avoid npx in the hook. .husky/pre-commit:1 should call the local lint-staged binary directly so the hook fails closed if dependencies are missing; npx can still resolve a remote package, and --no-install is deprecated in npm 11.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.husky/pre-commit at line 1, Update the pre-commit hook command to invoke
the project’s locally installed lint-staged binary directly instead of using
npx. Preserve the existing lint-staged arguments and ensure the hook fails when
the local dependency is unavailable.
| "*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": ["prettier --write --ignore-unknown", "eslint --fix"], | ||
| "*.{json,md,yml,yaml,css}": ["prettier --write --ignore-unknown"] |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Include SCSS in the formatting rule.
The staged-file formatter covers CSS but not SCSS, so SCSS changes can bypass the pre-commit formatting check. Add scss to the second glob.
As per coding guidelines, the formatter scope must cover **/*.{js,jsx,ts,tsx,css,scss}.
Suggested fix
- "*.{json,md,yml,yaml,css}": ["prettier --write --ignore-unknown"]
+ "*.{json,md,yml,yaml,css,scss}": ["prettier --write --ignore-unknown"]📝 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.
| "*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": ["prettier --write --ignore-unknown", "eslint --fix"], | |
| "*.{json,md,yml,yaml,css}": ["prettier --write --ignore-unknown"] | |
| "*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": ["prettier --write --ignore-unknown", "eslint --fix"], | |
| "*.{json,md,yml,yaml,css,scss}": ["prettier --write --ignore-unknown"] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.lintstagedrc.json around lines 2 - 3, Update the second formatting glob in
the lint-staged configuration to include scss alongside css, ensuring staged
SCSS files are processed by Prettier while preserving the existing JSON,
Markdown, YAML, and CSS coverage.
Source: Coding guidelines
| const upstream = await fetch(`https://${expected.host}/api/${expected.projectId}/envelope/`, { | ||
| method: "POST", | ||
| body: envelope, | ||
| headers: { "Content-Type": "application/x-sentry-envelope" }, | ||
| }); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
Add a timeout to the upstream Sentry relay fetch.
This fetch to Sentry's ingest host has no timeout. If the upstream host is slow or unreachable, this request-thread call can hang indefinitely, tying up server capacity on every client error report relayed through this tunnel.
⏱️ Proposed fix: bound the upstream request with a timeout
const upstream = await fetch(`https://${expected.host}/api/${expected.projectId}/envelope/`, {
method: "POST",
body: envelope,
headers: { "Content-Type": "application/x-sentry-envelope" },
+ signal: AbortSignal.timeout(5000),
});📝 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.
| const upstream = await fetch(`https://${expected.host}/api/${expected.projectId}/envelope/`, { | |
| method: "POST", | |
| body: envelope, | |
| headers: { "Content-Type": "application/x-sentry-envelope" }, | |
| }); | |
| const upstream = await fetch(`https://${expected.host}/api/${expected.projectId}/envelope/`, { | |
| method: "POST", | |
| body: envelope, | |
| headers: { "Content-Type": "application/x-sentry-envelope" }, | |
| signal: AbortSignal.timeout(5000), | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/api/monitoring/route.ts` around lines 61 - 65, Update the upstream
Sentry relay fetch in the monitoring route to enforce a finite timeout, using an
AbortSignal or equivalent request cancellation mechanism and the existing
timeout conventions if available. Ensure the request is aborted when the timeout
expires while preserving the current POST payload and headers.
| <<<<<<< HEAD | ||
| // Gated server-side Sentry init. SENTRY_DSN is unset by default, so this is a | ||
| // no-op (and never imports the SDK) until Sentry is configured. Runs for both the | ||
| // Node.js and Edge runtimes so server + proxy errors are captured. The SDK is | ||
| // dynamically imported so a deployment without a DSN never loads it. | ||
| async function initSentryServer() { | ||
| const dsn = process.env.SENTRY_DSN; | ||
| if (!dsn) return; | ||
| const [Sentry, { scrubSentryErrorEvent }] = await Promise.all([ | ||
| import("@sentry/nextjs"), | ||
| import("@/lib/observability/sentry-scrub"), | ||
| ]); | ||
| Sentry.init({ | ||
| dsn, | ||
| environment: process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV, | ||
| tracesSampleRate: Number(process.env.SENTRY_TRACES_SAMPLE_RATE ?? 0) || 0, | ||
| sendDefaultPii: false, | ||
| beforeSend: scrubSentryErrorEvent, | ||
| }); | ||
| } | ||
| ======= | ||
| import type { Instrumentation } from "next"; | ||
| >>>>>>> origin/main | ||
|
|
||
| // Next.js calls register() once when a server instance starts, before it serves | ||
| // any requests. We use it to fail fast: a clinical production server must be fully | ||
| // and correctly configured rather than silently degrading — or, worse, serving | ||
| // unauthenticated demo content — on the first request. See production-readiness | ||
| // plan items 0.1 and 0.3. | ||
| export async function register() { | ||
| // Wire error tracking first so a misconfiguration thrown by the gates below is | ||
| // itself reportable. No-op unless SENTRY_DSN is set. | ||
| await initSentryServer(); | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
set -euo pipefail
printf '\n== conflict markers ==\n'
rg -n '^(<<<<<<<|=======|>>>>>>>)' src/instrumentation.ts src/lib/env.ts package.json || true
printf '\n== instrumentation excerpt ==\n'
cat -n src/instrumentation.ts | sed -n '1,160p'
printf '\n== env excerpt ==\n'
cat -n src/lib/env.ts | sed -n '1,80p'
printf '\n== package.json scripts/deps excerpts ==\n'
cat -n package.json | sed -n '1,40p'
printf '\n-- deps slice --\n'
cat -n package.json | sed -n '140,170p'Repository: BigSimmo/Database
Length of output: 16769
Resolve the merge conflicts in src/instrumentation.ts, src/lib/env.ts, and package.json Literal <<<<<<< / ======= / >>>>>>> markers are still present, so the branch won’t parse or build until these files are merged cleanly.
🧰 Tools
🪛 Biome (2.5.3)
[error] 1-6: Expected a statement but instead found '<<<<<<< HEAD
// Gated server-side Sentry init. SENTRY_DSN is unset by default, so this is a
// no-op (and never imports the SDK) until Sentry is configured. Runs for both the
// Node.js and Edge runtimes so server + proxy errors are captured. The SDK is
// dynamically imported so a deployment without a DSN never loads it.
async'.
(parse)
[error] 9-9: await is only allowed within async functions and at the top levels of modules.
(parse)
[error] 21-21: Expected a statement but instead found '======='.
(parse)
[error] 23-23: Expected a statement but instead found '>>>>>>> origin/main'.
(parse)
📍 Affects 3 files
src/instrumentation.ts#L1-L34(this comment)src/instrumentation.ts#L91-L119src/lib/env.ts#L19-L39package.json#L13-L21package.json#L148-L152
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/instrumentation.ts` around lines 1 - 34, Resolve all remaining merge
conflicts by removing every conflict marker and retaining the intended combined
implementation. In src/instrumentation.ts lines 1-34 and 91-119, merge the
Sentry initialization with the Next.js instrumentation flow; cleanly merge the
conflicting environment definitions in src/lib/env.ts lines 19-39; and resolve
both dependency/configuration conflicts in package.json lines 13-21 and 148-152,
ensuring each file remains valid and builds successfully.
| // Privacy scrubber for Sentry events (client + server). This app handles clinical | ||
| // queries/answers and user emails; NONE of that may leave the app in an error | ||
| // report. The scrubber runs as Sentry's `beforeSend`/`beforeSendTransaction` hook | ||
| // and enforces the same PII contract the structured logger uses (SENSITIVE_KEY, | ||
| // imported from src/lib/logger.ts as the single source of truth): | ||
| // - strips request cookies, headers, query string and body outright; | ||
| // - drops user identifiers; | ||
| // - redacts any extra/context/tag value whose KEY looks sensitive; | ||
| // - truncates long strings so a stray query embedded in an error message is bounded. | ||
| // It is intentionally conservative: over-redaction only loses debugging detail, | ||
| // under-redaction leaks patient data. | ||
| import type { ErrorEvent, Event } from "@sentry/nextjs"; | ||
| import { SENSITIVE_KEY } from "@/lib/logger"; | ||
|
|
||
| const REDACTED = "[redacted]"; | ||
| const MAX_STRING_LENGTH = 512; | ||
| const MAX_DEPTH = 6; | ||
|
|
||
| function redactByKey(value: unknown, depth = 0): unknown { | ||
| if (value === null || value === undefined) return value; | ||
| if (typeof value === "string") { | ||
| return value.length > MAX_STRING_LENGTH ? `${value.slice(0, MAX_STRING_LENGTH)}…[truncated]` : value; | ||
| } | ||
| if (typeof value === "number" || typeof value === "boolean") return value; | ||
| if (depth >= MAX_DEPTH) return "[depth-limited]"; | ||
| if (Array.isArray(value)) return value.slice(0, 50).map((item) => redactByKey(item, depth + 1)); | ||
| if (typeof value === "object") { | ||
| const out: Record<string, unknown> = {}; | ||
| for (const [key, val] of Object.entries(value as Record<string, unknown>)) { | ||
| out[key] = SENSITIVE_KEY.test(key) ? REDACTED : redactByKey(val, depth + 1); | ||
| } | ||
| return out; | ||
| } | ||
| return String(value); | ||
| } | ||
|
|
||
| function truncate(value: string | undefined): string | undefined { | ||
| if (typeof value !== "string") return value; | ||
| return value.length > MAX_STRING_LENGTH ? `${value.slice(0, MAX_STRING_LENGTH)}…[truncated]` : value; | ||
| } | ||
|
|
||
| export function scrubSentryEvent<T extends Event>(event: T): T { | ||
| // Request context is the highest-risk surface: cookies/auth headers are secrets, | ||
| // and query strings / bodies can carry the clinical query. Remove them wholesale. | ||
| if (event.request) { | ||
| delete event.request.cookies; | ||
| delete event.request.query_string; | ||
| delete event.request.data; | ||
| if (event.request.headers) { | ||
| event.request.headers = redactByKey(event.request.headers) as Record<string, string>; | ||
| } | ||
| } | ||
|
|
||
| // No user identity (email/id/ip) — the request-hash correlation id is enough. | ||
| delete event.user; | ||
|
|
||
| if (event.extra) event.extra = redactByKey(event.extra) as Record<string, unknown>; | ||
| if (event.contexts) event.contexts = redactByKey(event.contexts) as typeof event.contexts; | ||
| if (event.tags) event.tags = redactByKey(event.tags) as typeof event.tags; | ||
|
|
||
| // Defensively bound exception/message text so a query echoed into an Error | ||
| // message cannot arrive verbatim. | ||
| if (event.message) event.message = truncate(event.message) as string; | ||
| for (const exception of event.exception?.values ?? []) { | ||
| if (exception.value) exception.value = truncate(exception.value); | ||
| } | ||
|
|
||
| return event; | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== Files related to Sentry ==\n'
git ls-files | rg '(^|/)(sentry|instrumentation).*\\.(ts|tsx|js|jsx|mjs|cjs)$|sentry.*\\.(ts|tsx|js|jsx|mjs|cjs)$'
printf '\n== Search for breadcrumbs / beforeBreadcrumb / beforeSend ==\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
'beforeBreadcrumb|breadcrumbs|beforeSendTransaction|beforeSend\\s*:' src .
printf '\n== Map candidate files ==\n'
for f in src/instrumentation.ts src/instrumentation-client.ts src/lib/observability/sentry-scrub.ts; do
if [ -f "$f" ]; then
echo "--- $f"
ast-grep outline "$f" --view expanded || true
fi
doneRepository: BigSimmo/Database
Length of output: 186
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for f in src/instrumentation.ts src/instrumentation-client.ts src/lib/observability/sentry-scrub.ts; do
if [ -f "$f" ]; then
echo "===== $f ====="
nl -ba "$f" | sed -n '1,240p'
echo
fi
doneRepository: BigSimmo/Database
Length of output: 230
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '== Relevant tracked files ==\n'
git ls-files | rg '(^|/)(instrumentation|sentry).*\\.(ts|tsx|js|jsx|mjs|cjs)$|sentry-scrub\\.ts$' || true
printf '\n== Candidate paths in src ==\n'
fd -a 'instrumentation.*|sentry-scrub\\.ts' src 2>/dev/null || true
printf '\n== Read candidate files if present ==\n'
for f in src/instrumentation.ts src/instrumentation-client.ts src/lib/observability/sentry-scrub.ts; do
if [ -f "$f" ]; then
echo "--- $f"
sed -n '1,220p' "$f" | cat -n
fi
doneRepository: BigSimmo/Database
Length of output: 13724
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for f in src/lib/observability/sentry-client.ts src/lib/observability/error-capture.ts src/lib/observability/sentry-scrub.ts src/instrumentation-client.ts; do
if [ -f "$f" ]; then
echo "===== $f ====="
sed -n '1,240p' "$f" | cat -n
echo
fi
done
printf '\n== Breadcrumb-related search in observability ==\n'
rg -n 'beforeBreadcrumb|breadcrumbs|breadcrumb' src/lib/observability src/instrumentation-client.ts src/instrumentation.tsRepository: BigSimmo/Database
Length of output: 11449
Drop breadcrumbs in the client Sentry path src/lib/observability/sentry-scrub.ts still leaves event.breadcrumbs intact, so the browser init can ship console/fetch/XHR breadcrumbs with clinical query text or URLs. Strip breadcrumbs here, or disable them in src/instrumentation-client.ts as well.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/observability/sentry-scrub.ts` around lines 1 - 69, Update
scrubSentryEvent to remove event.breadcrumbs along with the other high-risk
request and identity fields, ensuring client-side console, fetch, and XHR
breadcrumb data cannot be sent to Sentry. Do not alter the existing redaction
and truncation behavior.
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Fixes Applied SuccessfullyFixed 4 file(s) based on 6 unresolved review comments. Files modified:
Commit: The changes have been pushed to the Time taken: |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.githooks/pre-commit:
- Line 24: Update the lint-staged invocation in the pre-commit hook to use npx
--no (or npm exec --no), ensuring it only runs the locally installed lint-staged
binary and fails when unavailable instead of installing from the registry.
In `@src/lib/env.ts`:
- Line 31: Update the NEXT_PUBLIC_SENTRY_DSN schema in envSchema to normalize an
empty string to undefined before URL validation, matching the existing
SENTRY_DSN behavior. Preserve valid URLs and the optional unset behavior so
envSchema.parse(process.env) accepts blank public DSNs.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d1e1ad16-6a57-467b-a586-576ae3029b47
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
.githooks/pre-commit.githooks/pre-pushpackage.jsonsrc/lib/env.ts
💤 Files with no reviewable changes (1)
- package.json
| exit 0 | ||
| fi | ||
|
|
||
| npx lint-staged |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- .githooks/pre-commit ---\n'
cat -n .githooks/pre-commit
printf '\n--- package.json ---\n'
cat -n package.json
printf '\n--- lockfiles / package manager hints ---\n'
git ls-files | rg '(^|/)(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb|npm-shrinkwrap\.json)$|(^|/)\.npmrc$|(^|/)\.yarnrc(\.yml)?$'Repository: BigSimmo/Database
Length of output: 246
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- .githooks/pre-commit ---'
cat -n .githooks/pre-commit
echo
echo '--- package.json ---'
cat -n package.json
echo
echo '--- lockfiles / package manager hints ---'
git ls-files | rg '(^|/)(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb|npm-shrinkwrap\.json)$|(^|/)\.npmrc$|(^|/)\.yarnrc(\.yml)?$'Repository: BigSimmo/Database
Length of output: 15456
🌐 Web query:
npm 11 npx --no-install npm exec --no-install local binary missing install from registry behavior
💡 Result:
In npm 11 (as well as all versions since npm v7), the behavior of npx and npm exec has been standardized around the npm exec command [1][2][3]. Regarding your specific query: 1. Deprecation of --no-install: The --no-install option is deprecated and is automatically converted to --no [1][2][4]. 2. Behavior with --no: When you use --no (or the deprecated --no-install), npm is explicitly instructed not to attempt to install any missing packages from the registry [2][3][5]. 3. Handling of Local Binaries: - By design, npx and npm exec include locally installed package executables in the PATH of the executed command [1][6][7]. - If a binary is already present in your project's local dependencies (e.g., in node_modules/.bin), it should be found and executed correctly regardless of the --no flag [1][3]. - If the binary is missing from the local dependencies and you use --no, npm exec will fail because it is prohibited from installing the package from the registry to resolve the command [2][5][8]. If you find that a locally available binary is not being detected when using --no, it may be related to specific configuration issues, such as workspace contexts or how the binary is defined in package.json [9]. If the binary is not part of the project's dependency tree, npm cannot execute it without performing an installation, which the --no flag prevents [2][3].
Citations:
- 1: https://docs.npmjs.com/cli/v11/commands/npx/
- 2: https://unpkg.com/npm@11.1.0/docs/output/commands/npm-exec.html
- 3: https://cdn.jsdelivr.net/npm/npm@11.6.1/docs/output/commands/npx.html
- 4: https://docs.npmjs.com/cli/v11/commands/npm-exec/
- 5: https://docs.npmjs.com/cli/v7/commands/npm-exec/
- 6: https://unpkg.com/npm@11.1.0/docs/output/commands/npx.html
- 7: https://manpages.debian.org/unstable/npm/npx.1.en.html
- 8: https://docs.npmjs.com/cli/v9/commands/npm-exec/
- 9: [BUG] npx/npm exec don't work in child workspace when using --no/--no-install and --package npm/cli#3520
Prevent npx from installing lint-staged here. Use npx --no lint-staged (or npm exec --no lint-staged) so a missing local binary fails instead of fetching an unpinned package from the registry.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.githooks/pre-commit at line 24, Update the lint-staged invocation in the
pre-commit hook to use npx --no (or npm exec --no), ensuring it only runs the
locally installed lint-staged binary and fails when unavailable instead of
installing from the registry.
| // tree-shaken out of the client bundle at build time via __SENTRY_ENABLED__ | ||
| // DefinePlugin literal. The public DSN is safe to expose; keep SENTRY_AUTH_TOKEN | ||
| // (source-map upload) out of any NEXT_PUBLIC_* var — it is build-time only. | ||
| NEXT_PUBLIC_SENTRY_DSN: z.string().url().optional(), |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '== file list ==\n'
git ls-files src/lib/env.ts
printf '\n== relevant references ==\n'
rg -n "NEXT_PUBLIC_SENTRY_DSN|SENTRY_DSN|preprocess|blank|string\\.url\\(\\)\\.optional\\(\\)" src/lib/env.ts src -g '!**/node_modules/**' || true
printf '\n== env.ts excerpt ==\n'
nl -ba src/lib/env.ts | sed -n '1,120p'Repository: BigSimmo/Database
Length of output: 6098
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('src/lib/env.ts')
text = p.read_text()
print('contains NEXT_PUBLIC_SENTRY_DSN:', 'NEXT_PUBLIC_SENTRY_DSN' in text)
print('contains SENTRY_DSN blank normalization:', 'trim() === ""' in text or 'empty strings' in text)
PYRepository: BigSimmo/Database
Length of output: 239
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import re, pathlib
text = pathlib.Path('src/lib/env.ts').read_text()
# show the exact schema lines around SENTRY fields
for m in re.finditer(r'SENTRY_DSN|NEXT_PUBLIC_SENTRY_DSN', text):
start = max(0, m.start()-180)
end = min(len(text), m.end()+220)
print('---')
print(text[start:end])
PYRepository: BigSimmo/Database
Length of output: 997
🏁 Script executed:
#!/bin/bash
set -euo pipefail
nl -ba src/lib/env.ts | sed -n '1,120p'Repository: BigSimmo/Database
Length of output: 195
Treat an empty public DSN as unset.
NEXT_PUBLIC_SENTRY_DSN="" is still rejected by z.string().url().optional(), so optional client-side Sentry config can abort envSchema.parse(process.env) during startup. Normalize blank strings to undefined here, matching SENTRY_DSN.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/env.ts` at line 31, Update the NEXT_PUBLIC_SENTRY_DSN schema in
envSchema to normalize an empty string to undefined before URL validation,
matching the existing SENTRY_DSN behavior. Preserve valid URLs and the optional
unset behavior so envSchema.parse(process.env) accepts blank public DSNs.
Fixed 4 file(s) based on 6 unresolved review comments. Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
|
Superseded by #636, which is now merged into This branch was 40 commits stale and its tooling overlapped work The outstanding merge conflicts and review comments here all target code that was intentionally dropped and never merged. Closing to avoid resurrecting the duplicate approach. The one valid nitpick that applies to merged code (jsdom |
Summary
Adds eight developer-productivity tools, each inert / advisory / baseline-calibrated so none can fail CI on pre-existing state:
prettier --write+eslint --fix(prepareguarded withhusky || truesonpm ci --omit=devin the Dockerfile stays safe; the worktreeconfig.worktreeintentionally disables hooks inside worktrees, so this fires for real commits from the main checkout)..editorconfig+.github/CODEOWNERS— editor consistency and review routing on high-risk paths (supabase, env, RAG/retrieval, governance, workflows).tests/**/*.dom.test.tsx); the existing node suite is untouched.tests/ui-accessibility.spec.ts(advisory lane; zero critical/serious violations at baseline).buildjob andverify:release(current 1.18 MB)./api/monitoring, DSN-match validated to prevent open relay),onRequestError+ error-boundary capture, and a PII scrubber reusinglogger.tsSENSITIVE_KEY. Fully no-op until a DSN is set; the SDK is tree-shaken out of the client bundle via a DefinePlugin__SENTRY_ENABLED__literal (typeof-guarded for Turbopack dev), so the CSP is unchanged.Verification
Ran the constituent gates individually (all green) rather than the
verify:pr-localwrapper:lint,typecheck,format:check— cleantest— 214 files / 1960 tests pass, 0 failures (node + new jsdom project)build— exit 0; client bundle 1.18 MB < 1.3 MB budget; client-bundle secret scan passes; Sentry confirmed absent from client chunks when DSN unsettest:e2e:accessibility— 5/5 pass incl. new axe scanscheck:runtime,check:github-actions,sitemap:check(regenerated for the new/api/monitoringroute),brand:check,check:type-scale,check:icon-scaleNo retrieval/ranking/answer-generation behavior changed, so the eval gates are N/A.
Clinical Governance Preflight
Touches privacy + production config (error tracking) and adds one API route, so completing this section:
beforeSendscrubber strips request cookies/query/body, drops user identity, and redacts PII keys before any event leaves the appClinical KB Database(sjrfecxgysukkwxsowpy) — untouchedenv.tsstaysserver-only; only the public Sentry DSN is client-exposed;SENTRY_AUTH_TOKENadded to the client-bundle secret scanner as defense-in-depthNotes
NEXT_PUBLIC_SENTRY_DSN+SENTRY_DSNand rebuilds — no runtime/behavior change and zero client-bundle cost until then.🤖 Generated with Claude Code
Summary by CodeRabbit