Skip to content

Phase 9: User Settings UI on Curated Catalog#74

Merged
kl3inIT merged 50 commits into
mainfrom
gsd/phase-09-user-settings-ui-on-curated-catalog
May 31, 2026
Merged

Phase 9: User Settings UI on Curated Catalog#74
kl3inIT merged 50 commits into
mainfrom
gsd/phase-09-user-settings-ui-on-curated-catalog

Conversation

@kl3inIT
Copy link
Copy Markdown
Owner

@kl3inIT kl3inIT commented May 31, 2026

Summary

Phase 9: User Settings UI on Curated Catalog (v1.2)

Goal: A user can open /ai and configure their writing voice, assistant behavior, sender safety net, and per-feature AI provider/model on a single settings surface — with the AI tab pulling exclusively from the admin-curated catalog and BYOK only for the four user-allowed providers (OpenAI / Anthropic / Google / DeepSeek).

Status: Phase marked complete 2026-05-29 (7/7 plans). ⚠️ No automated VERIFICATION.md on record — shipped per explicit operator request; 09-VALIDATION.md + Playwright golden path + Phase9ArchitectureTest stand in as evidence.

Phase 9 delivers the Inbox Zero–style /ai settings surface: flat SectionHeader groups (Your voice, Behavior, Updates, Safety net, AI Provider), SettingCard + Dialog edit pattern, a knowledge-snippet table with UNIQUE(tenant_id, title), a LOW/MEDIUM/HIGH draft-confidence enum, sender safety-net add/remove with a triage audit "blocked by safety net" badge, and a single BYOK card enforcing the Save → Test → Pick → Activate lifecycle through the shared ProviderConnectionTester. The final commit on this branch folds in post-completion cleanups: removing the tone preset, merging assistant_memory into assistant_knowledge, and a frontend chat-streaming regression fix.

Changes

Plan 09-01: Database foundation

Liquibase changesets 094–097 (assistant settings columns, knowledge unique-title, safety-net pattern kind + audit badge, user BYOK key table) + JPA scaffolding + Wave-0 test stubs.

Plan 09-02: Voice + Behavior + Knowledge backend

Voice/behavior settings endpoints, tenant-scoped knowledge-snippet CRUD, shared sanitizer + knowledge write-site ArchUnit rules, draft runtime reads for auto-draft/signature/sensitive-data toggles.

Plan 09-03: Safety Net + audit badge

Safety-net DOMAIN pattern opt-in, tenant-scoped DELETE authority, matcher returning matched patterns, triage audit API exposing blockedBySafetyNetPattern.

Plan 09-04: BYOK + cost

Shared ProviderConnectionTester (admin MKEY + user BYOK), UserByokService + ByokProviderResolver, /api/byok endpoints, /api/settings/ai/cost 7-day tenant cost endpoint.

Plan 09-05: Generate-from-Sent (SET-VOICE-07)

POST /api/settings/voice/generate-from-sent preview, Gmail Sent reader with quoted-reply stripping + aggregate prompt cap, in-memory privacy invariant + Spring AI observation hardening, leak/rate-limit tests.

Plan 09-06: Frontend settings surface

Flat five-section /ai shell; Voice/Behavior/Knowledge/Safety-net/BYOK features deriving types from generated schema.d.ts; OpenAPI regen after legacy /api/llm/byok deletion; audit safety-net badge in table + card views.

Plan 09-07: Validation closeout

Playwright ai-settings.spec.ts golden path, aggregate Phase9ArchitectureTest, manual UX checkpoint, 09-VALIDATION.md + roadmap closeout.

Post-completion cleanups (this branch's final commit)

  • Tone preset removed (TonePresetDialog deleted; voice DTO/command/result trimmed)
  • assistant_memory merged into assistant_knowledge (changeset 106); AssistantMemoryEntity/Service/Repository + tests deleted; one shared store
  • Frontend chat streaming fix: StreamingTextResponse + AssistantTextNormalizer, resolves one-block streaming regression
  • Legacy dashboard route + DashboardPageClient/PauseBanner removed
  • react-doctor skill/workflow/config tooling added

Requirements Addressed

19 requirements: SET-VOICE-01..06, SET-BEHV-01..05, SET-SAFE-01..04, SET-AI-01..04 (SET-SAFE-02/03 deferred to v1.3 per spec-phase scope decision).

Verification

  • Automated VERIFICATION.md: not on record — phase closed via 09-VALIDATION.md instead
  • Playwright golden path apps/web/e2e/ai-settings.spec.ts
  • Phase9ArchitectureTest aggregate ArchUnit guard
  • Voice-generation privacy leak + rate-limit tests
  • Manual reviewer: confirm /ai golden path + BYOK Save→Test→Pick→Activate in a browser before merge

Key Decisions

  • SensitiveDataProtectionDecider lives in core.llm.usecases (not llm.redaction) so chat settings avoid depending on a non-exported impl package.
  • Triage reads draft settings through TriageDraftSettings to avoid a triage → chat Modulith cycle.
  • SafetyNetMatcher lives in core.triage.usecases (shared via TriageOrchestratorService), not a worker-only class.
  • BYOK lives on /ai (single-tenant-per-user); per-feature picker + tenant-wide mode card removed in favor of one BYOK card with an Active switch.
  • BYOK user key storage reuses the existing single-blob RefreshTokenCipher AES-GCM envelope.

🤖 Generated with Claude Code

kl3inIT added 30 commits May 27, 2026 00:35
- Move model probing into the Spring AI gateway package and delegate admin master-key probes through ProviderConnectionTester

- Add per-provider probe result model collection with Anthropic x-api-key plus anthropic-version handling

- Add BYOK base URL SSRF validation, pinned probe transport, provider allow-list, and Redis-backed rate limiter
Add UserByokService and ByokProviderResolver for stored-row BYOK lifecycle.

Replace legacy tenant BYOK resolver in gateway and chat factory.

Rewrite legacy /api/llm/byok controller as 410 Gone shim and fill BYOK lifecycle tests.
Add /api/byok controller, DTOs, stored-row test connection, activation and model endpoints.

Add /api/settings/ai/cost backed by tenant-wide seven-day llm_call_audit SUM.

Fill BYOK controller, SSRF, plaintext, enum-only, sentinel, and cost tests.
…tings-ui-on-curated-catalog

# Conflicts:
#	apps/web/features/ai/components/AiConfigPage.tsx
#	apps/web/lib/api/schema.d.ts
kl3inIT and others added 15 commits May 30, 2026 22:21
…tings-ui-on-curated-catalog

# Conflicts:
#	apps/web/lib/api/schema.d.ts
#	apps/web/openapi/openapi.json
#	backend/api/src/test/java/com/zeromail/api/controllers/llm/ByokControllerIntegrationTest.java
#	backend/core/src/main/java/com/zeromail/core/thread/usecases/ClassifyThreadReplyStatusService.java
#	backend/core/src/test/java/com/zeromail/core/thread/ClassifyThreadReplyStatusServiceIntegrationTest.java
…/Tools

- Replace MAIL_NAV/MANAGE_NAV with DAILY_NAV/AUTOMATION_NAV/TOOLS_NAV
- Place Rules and AI config together in Automation group
- Swap nav.sectionMail/sectionManage for sectionDaily/sectionAutomation/sectionTools

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Rebuild vi.json/en.json with sectionDaily/sectionAutomation/sectionTools
- Drop orphaned sectionMail/sectionManage (additive generator leaves stale keys)
- typecheck, lint, i18n:check all green

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ro-mail.*

- SecurityConfig @value session.cookie.secure + admin.webauthn.* placeholders -> zero-mail.*
- Loadtest + 5 e2e-stub @ConditionalOnProperty keys -> zero-mail.*
- TriagePendingReaperBatch + WaitlistService @value placeholders -> zero-mail.*
- application.yml: fold standalone zeromail: session block into zero-mail:
- application-loadtest.yml + application-e2e-stub.yml: keys + single zero-mail: top-level mapping
- env var NAME ZEROMAIL_SESSION_COOKIE_SECURE unchanged (relaxed binding maps it)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dant yml autoconfigure exclude, and no-op prod format_sql

- base ymls: remove provider/base-url/4 models/timeouts under llm.platform (all equal LlmProperties record defaults; only api-key is deployment-supplied)
- base ymls: remove spring.autoconfigure.exclude block (GenAI embedding exclusion already on @SpringBootApplication excludeName, timing-safe per Boot #26858)
- prod profiles: remove no-op spring.jpa format_sql:false (base already sets it); annotate retained privacy observations block as intentional defense-in-depth

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (low-fan-out subtrees)

- Extract CryptoProperties, GmailProperties, BillingProperties, AdminProperties from
  ZeroMailCoreProperties into per-feature config packages, each its own
  @ConfigurationProperties(prefix=zero-mail.<feature>); bound keys unchanged
- Preserve @validated + @DefaultValue + compact-constructor defaults + masked toString()
  (crypto KEK, LemonSqueezy secret, admin audit HMAC KEK)
- Rewire consumers to inject the specific record instead of the god-object
- ZeroMailCoreProperties now holds only the llm subtree (deleted in Task 3b)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rties and delete god-object

- New @ConfigurationProperties(prefix=zero-mail.llm) LlmProperties holding relocated
  PlatformProperties (was ZeroMailLlmProperties) + ByokProperties (was ZeroMailLlmByokProperties)
- Masked toString() over platform api-key
- Update all .llm() consumers + ~15 nested-import sites across main, test, and aiEval source set
- DELETE ZeroMailCoreProperties.java (god-object fully decomposed)
- bound keys unchanged (zero-mail.llm.platform.*, zero-mail.llm.byok.*)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ule config records

- ZeroMailApiProperties -> ApiProperties (prefix zero-mail.api unchanged)
- ZeroMailWorkerProperties -> WorkerProperties (prefix zero-mail.worker unchanged)
- ZeroMailLlmDriftProperties -> DriftProperties (prefix zero-mail.llm.drift unchanged)
- ZeroMailChatProperties -> ChatProperties (prefix zero-mail.chat unchanged)
- rename test ZeroMailApiPropertiesTest -> ApiPropertiesTest; update all reference sites
- no @ConfigurationProperties prefix changed; only Java type names

Note: chat controller/orchestrator/history/tokenizer/factory carried concurrent
parallel-agent streaming edits in the working tree at stage time (same race as
STATE.md Phase 1.2 P05 / 01.2.1 P04); included here since the rename is interleaved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ia zero-mail-shared.yml

- New backend/core zero-mail-shared.yml holds the correctness-locked block:
  spring.ai.model.*=none (8 modalities) + chat[.client].observations.log-prompt/completion=false
- Both base ymls import it via spring.config.import: optional:classpath:zero-mail-shared.yml
  (imported-file values WIN, locking the privacy invariant) and drop the duplicated block
- api keeps spring.ai.openai...include-completion; worker keeps spring.ai.retry.max-attempts
- CONVENTIONS.md #9: document the centralization bend for correctness-locked shared boilerplate

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ts ciphers

The god-object split placed CryptoProperties in an invented top-level
package core.crypto.config holding a single 1-field record, orphaned from
the actual crypto code. Relocate it to com.zeromail.core.shared.crypto —
beside PlatformSecretCipher/Hashing and the RefreshTokenCryptoConfig that
consumes it. Bound key zero-mail.crypto.refresh-token-key-base64 unchanged;
masked toString() + @validated preserved. Empty core.crypto package removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…+ state

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tings

- /settings now uses /ai-style SectionHeader + SettingCard (no tabs): Account,
  Display (inline language + theme segmented), Gmail connection, Danger zone.
- Removed pseudo-tab nav strip, credit card, triage pause card, provider card,
  and the privacy card (all duplicated elsewhere).
- Consolidated daily-digest into /ai Updates (ported send-hour Select); deleted
  NotificationsSection + its test.
- Deleted /settings/privacy route + orphaned features/privacy; removed its
  AppSidebar nav item + privacy-page.spec.ts; cleaned check-i18n allowlist.
- SettingCard.title made optional (backward-compatible) so single-card sections
  don't repeat the section header label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…erge

Phase 9 follow-ups on the /ai settings surface plus adjacent cleanups:

- Voice settings: tone-preset removed (TonePresetDialog deleted), DTO/command/result trimmed
- Merge assistant_memory into assistant_knowledge (changeset 106); delete
  AssistantMemoryEntity/Service/Repository + tests; SearchMemoriesToolHandler
  and knowledge service now share one store
- Frontend chat streaming: add StreamingTextResponse component + AssistantTextNormalizer,
  fix one-block streaming regression
- Remove legacy dashboard route + DashboardPageClient/PauseBanner
- Add react-doctor skill/workflow/config tooling
- Regenerate OpenAPI schema + lockfile updates

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Important

Review skipped

Too many files!

This PR contains 276 files, which is 126 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b8b8d87-aaff-4290-926c-160f86ba9367

📥 Commits

Reviewing files that changed from the base of the PR and between 8153c1f and a81fdb1.

⛔ Files ignored due to path filters (24)
  • .ci-backend-gradle.log is excluded by !**/*.log
  • .ci-frontend-web.log is excluded by !**/*.log
  • .ci-playwright.log is excluded by !**/*.log
  • .planning/REQUIREMENTS.md is excluded by !.planning/**
  • .planning/ROADMAP.md is excluded by !.planning/**
  • .planning/STATE.md is excluded by !.planning/**
  • .planning/debug/frontend-streaming-one-block.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-01-SUMMARY.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-02-SUMMARY.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-03-SUMMARY.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-04-SUMMARY.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-05-SUMMARY.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-06-SUMMARY.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-07-SUMMARY.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-REVIEW.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-UAT.md is excluded by !.planning/**
  • .planning/phases/09-user-settings-ui-on-curated-catalog/09-VALIDATION.md is excluded by !.planning/**
  • .planning/quick/260530-vmp-reorganize-sidebar-nav-into-3-frequency-/260530-vmp-PLAN.md is excluded by !.planning/**
  • .planning/quick/260530-vmp-reorganize-sidebar-nav-into-3-frequency-/260530-vmp-SUMMARY.md is excluded by !.planning/**
  • .planning/quick/260530-w9t-redesign-backend-config-properties-to-20/260530-w9t-PLAN.md is excluded by !.planning/**
  • .planning/quick/260530-w9t-redesign-backend-config-properties-to-20/260530-w9t-RESEARCH.md is excluded by !.planning/**
  • .planning/quick/260530-w9t-redesign-backend-config-properties-to-20/260530-w9t-SUMMARY.md is excluded by !.planning/**
  • .planning/quick/260531-lpx-flatten-settings-page/260531-lpx-PLAN.md is excluded by !.planning/**
  • .planning/quick/260531-lpx-flatten-settings-page/260531-lpx-SUMMARY.md is excluded by !.planning/**
📒 Files selected for processing (276)
  • .claude/skills/react-doctor/SKILL.md
  • .github/workflows/react-doctor.yml
  • AGENTS.md
  • CLAUDE.md
  • CONVENTIONS.md
  • apps/web/__tests__/byok-key-handling.test.ts
  • apps/web/__tests__/chat/streaming-text-response.test.tsx
  • apps/web/__tests__/chat/tool-catalog-contract.test.ts
  • apps/web/__tests__/chat/tool-results.test.tsx
  • apps/web/app/(protected)/(app)/dashboard/loading.tsx
  • apps/web/app/(protected)/(app)/dashboard/page.tsx
  • apps/web/app/(protected)/(app)/settings/SettingsClient.tsx
  • apps/web/app/(protected)/(app)/settings/privacy/page.tsx
  • apps/web/components/shell/AppShell.tsx
  • apps/web/components/shell/AppSidebar.tsx
  • apps/web/e2e/ai-settings.spec.ts
  • apps/web/e2e/byok.spec.ts
  • apps/web/e2e/chat/stream-happy-path.spec.ts
  • apps/web/e2e/chrome-test-utils.ts
  • apps/web/e2e/launch-golden-path.spec.ts
  • apps/web/e2e/privacy-page.spec.ts
  • apps/web/e2e/rules-examples.spec.ts
  • apps/web/features/ai/AiProviderSection.test.tsx
  • apps/web/features/ai/api/ai-settings-api.ts
  • apps/web/features/ai/api/byok-api.ts
  • apps/web/features/ai/components/AiConfigPage.tsx
  • apps/web/features/ai/components/AiOutputLanguageDialog.tsx
  • apps/web/features/ai/components/AiProviderSection.tsx
  • apps/web/features/ai/components/BehaviorSection.tsx
  • apps/web/features/ai/components/ConfirmDialog.tsx
  • apps/web/features/ai/components/DraftConfidenceDialog.tsx
  • apps/web/features/ai/components/EmailSignatureDialog.tsx
  • apps/web/features/ai/components/PersonalInstructionsDialog.tsx
  • apps/web/features/ai/components/SafetyNetSection.tsx
  • apps/web/features/ai/components/SectionHeader.tsx
  • apps/web/features/ai/components/SettingCard.tsx
  • apps/web/features/ai/components/UpdatesSection.tsx
  • apps/web/features/ai/components/WritingStyleDialog.tsx
  • apps/web/features/ai/components/YourVoiceSection.tsx
  • apps/web/features/ai/hooks/useActivateByok.ts
  • apps/web/features/ai/hooks/useAiCost.ts
  • apps/web/features/ai/hooks/useBehaviorSettings.ts
  • apps/web/features/ai/hooks/useByok.ts
  • apps/web/features/ai/hooks/useDeleteByok.ts
  • apps/web/features/ai/hooks/useGenerateVoiceFromSent.ts
  • apps/web/features/ai/hooks/useSaveByok.ts
  • apps/web/features/ai/hooks/useSelectByokModel.ts
  • apps/web/features/ai/hooks/useTestByokConnection.ts
  • apps/web/features/ai/hooks/useUpdateBehaviorSettings.ts
  • apps/web/features/ai/hooks/useUpdateVoiceSettings.ts
  • apps/web/features/ai/hooks/useVoiceSettings.ts
  • apps/web/features/ai/messages.ts
  • apps/web/features/ai/query-keys.ts
  • apps/web/features/chat/components/conversation-pane.tsx
  • apps/web/features/chat/components/preview-card/body/save-memory-body.tsx
  • apps/web/features/chat/components/preview-card/preview-card-state.ts
  • apps/web/features/chat/components/preview-card/preview-card.tsx
  • apps/web/features/chat/components/streaming-text-response.tsx
  • apps/web/features/chat/components/tool-results.tsx
  • apps/web/features/chat/hooks/use-chat.ts
  • apps/web/features/chat/hooks/use-confirm-action.ts
  • apps/web/features/chat/messages.ts
  • apps/web/features/dashboard/components/DashboardPageClient.tsx
  • apps/web/features/dashboard/messages.ts
  • apps/web/features/knowledge/api/knowledge-api.ts
  • apps/web/features/knowledge/components/KnowledgeDialog.tsx
  • apps/web/features/knowledge/components/KnowledgeRow.tsx
  • apps/web/features/knowledge/components/KnowledgeTable.test.tsx
  • apps/web/features/knowledge/components/KnowledgeTable.tsx
  • apps/web/features/knowledge/hooks/useCreateKnowledge.ts
  • apps/web/features/knowledge/hooks/useDeleteKnowledge.ts
  • apps/web/features/knowledge/hooks/useKnowledge.ts
  • apps/web/features/knowledge/hooks/useUpdateKnowledge.ts
  • apps/web/features/knowledge/query-keys.ts
  • apps/web/features/llm/api/llm-api.ts
  • apps/web/features/llm/components/ByokForm.test.tsx
  • apps/web/features/llm/components/ByokForm.tsx
  • apps/web/features/llm/hooks/use-byok.ts
  • apps/web/features/llm/query-keys.ts
  • apps/web/features/notifications/__tests__/NotificationsSection.test.tsx
  • apps/web/features/notifications/components/NotificationsSection.tsx
  • apps/web/features/privacy/components/PrivacySections.tsx
  • apps/web/features/privacy/messages.ts
  • apps/web/features/shell/messages.ts
  • apps/web/features/triage/__tests__/AuditSafetyNetBadge.test.tsx
  • apps/web/features/triage/api/triage-api.ts
  • apps/web/features/triage/components/AuditCardList.tsx
  • apps/web/features/triage/components/AuditRow.tsx
  • apps/web/features/triage/components/AuditSafetyNetBadge.tsx
  • apps/web/features/triage/components/PauseBanner.test.tsx
  • apps/web/features/triage/components/PauseBanner.tsx
  • apps/web/features/triage/components/SenderRow.tsx
  • apps/web/features/triage/components/SenderSafetyNetList.test.tsx
  • apps/web/features/triage/components/SenderSafetyNetList.tsx
  • apps/web/features/triage/hooks/useDeleteProtectedSender.ts
  • apps/web/features/triage/hooks/useOptInSender.ts
  • apps/web/i18n/messages/en.json
  • apps/web/i18n/messages/vi.json
  • apps/web/lib/api/errors.ts
  • apps/web/lib/api/schema.d.ts
  • apps/web/openapi/openapi.json
  • apps/web/package.json
  • apps/web/scripts/check-i18n.ts
  • apps/web/vitest.config.ts
  • backend/api/src/main/java/com/zeromail/api/admin/AdminBootstrapRunner.java
  • backend/api/src/main/java/com/zeromail/api/config/ApiProperties.java
  • backend/api/src/main/java/com/zeromail/api/config/GlobalExceptionHandler.java
  • backend/api/src/main/java/com/zeromail/api/config/SpringAiObservationProperties.java
  • backend/api/src/main/java/com/zeromail/api/controllers/admin/AdminCatalogController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/admin/AdminMasterKeyController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/admin/AdminSpendController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/byok/UserByokController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/chat/ChatController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/llm/ByokController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/settings/KnowledgeSnippetController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/settings/SettingsAiCostController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/settings/SettingsBehaviorController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/settings/SettingsVoiceController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/settings/VoiceGenerateController.java
  • backend/api/src/main/java/com/zeromail/api/controllers/triage/SenderSafetyNetController.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/cat/CatalogListResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/cat/FeatureDefaultMatrixResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/cat/SetFeatureDefaultTierRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/AddProviderKeyRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/CreateProviderRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/CreateProviderResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/MasterKeySetRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/ProviderKeyListResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/ProviderKeyResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/RotateMasterKeyRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/RotationResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/SetFeatureDefaultRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/TestConnectionRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/admin/mkey/TestConnectionResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/byok/ByokActivateRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/byok/ByokModelRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/byok/ByokResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/byok/ByokSaveRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/byok/ByokTestConnectionResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/byok/package-info.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/AiCostResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/BehaviorSettingsResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/BehaviorSettingsUpdateRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/GenerateFromSentRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/GenerateFromSentResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/KnowledgeSnippetListResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/KnowledgeSnippetRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/KnowledgeSnippetResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/VoiceSettingsResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/settings/VoiceSettingsUpdateRequest.java
  • backend/api/src/main/java/com/zeromail/api/dto/triage/AuditEntryResponse.java
  • backend/api/src/main/java/com/zeromail/api/dto/triage/ProtectedSendersResponse.java
  • backend/api/src/main/java/com/zeromail/api/e2estub/E2eStubAuthConfig.java
  • backend/api/src/main/java/com/zeromail/api/e2estub/E2eStubChatModel.java
  • backend/api/src/main/java/com/zeromail/api/e2estub/E2eStubGmailApiClientFactory.java
  • backend/api/src/main/java/com/zeromail/api/e2estub/E2eStubPubsubVerifierConfig.java
  • backend/api/src/main/java/com/zeromail/api/e2estub/E2eStubResetController.java
  • backend/api/src/main/java/com/zeromail/api/loadtest/LoadtestPubsubVerifierConfig.java
  • backend/api/src/main/java/com/zeromail/api/security/CorsConfig.java
  • backend/api/src/main/java/com/zeromail/api/security/GoogleOAuthSuccessHandler.java
  • backend/api/src/main/java/com/zeromail/api/security/LemonSqueezyWebhookSignatureFilter.java
  • backend/api/src/main/java/com/zeromail/api/security/LoginRedirectAuthenticationFailureHandler.java
  • backend/api/src/main/java/com/zeromail/api/security/PubSubSecurityConfig.java
  • backend/api/src/main/java/com/zeromail/api/security/SecurityConfig.java
  • backend/api/src/main/java/com/zeromail/api/websocket/WebSocketConfig.java
  • backend/api/src/main/resources/application-e2e-stub.yml
  • backend/api/src/main/resources/application-loadtest.yml
  • backend/api/src/main/resources/application-prod.yml
  • backend/api/src/main/resources/application.yml
  • backend/api/src/test/java/com/zeromail/api/config/ApiPropertiesTest.java
  • backend/api/src/test/java/com/zeromail/api/config/SpringAiObservationDisabledTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/byok/ByokActivateGateModelMissingTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/byok/ByokActivateGateNotTestedTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/byok/ByokControllerApiTestSupport.java
  • backend/api/src/test/java/com/zeromail/api/controllers/byok/ByokResponseNeverEchoesPlaintextTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/byok/ByokSaveBaseUrlValidationTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/byok/ByokSaveProviderAllowListTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/byok/ByokSaveSsrfRejectionTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/byok/ByokTestConnectionEnumOnlyTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/chat/ChatControllerStreamIT.java
  • backend/api/src/test/java/com/zeromail/api/controllers/llm/ByokControllerIntegrationTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/settings/KnowledgeSnippetControllerTenantIsolationTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/settings/SettingsAiCostControllerTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/settings/SettingsVoiceControllerTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/settings/SettingsVoiceLanguageValidationTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/triage/AuditEntryResponseSafetyNetFieldTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/triage/SenderSafetyNetDeleteAuthorityTest.java
  • backend/api/src/test/java/com/zeromail/api/controllers/triage/SenderSafetyNetDomainPatternTest.java
  • backend/api/src/test/java/com/zeromail/api/e2estub/E2eStubGmailApiClientFactoryTest.java
  • backend/api/src/test/java/com/zeromail/api/security/GoogleOAuthSuccessHandlerTest.java
  • backend/api/src/test/java/com/zeromail/api/security/LoginRedirectAuthenticationFailureHandlerTest.java
  • backend/api/src/test/java/com/zeromail/api/security/TestSessionSupport.java
  • backend/core/src/aiEval/java/com/zeromail/core/aiEval/DraftTokenBudgetEvalTest.java
  • backend/core/src/main/java/com/zeromail/core/admin/audit/usecases/AdminAuditHmacSecretProvider.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/domain/event/CatalogChangedEvent.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/persistence/FeatureDefaultProviderEntity.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/persistence/ModelCatalogEntity.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/persistence/ModelCatalogRepository.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/persistence/ProviderCatalogEntity.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/persistence/lowlevel/CatalogSyncJobRepository.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/persistence/lowlevel/ModelCatalogWriteRepository.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/persistence/lowlevel/ProviderCatalogLookupRepository.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/persistence/lowlevel/ProviderCatalogWriteRepository.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/projection/CatalogSyncJob.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/projection/ResolvedRoute.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/usecases/CatalogAdminService.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/usecases/CatalogSyncJobConsumer.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/usecases/CatalogSyncOrchestrator.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/usecases/FeatureDefaultProviderService.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/usecases/FeatureDefaultTierService.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/usecases/LlmRouter.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/usecases/ModelSchemaValidator.java
  • backend/core/src/main/java/com/zeromail/core/admin/cat/usecases/ModelVerificationService.java
  • backend/core/src/main/java/com/zeromail/core/admin/config/AdminProperties.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/domain/event/MasterKeyRotatedEvent.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/exception/MasterKeyTestFailedException.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/exception/MissingMasterKeyRowException.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/exception/ProviderKeyReorderMismatchException.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/persistence/KeyFormatAttributeConverter.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/persistence/LlmProviderAttributeConverter.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/persistence/LlmProviderMasterKeyEntity.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/persistence/LlmProviderMasterKeyId.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/persistence/LlmProviderMasterKeyRepository.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/persistence/lowlevel/LlmProviderMasterKeyWriteRepository.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/projection/MasterKeyDependentsCount.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/projection/MasterKeyMaskedRow.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/usecases/MasterKeyAdminService.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/usecases/MasterKeyEditSessionService.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/usecases/MasterKeyMasker.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/usecases/ProviderMasterKeyResolver.java
  • backend/core/src/main/java/com/zeromail/core/admin/mkey/usecases/ProviderMasterKeyRouteCredentialResolver.java
  • backend/core/src/main/java/com/zeromail/core/admin/package-info.java
  • backend/core/src/main/java/com/zeromail/core/admin/spend/persistence/lowlevel/SpendAggregateReadRepository.java
  • backend/core/src/main/java/com/zeromail/core/admin/spend/projection/SpendQuery.java
  • backend/core/src/main/java/com/zeromail/core/admin/spend/usecases/SpendAggregateQueryService.java
  • backend/core/src/main/java/com/zeromail/core/admin/tenant/usecases/TenantDeletionRegistry.java
  • backend/core/src/main/java/com/zeromail/core/billing/config/BillingProperties.java
  • backend/core/src/main/java/com/zeromail/core/billing/usecases/BillingPlanQueryService.java
  • backend/core/src/main/java/com/zeromail/core/billing/usecases/LemonSqueezyCheckoutClient.java
  • backend/core/src/main/java/com/zeromail/core/chat/domain/ChatToolName.java
  • backend/core/src/main/java/com/zeromail/core/chat/exception/KnowledgeSnippetException.java
  • backend/core/src/main/java/com/zeromail/core/chat/exception/SettingsValidationException.java
  • backend/core/src/main/java/com/zeromail/core/chat/llm/VercelProtocolEmitter.java
  • backend/core/src/main/java/com/zeromail/core/chat/llm/springai/SpringAiChatModelFactory.java
  • backend/core/src/main/java/com/zeromail/core/chat/package-info.java
  • backend/core/src/main/java/com/zeromail/core/chat/persistence/AssistantKnowledgeMemoryEntity.java
  • backend/core/src/main/java/com/zeromail/core/chat/persistence/AssistantKnowledgeMemoryJpaRepository.java
  • backend/core/src/main/java/com/zeromail/core/chat/persistence/AssistantMemoryEntity.java
  • backend/core/src/main/java/com/zeromail/core/chat/persistence/AssistantMemoryJpaRepository.java
  • backend/core/src/main/java/com/zeromail/core/chat/persistence/AssistantSettingsEntity.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/AssistantKnowledgeService.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/AssistantMemoryService.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/AssistantPersonalInstructionsService.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/AssistantTextNormalizer.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/ChatHistoryService.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/ChatOrchestrator.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/ChatProperties.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/ChatToolCatalog.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/LlmTokenizer.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/AssistantDraftSettingsService.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/DraftConfidenceThresholdResolver.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/GmailSentMessagesReader.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/QuotedReplyStripper.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/SettingsBehaviorCommand.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/SettingsBehaviorResult.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/SettingsBehaviorService.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/SettingsVoiceCommand.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/SettingsVoiceResult.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/SettingsVoiceService.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/VoiceGenerationCommand.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/VoiceGenerationPrompt.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/VoiceGenerationResult.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/VoiceGenerationService.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/settings/package-info.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/tools/ConfirmRequiredToolHandlers.java
  • backend/core/src/main/java/com/zeromail/core/chat/usecases/tools/SearchMemoriesToolHandler.java

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gsd/phase-09-user-settings-ui-on-curated-catalog

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 31, 2026

React Doctor

React Doctor found 66 issues in 15 files.

Score Issues Errors Warnings Affected Files Scope
92 / 100 (Great) 66 0 66 15 426 files changed on gsd/phase-09-user-settings-ui-on-curated-catalog vs. main

Top Findings

Rule Severity Category Count
react-doctor/only-export-components warning Architecture 27
react-doctor/no-multi-comp warning Architecture 19
react-doctor/no-derived-useState warning State & Effects 5
react-doctor/js-hoist-intl warning Performance 4
react-doctor/design-no-redundant-padding-axes warning Architecture 2

View workflow run

Generated by React Doctor. Questions? Contact founders@million.dev.

Phase 9 cleanup (tone-preset removal, dashboard deletion, memory merge) removed
tested surface and dropped function coverage to 22.28%, below the 25% gate.
Align functions with the other three metrics already at 20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
}

@Transactional(readOnly = true)
public boolean autoDraftRepliesEnabled(UUID tenantId) {

@Transactional(readOnly = true)
public Optional<ResolvedLlmProviderCredential> resolve(
UUID tenantId, String ignoredFallbackModel) {

@Transactional(readOnly = true)
public Optional<ResolvedLlmProviderCredential> resolveForChat(
UUID tenantId, String ignoredRequestedModel) {

@FunctionalInterface
interface PinnedResolutionObserver {
void observe(String host, InetAddress resolvedAddress);

@FunctionalInterface
interface PinnedResolutionObserver {
void observe(String host, InetAddress resolvedAddress);
if (statusParts.length < 2) {
throw new IOException("Malformed HTTP status line");
}
int statusCode = Integer.parseInt(statusParts[1]);
kl3inIT and others added 3 commits May 31, 2026 16:29
Two CI gate failures on backend/api:test:
- Spring Modulith verify(): controllers depended on non-exposed dto.byok
  types (plan 09-04 added the BYOK DTOs without a NamedInterface). Add
  package-info @NamedInterface("byok") mirroring the other dto.* packages.
- SpringAiObservationDisabledTest read worker application.yml literally, but
  commit f3789ba centralized the prompt/completion suppression keys into
  zero-mail-shared.yml. Point the static-config assertion at the shared file
  (imported by both api and worker), preserving the privacy invariant guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…aries

Phase 9 BYOK reused admin master-key types from core.llm, which violated the
deliberate 'core.llm must not import core.admin' boundary (LlmGatewayBoundaryTest,
108x) and created an admin<->llm module cycle (ThreadModuleScenarioTest). Both
were latent: this branch had never hit CI.

- Move LlmProvider, KeyFormat, MasterKeyTestResult from core.admin.mkey into
  core.llm.domain (the foundational layer admin adapts to); rewrite ~80 imports.
  admin now depends downward on llm.domain (allowed direction).
- Expose llm.config (RestClientConfig reads LlmProperties) and llm.byok
  (chat SpringAiChatModelFactory + VoiceGenerationService use ByokProviderResolver
  / ByokRateLimiter) as NamedInterfaces; whitelist them in the consuming modules'
  allowedDependencies (+ billing::domain for chat VoiceGenerationService).
- ApplicationYmlLlmConfigTest: assert the centralized observation-suppression
  block in zero-mail-shared.yml (completing the f3789ba refactor), matching the
  api SpringAiObservationDisabledTest fix.

Full backend core+api suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kl3inIT kl3inIT merged commit 937a401 into main May 31, 2026
17 of 19 checks passed
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.

2 participants