System persistence and files#5
Merged
BillChirico merged 1 commit intoauto-claude/002-graceful-shutdown-handlingfrom Feb 4, 2026
Merged
Conversation
…utdown - Add mkdirSync to ensure data directory exists before saving state - Remove accidentally committed local dev files (.auto-claude-security.json, .auto-claude-status, .claude_settings.json) and add to .gitignore - Reorder graceful shutdown to wait for pending requests before saving state to ensure in-flight conversation history is captured
Contributor
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the
✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
3094a9c
into
auto-claude/002-graceful-shutdown-handling
2 checks passed
BillChirico
added a commit
that referenced
this pull request
Feb 4, 2026
* auto-claude: subtask-1-1 - Create data directory structure for state persistence Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * auto-claude: subtask-1-2 - Add state persistence functions (save/load convers * auto-claude: subtask-1-3 - Add pending request tracking mechanism Added pendingRequests Set to track active AI requests during shutdown. The generateResponse function now registers requests on start and removes them on completion (success or error) using try-finally. Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * auto-claude: subtask-1-4 - Implement graceful shutdown orchestration - Add gracefulShutdown function that saves state, waits for pending requests, and destroys Discord client - Handle SIGTERM and SIGINT signals for clean shutdown - Add 5-second timeout for pending requests during shutdown - Add loadState() call on startup to restore previous state - Include detailed logging for shutdown process * fix: correct state file location and shutdown timeout (qa-requested) - Fix state.json path to use data/ directory (critical security fix) - Update shutdown timeout from 5s to 10s per spec Addresses QA review feedback from session 1 Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix: ensure data directory exists, remove local dev files, reorder shutdown (#5) - Add mkdirSync to ensure data directory exists before saving state - Remove accidentally committed local dev files (.auto-claude-security.json, .auto-claude-status, .claude_settings.json) and add to .gitignore - Reorder graceful shutdown to wait for pending requests before saving state to ensure in-flight conversation history is captured Co-authored-by: Cursor Agent <[email protected]> --------- Co-authored-by: Claude Sonnet 4.5 <[email protected]> Co-authored-by: Cursor Agent <[email protected]>
BillChirico
added a commit
that referenced
this pull request
Feb 11, 2026
When both topChannels and suggestedChannels are empty, channelText was an empty string producing broken output like 'Check out — there's always something interesting going on!'. Also handles voice-only activity where getActivityLevel promotes to busy/hype but no text channels exist. Each template now has a graceful fallback when no channel references are available. Resolves Bugbot review threads #1 and #5.
BillChirico
added a commit
that referenced
this pull request
Feb 16, 2026
…reation The health check now performs a lightweight search against the mem0 platform to verify the SDK client actually works, rather than just checking that the client object was created. This properly validates connectivity with the hosted mem0 platform. Also fixes misleading test name that asserted success but was named as a failure case. Resolves review threads #5, #11.
BillChirico
added a commit
that referenced
this pull request
Feb 16, 2026
…reation The health check now performs a lightweight search against the mem0 platform to verify the SDK client actually works, rather than just checking that the client object was created. This properly validates connectivity with the hosted mem0 platform. Also fixes misleading test name that asserted success but was named as a failure case. Resolves review threads #5, #11.
BillChirico
added a commit
that referenced
this pull request
Feb 16, 2026
…reation The health check now performs a lightweight search against the mem0 platform to verify the SDK client actually works, rather than just checking that the client object was created. This properly validates connectivity with the hosted mem0 platform. Also fixes misleading test name that asserted success but was named as a failure case. Resolves review threads #5, #11.
BillChirico
added a commit
that referenced
this pull request
Feb 16, 2026
…uard - Add cursor-based pagination with `after` param to fetchUserGuilds, looping until all guilds are fetched (issue #2) - Propagate RefreshTokenError to session and auto-redirect to sign-in via SessionGuard component in Providers (issue #3) - Add dockerContext = ".." to railway.toml for monorepo Docker builds (issue #4) - Create /api/health returning 200 JSON; update railway.toml healthcheckPath (issue #5) - Conditionally render Add to Server buttons only when CLIENT_ID is set (issue #6) - Add AbortController cleanup to guild fetch useEffect in ServerSelector (issue #7) - Refuse unauthenticated bot API requests when BOT_API_SECRET is missing (issue #9) - Add retry + empty/error states to ServerSelector (issue #13 partial)
BillChirico
added a commit
that referenced
this pull request
Feb 17, 2026
- Differentiate requireGuildAdmin (ADMINISTRATOR only) from requireGuildModerator (ADMINISTRATOR | MANAGE_GUILD), aligning REST admin check with slash-command isAdmin (#1, #2, #12) - Add botOwners startup warning when using default upstream ID (#3) - Add SESSION_SECRET, DISCORD_CLIENT_SECRET, DISCORD_REDIRECT_URI to README deployment table (#4) - Pass actual permission level to getPermissionError so modlog denial says 'moderator' not 'administrator' (#5) - Guard _seedOAuthState with NODE_ENV production check (#6) - Add test: valid JWT with no server-side session (#7) - Add DiscordApiError class with HTTP status (#8) - Add moderatorRoleId support to isModerator (#9) - Remove no-op delete override from SessionStore (#10) - Cap oauthStates at 10k entries (#11) - Fix hasOAuthGuildPermission docstring for bitwise OR semantics (#12) - Handle dashboard URL fragment collision (#13) - Cap guildCache at 10k entries (#14) - Add SESSION_TTL_MS co-location comment with JWT expiry (#15) - Cache SESSION_SECRET via lazy getter in verifyJwt (#16) - Remove PII (username) from OAuth auth log (#17)
BillChirico
added a commit
that referenced
this pull request
Feb 17, 2026
- Differentiate requireGuildAdmin (ADMINISTRATOR only) from requireGuildModerator (ADMINISTRATOR | MANAGE_GUILD), aligning REST admin check with slash-command isAdmin (#1, #2, #12) - Add botOwners startup warning when using default upstream ID (#3) - Add SESSION_SECRET, DISCORD_CLIENT_SECRET, DISCORD_REDIRECT_URI to README deployment table (#4) - Pass actual permission level to getPermissionError so modlog denial says 'moderator' not 'administrator' (#5) - Guard _seedOAuthState with NODE_ENV production check (#6) - Add test: valid JWT with no server-side session (#7) - Add DiscordApiError class with HTTP status (#8) - Add moderatorRoleId support to isModerator (#9) - Remove no-op delete override from SessionStore (#10) - Cap oauthStates at 10k entries (#11) - Fix hasOAuthGuildPermission docstring for bitwise OR semantics (#12) - Handle dashboard URL fragment collision (#13) - Cap guildCache at 10k entries (#14) - Add SESSION_TTL_MS co-location comment with JWT expiry (#15) - Cache SESSION_SECRET via lazy getter in verifyJwt (#16) - Remove PII (username) from OAuth auth log (#17)
BillChirico
added a commit
that referenced
this pull request
Feb 28, 2026
Create proxy routes following the existing bot-api-proxy pattern: - GET /api/guilds/:guildId/members — enriched member list - GET /api/guilds/:guildId/members/:userId — member detail - POST /api/guilds/:guildId/members/:userId/xp — XP adjustment - GET /api/guilds/:guildId/members/:userId/cases — mod case history - GET /api/guilds/:guildId/members/export — CSV export (streamed) All routes include guild admin authorization and proper error handling. CSV export uses 30s timeout and streams the response body. Addresses review comment #5 on PR #119.
BillChirico
added a commit
that referenced
this pull request
Feb 28, 2026
* feat(dashboard): add member table component with sort and pagination * feat(dashboard): add members list page * feat(dashboard): add member detail page with stats and admin actions * feat(members): add member management API with detail, history, XP adjust, and CSV export * feat(members): mount members router, export guild middleware, remove superseded basic members endpoint * test(members): add API endpoint tests (26 tests) * fix: lint import ordering in member dashboard components * fix(members-api): add rate limiting, CSV injection protection, XP transaction safety - CSV injection: escapeCsv now prefixes formula chars (=,+,-,@,\t,\r) with single quote - XP bounds: cap adjustments to ±1,000,000 - XP transaction: wrap upsert + level update in BEGIN/COMMIT/ROLLBACK - Warning filter: add AND action = 'warn' to recent warnings query in member detail - CSV export: paginate guild.members.list() for guilds > 1000 members - Pool safety: wrap getPool() in safeGetPool() with try/catch, return 503 on failure - Search total: return filteredTotal alongside total when search is active Addresses review comments #8-15 on PR #119. * fix(dashboard): align member interfaces with backend API response shape Frontend-backend contract fixes: - MemberRow: id/displayName/avatar/messages_sent/warning_count/joinedAt (was snake_case) - Avatar: use full URL from backend directly, remove hash-based CDN helper - Pagination: cursor → nextAfter, param 'cursor' → 'after' - MemberDetail: flat response shape with stats/reputation/warnings sub-objects - SortColumn: restrict to API-supported values (messages/xp/warnings/joined) - Role color: use hexColor string directly instead of number conversion - XP progress: use next_level_xp from reputation object - CSV export: add error state instead of silent catch - Dependency array: add fetchMembers, remove eslint-disable comment - Keyboard accessibility: tabIndex={0} + onKeyDown for Enter/Space on rows - Guild context: handleRowClick depends on guildId - Search total: display filteredTotal when available Addresses review comments #1-7, #16-19 on PR #119. * feat(dashboard): add Next.js API proxy routes for member endpoints Create proxy routes following the existing bot-api-proxy pattern: - GET /api/guilds/:guildId/members — enriched member list - GET /api/guilds/:guildId/members/:userId — member detail - POST /api/guilds/:guildId/members/:userId/xp — XP adjustment - GET /api/guilds/:guildId/members/:userId/cases — mod case history - GET /api/guilds/:guildId/members/export — CSV export (streamed) All routes include guild admin authorization and proper error handling. CSV export uses 30s timeout and streams the response body. Addresses review comment #5 on PR #119. * test(members): update tests for API changes - XP tests: mock pool.connect() + client for transaction flow - Add XP bounds test (±1,000,000 limit) - Verify BEGIN/COMMIT/release called in transaction - Search test: assert filteredTotal in response * fix: lint and formatting fixes across all changed files - Use template literals instead of string concatenation (biome) - Use const for non-reassigned variables - Add button type='button' for a11y - Remove unused displayTotal variable - Use Number.isNaN over global isNaN - Format proxy routes to match biome standards * fix(members-api): add global + per-route rate limiting to satisfy CodeQL * 📝 Add docstrings to `feat/member-management` Docstrings generation was requested by @BillChirico. The following files were modified: * `src/api/routes/guilds.js` * `src/api/routes/members.js` * `web/src/app/api/guilds/[guildId]/members/[userId]/cases/route.ts` * `web/src/app/api/guilds/[guildId]/members/[userId]/route.ts` * `web/src/app/api/guilds/[guildId]/members/[userId]/xp/route.ts` * `web/src/app/api/guilds/[guildId]/members/export/route.ts` * `web/src/app/api/guilds/[guildId]/members/route.ts` * `web/src/app/dashboard/members/[userId]/page.tsx` * `web/src/app/dashboard/members/page.tsx` * `web/src/components/dashboard/member-table.tsx` These files were ignored: * `tests/api/routes/guilds.test.js` * `tests/api/routes/members.test.js` * fix: correct CSV formula-injection bug in escapeCsv The escapeCsv function was discarding the original string value when prefixing with a single quote to neutralize formula injection. Now correctly preserves the value: str = `'${str}` instead of str = `'`. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: include guildId in member row click navigation Include guildId as a query parameter when navigating to member detail page to ensure guild context is preserved across navigation. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: use safeGetPool in all member endpoints The GET /:id/members endpoint was using raw getPool() instead of safeGetPool() with the 503 guard used by all other member endpoints. Now consistently returns 503 when database is unavailable. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: log CSV export errors instead of silent failure Add console.error logging when CSV export fails, in addition to setting the error state for user display. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(members): dedupe rate limiting, add 401 handling, fix loading state, remove console.error * fix(members): reject fractional XP amounts (column is INTEGER) * test: boost branch coverage to 85% with targeted tests Raise branch coverage from 83.32% to 85.24% across 4 key files: - sentry.js: beforeSend filter, tracesSampleRate parsing, environment resolution - events.js: review/showcase/challenge button handlers, partial fetch, rate limit/link filter branches - rateLimit.js: repeat offender edge cases, permission checks, alert channel, cleanup - members.js: safeGetPool null paths (503), transaction rollback, escapeCsv edge cases New files: tests/modules/events-extra.test.js Modified: tests/modules/rateLimit.test.js, tests/sentry.init.test.js Removed unused import: src/api/index.js * fix(members): reject non-integer XP amounts with 400 The reputation.xp column is INTEGER. Fractional values like 1.5 pass the existing finite/non-zero check but get silently truncated by PostgreSQL (admin adds 1.5, only 1 is stored). Add explicit Number.isInteger check after the existing guards, returning 400 with 'amount must be an integer'. * test(members): add test for fractional XP amount returning 400 Covers the new Number.isInteger guard — sending amount: 1.5 must return 400 with error 'amount must be an integer'. * fix(members): scope membersRateLimit to member routes only The global router.use(membersRateLimit) was applied to every request hitting this router, which is mounted at /api/v1/guilds before the guilds router. This accidentally rate-limited non-member guild endpoints (e.g. /guilds/:id/analytics). Remove the global router.use and add membersRateLimit explicitly on each of the five member route definitions (export, list, detail, cases, xp) so rate limiting is scoped correctly. * fix(members-ui): fix roleColorStyle fallback for hex alpha concatenation The caller appends '40' and '15' to the result for borderColor and backgroundColor. When the fallback was 'hsl(var(--muted-foreground))' this produced 'hsl(var(--muted-foreground))40' — not valid CSS — so roles with Discord's default color (#000000) got no border/background. Replace the HSL CSS-variable fallback with a plain hex grey (#6b7280) so appending hex alpha digits produces valid 8-digit hex colours. * fix: biome formatting in members.js * fix(members): add AbortController and request sequencing to prevent stale responses Add AbortController to cancel in-flight fetch requests when a new request is triggered (e.g., from search/sort changes). Also add a monotonic request ID counter to guard against out-of-order responses overwriting newer state. - Abort previous request on each new fetchMembers call - Pass AbortSignal to fetch() - Silently ignore AbortError exceptions - Discard stale responses/errors via requestId guard - Only clear loading state for the current (non-superseded) request - Abort in-flight request on component unmount * fix(members): use Discord server-side search instead of page-local filtering Replace client-side substring filtering (which only searched within the current page of Discord results) with Discord's guild.members.search() API for server-side search across all guild members. - search param now triggers guild.members.search({ query, limit }) - Cursor pagination (nextAfter) is null during search (Discord search does not support cursor-based paging) - filteredTotal reflects actual server-side search result count - Sort still applies to the returned page (documented limitation — global sort would require DB-driven member indexing) - Updated tests to verify search() is called and filteredTotal semantics * fix(members): stream CSV export in batches to reduce memory pressure Replace the pattern of accumulating all guild members in one in-memory Map before writing CSV. Now each batch of 1000 members is fetched from Discord, enriched from the DB, written to the response, and released for GC — keeping peak memory at O(batch_size) instead of O(total_members). - Move CSV header write before the batch loop - Process and write each batch inline instead of collecting all first - Remove userIds.length > 0 guards (batch loop guarantees non-empty) - Track exportedCount incrementally - Added streaming export test * fix: button types, useCallback deps, array keys, remove duplicate tests and eslint comments --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <[email protected]>
BillChirico
added a commit
that referenced
this pull request
Feb 28, 2026
Replaces two hardcoded 25s with a single PAGE_SIZE = 25 constant.
BillChirico
added a commit
that referenced
this pull request
Feb 28, 2026
* feat(conversations): add flagged_messages migration * feat(conversations): add API endpoints for list, detail, search, flag, stats * feat(conversations): mount router in API index * feat(conversations): add conversation list and replay dashboard pages * feat(conversations): add Next.js API proxy routes * test(conversations): add API and grouping tests * fix(conversations): escape ILIKE wildcards to prevent wildcard injection * fix(conversations): remove unused _totalChars variable * fix(conversations): cap list query to 5000 rows to prevent memory exhaustion * fix(conversations): replace in-memory stats grouping with SQL aggregates * fix(conversations): bound conversation detail query by time window instead of full channel scan * style: alphabetize imports and format authorizeGuildAdmin calls * test(conversations): fix stats mock to match SQL conversation counting * test(conversations): add POST flag endpoint to guild validation test The auth test already covered all 5 endpoints including POST .../flag, but the guild-validation test only checked 4 GET endpoints, leaving the flag endpoint's guild validation untested. Resolves review thread PRRT_kwDORICdSM5xTeiw * fix(conversations): parameterize SQL interval and fix flag button a11y Thread 3: Replace string interpolation of CONVERSATION_GAP_MINUTES in the window-function SQL with a $2 parameter to avoid hardcoded literals. Passes CONVERSATION_GAP_MINUTES as a query value instead. Thread 4: Change flag button wrapper from `focus-within:opacity-100` to `group-focus-within:opacity-100` so the button becomes visible whenever any element in the message bubble group receives keyboard focus, not just when the button's own children are focused — matching the group-hover pattern and ensuring proper keyboard accessibility. Also: biome --write reformatted label.tsx and textarea.tsx (pre-existing style issues). * test: add ILIKE wildcard escape coverage for % and _ characters Adds test cases verifying that % and _ characters in conversation search queries are properly escaped before being used in ILIKE patterns, preventing them from acting as SQL wildcards. * fix: deterministic flag status for duplicate flagged_messages rows When a message has been flagged multiple times (flagged_messages has no UNIQUE constraint on message_id), the previous Map construction would silently overwrite entries in iteration order, making the displayed status non-deterministic. Order the SELECT by created_at DESC so the first row per message_id that lands in the Map is always the most recently created flag, giving a predictable 'latest wins' behaviour. * refactor: extract escapeIlike utility from logQuery inline impl Creates src/utils/escapeIlike.js as a shared, exported utility. Conversations route now imports it instead of duplicating the regex. * fix(conversations): use escapeIlike(), fix non-deterministic flag status, add 30-day default window, verify conversationId on flag POST - Import escapeIlike() instead of inline regex (DRY #4) - Default to last 30 days when no `from` filter to prevent unbounded LIMIT 5000 scan (#3) - Fix Map construction: iterate ORDER BY DESC rows and only set first occurrence per key so most-recent flag status wins (#1) - Verify flagged messageId belongs to the conversation's channel before inserting (#2) * test(conversations): add ILIKE backslash escape test and fix flag mocks for new anchor check - Add test for backslash (\) escaping in ILIKE search (#7) - Update 'flag a message' mock to include anchorCheck query result * refactor(web): add LOG_PREFIX constant to all 5 conversation proxy routes (#6) Each route previously inlined its prefix string on every call. Extracts to module-scope const matching the pattern used by config/members/roles routes. * fix(ui): use MessagesSquare icon for Conversations sidebar entry (#8) AI Chat already uses MessageSquare. Conversations now uses MessagesSquare to distinguish the two nav items visually. * fix(ui): show last 4 digits of channel snowflake instead of raw ID (#9) Raw Discord snowflakes are 18+ digit numbers. Showing `${channelId.slice(-4)}` gives a minimal but less jarring display until a proper channel name resolver is wired up. * refactor(ui): extract PAGE_SIZE constant in conversations page (#5) Replaces two hardcoded 25s with a single PAGE_SIZE = 25 constant. * docs(migration): explain missing FK on conversation_first_id (#10) conversation_first_id has no FK because conversations are not a separate table with their own PK. They are virtual groups derived from message rows. message_id already carries a FK for referential integrity. * fix(lint): suppress pre-existing biome a11y errors in label component * fix(conversations): stray $ in JSX channel display, increase query limit to 10k
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…utdown