Skip to content

feat(dashboard): audit log — track all admin actions with attribution#260

Merged
BillChirico merged 8 commits intomainfrom
feat/issue-123
Mar 7, 2026
Merged

feat(dashboard): audit log — track all admin actions with attribution#260
BillChirico merged 8 commits intomainfrom
feat/issue-123

Conversation

@BillChirico
Copy link
Collaborator

Summary

Full-stack audit log for dashboard admin actions.

Changes

  • Migration 013: audit_logs table with guild/user/action/details/IP, indexed for fast pagination
  • auditLogger.js: logAuditEvent() + purgeOldAuditLogs() with graceful DB error handling
  • Express middleware: Auto-logs all mutating API requests (POST/PUT/PATCH/DELETE)
  • API route: GET /api/v1/guilds/:id/audit-log — paginated, filterable by user/action/date
  • DB maintenance: Retention purge wired into runMaintenance()
  • Dashboard page: Table with Time/Admin/Action/Target/Details, expandable rows, date+action+user filters
  • Next.js proxy: /api/guilds/[guildId]/audit-log
  • Sidebar nav: Audit Log entry with ClipboardList icon
  • Retention config: auditLog.retentionDays in dashboard under Moderation & Safety
  • 19 new tests: auditLogger unit tests + dbMaintenance mock updates

Closes #123

Bill Chirico added 5 commits March 7, 2026 15:16
Creates the audit_logs table with columns for guild_id, user_id, user_tag,
action, target_type, target_id, details (JSONB), ip_address, and created_at.

Indexes:
  - idx_audit_logs_guild_created (guild_id, created_at DESC) — primary access pattern
  - idx_audit_logs_guild_user   (guild_id, user_id)          — admin filter

Closes part of #123
…ditLogs (#123)

Introduces a standalone audit logger module:

  - logAuditEvent(pool, event) — fire-and-forget DB insert into audit_logs.
    Gracefully handles null pool, missing required fields, and query failures
    (logs error, never rethrows) so audit logging never breaks callers.

  - purgeOldAuditLogs(pool, retentionDays) — purges entries older than the
    configured retention period. retentionDays=0 disables purging.
    Handles missing table (42P01) without throwing.

Closes part of #123
…#123)

- Import getConfig + purgeOldAuditLogs in dbMaintenance.js
- Call purgeOldAuditLogs in runMaintenance() using auditLog.retentionDays from
  config (defaults to 90 days when not set)
- Update dbMaintenance tests: mock auditLogger module and config, keep existing
  assertions accurate

Closes part of #123
19 tests covering:
  - logAuditEvent: successful insert, field mapping, JSON serialisation,
    null pool graceful skip, missing required fields, DB failure (no rethrow)
  - purgeOldAuditLogs: row count return, info logging, retentionDays=0 skip,
    null pool skip, missing table (42P01), unexpected DB error

Closes part of #123
…123)

- types/config.ts: add AuditLogConfig interface + auditLog field to BotConfig;
  add 'auditLog' to ConfigSection union
- config-workspace/types.ts: add 'audit-log' to ConfigFeatureId union
- config-workspace/config-categories.ts: add audit-log feature + search items
  to moderation-safety category; add FEATURE_LABELS entry
- config-sections/AuditLogSection.tsx: new card component with enabled toggle
  and retention days input
- config-editor.tsx: import AuditLogSection, add updateAuditLogField callback,
  render section when moderation-safety + audit-log visible, add 'auditLog'
  to knownSections for changed-section tracking

Closes part of #123
@BillChirico BillChirico added this to the v0.1.0 - "Big Boy MVP" milestone Mar 7, 2026
@BillChirico BillChirico added the priority: medium Medium priority label Mar 7, 2026
Copilot AI review requested due to automatic review settings March 7, 2026 20:23
@BillChirico BillChirico added type: feature New feature scope: dashboard Web dashboard labels Mar 7, 2026
@BillChirico BillChirico added priority: medium Medium priority type: feature New feature scope: dashboard Web dashboard labels Mar 7, 2026
@github-project-automation github-project-automation bot moved this to Backlog in Volvox.Bot Mar 7, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

Warning

Rate limit exceeded

@BillChirico has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: aad9734d-e6be-466d-81b9-fbb6ba6f034c

📥 Commits

Reviewing files that changed from the base of the PR and between 4e21887 and 6af57fd.

📒 Files selected for processing (2)
  • TASK.md
  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
📝 Walkthrough

Walkthrough

Adds audit logging: a DB migration creating an audit_logs table, a backend auditLogger module with purge logic, integration into DB maintenance, Express middleware/userTag propagation, frontend config types and UI for enabling/retention, and tests for logger and maintenance behavior.

Changes

Cohort / File(s) Summary
Database migration
migrations/013_audit_log.cjs
Creates audit_logs table with columns (id, guild_id, user_id, user_tag, action, target_type, target_id, details, ip_address, created_at) and adds indexes on (guild_id, created_at DESC) and (guild_id, user_id). Includes down migration to drop the table.
Backend audit utilities & middleware
src/modules/auditLogger.js, src/api/middleware/auditLog.js, src/utils/dbMaintenance.js
Adds logAuditEvent(pool, event) (fire-and-forget insert) and purgeOldAuditLogs(pool, retentionDays) with error/table-missing handling; middleware now captures and propagates user_tag; maintenance invokes purge using configured retention.
Tests (backend)
tests/modules/auditLogger.test.js, tests/utils/dbMaintenance.test.js
Adds unit tests covering insertion parameter mapping, JSON details serialization, validation, error paths, purge behavior (including table-missing 42P01), and integration of purge call in maintenance flow.
Frontend types & config model
web/src/types/config.ts, web/src/components/dashboard/config-workspace/types.ts
Adds AuditLogConfig interface, auditLog?: AuditLogConfig to BotConfig, 'auditLog' to ConfigSection, and 'audit-log' to ConfigFeatureId.
Frontend UI: Config editor & section
web/src/components/dashboard/config-editor.tsx, web/src/components/dashboard/config-sections/AuditLogSection.tsx, web/src/components/dashboard/config-workspace/config-categories.ts
Introduces AuditLogSection component (enable switch, retention days input), wires it into ConfigEditor, updates Moderation & Safety category metadata, adds feature label and search items for audit-log settings.
Frontend pages & routing tweaks
web/src/app/dashboard/audit-log/page.tsx, web/src/app/api/guilds/[guildId]/audit-log/route.ts
Adds dashboard audit-log page and Next.js API proxy route (minor lint suppression in skeleton rendering).
Docs / TASK
TASK.md
Replaces previous frontend-only plan with backend-centric audit logging stack and documents added endpoints, middleware, migration, UI, and retention behavior.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main feature: a full-stack audit log system for tracking admin actions with user attribution on the dashboard.
Description check ✅ Passed The description comprehensively documents all major components of the audit logging system and relates directly to the changeset implemented.
Linked Issues check ✅ Passed All primary objectives from issue #123 are addressed: migration [#123], middleware auto-logging [#123], API endpoint with pagination/filters [#123], dashboard page with filters [#123], sidebar nav entry [#123], retention policy [#123], and tests [#123].
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #123 objectives: database migration, audit logging module, middleware, API routes, dashboard UI, config management, and tests—no extraneous or unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/issue-123
  • 🛠️ Publish Changes: Commit on current branch
  • 🛠️ Publish Changes: Create PR

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.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coveralls
Copy link

Coverage Status

coverage: 87.573% (+0.02%) from 87.549%
when pulling 2ecab60 on feat/issue-123
into f53f0be on main.

@greptile-apps
Copy link

greptile-apps bot commented Mar 7, 2026

Greptile Summary

This PR implements a full-stack audit log system: a audit_logs DB table (migration 013), an auditLogger.js module for direct inserts, Express middleware that auto-logs all mutating API requests with user_tag attribution, a paginated/filterable API route with CSV/JSON export, nightly retention purge wired into dbMaintenance, and a Next.js dashboard page with filters and expandable detail rows. The feature fits cleanly into the existing architecture (Winston logging, parameterised queries, SettingsFeatureCard pattern).

Two bugs need to be fixed before merge:

  • user_tag never returned to clients — The SELECT columns in src/api/routes/auditLog.js (paginated query line 163, export query line 220, and CSV headers line 117) all omit user_tag. The migration adds it, the middleware correctly inserts it, but the route never retrieves it. The frontend AuditEntry interface and the User table cell also need updating to surface the human-readable admin name instead of a raw Discord snowflake.

  • endDate filter silently excludes same-day entriesnew Date('2024-03-07') resolves to UTC midnight (2024-03-07T00:00:00.000Z). The resulting created_at <= '2024-03-07T00:00:00.000Z' condition excludes virtually all entries from that day. The end date should be normalized to end-of-day (setUTCHours(23, 59, 59, 999)) before building the ISO string.

One UX issue is also worth addressing: AuditLogSection renders its own "Enable Audit Logging" Switch, duplicating the toggle that SettingsFeatureCard already provides in its card header — consistent with every other config section in the editor.

Confidence Score: 2/5

  • Not safe to merge — two logic bugs mean user_tag is never surfaced and the end-date filter silently drops same-day entries.
  • The backend module, middleware, migration, and tests are solid, but the route file that glues everything together has two concrete bugs: the SELECT queries omit user_tag (defeating the main goal of admin attribution) and the endDate normalisation is incorrect (causing subtle filtering errors). Both are straightforward one-line fixes but affect core behaviour of the feature.
  • src/api/routes/auditLog.js requires attention for both the missing user_tag column in SELECT queries and the endDate UTC-midnight truncation.

Important Files Changed

Filename Overview
migrations/013_audit_log.cjs Clean schema: adds audit_logs table with guild/user/action/IP columns, appropriate indexes for the dashboard's primary query pattern, and a safe down migration.
src/modules/auditLogger.js Well-structured module: graceful null-pool handling, required-field validation, error logging without rethrowing, and correct parameterized queries throughout.
src/api/routes/auditLog.js Two critical issues: user_tag is missing from both SELECT queries (paginated + export) so it will never reach the client despite being correctly inserted; endDate is parsed as UTC midnight which excludes same-day entries.
web/src/app/dashboard/audit-log/page.tsx AuditEntry interface and table rendering omit user_tag, so the User column shows raw Discord snowflake IDs; abort controller + request-ID race condition handling is solid.
web/src/components/dashboard/config-sections/AuditLogSection.tsx Renders a second "Enable Audit Logging" Switch that duplicates the toggle already provided by the parent SettingsFeatureCard header; retention-period input is correct.

Sequence Diagram

sequenceDiagram
    participant Admin as Dashboard Admin
    participant Next as Next.js Proxy
    participant Express as Express API
    participant Middleware as auditLogMiddleware
    participant DB as PostgreSQL
    participant WS as WebSocket (auditStream)

    Admin->>Next: POST/PUT/PATCH/DELETE /api/guilds/:id/*
    Next->>Express: Proxied mutating request
    Express->>Middleware: auditLogMiddleware intercepts
    Middleware->>Middleware: deriveAction(), extractGuildId()
    Middleware->>Middleware: capture beforeConfig (if config update)
    Middleware->>Express: next() — request proceeds normally
    Express-->>Middleware: res.on('finish') fires (2xx/3xx only)
    Middleware->>Middleware: computeConfigDiff(), maskSensitiveFields()
    Middleware->>DB: INSERT INTO audit_logs (guild_id, user_id, user_tag, action, ...)
    DB-->>Middleware: RETURNING row (id, created_at, ...)
    Middleware->>WS: broadcastAuditEntry(row)

    Admin->>Next: GET /api/guilds/:id/audit-log?filters
    Next->>Express: GET /api/v1/guilds/:id/audit-log
    Express->>DB: SELECT COUNT(*) + SELECT columns FROM audit_logs WHERE ...
    DB-->>Express: {total, entries[]}
    Express-->>Next: {entries, total, limit, offset}
    Next-->>Admin: Rendered audit log table

    Note over DB: Nightly: runMaintenance()<br/>DELETE WHERE created_at < NOW() - interval(retentionDays)
Loading

Comments Outside Diff (2)

  1. src/api/routes/auditLog.js, line 163-164 (link)

    user_tag omitted from SELECT — never returned to clients

    This PR correctly adds user_tag to the migration schema and updates the middleware INSERT to populate it, but both SELECT queries in this route file exclude it. As a result, the API response will never contain user_tag, the frontend AuditEntry type doesn't declare it, and the dashboard table will always render raw Discord snowflake IDs in the "User" column instead of human-readable names.

    The same omission exists in the export query on line 220 and in the rowsToCsv headers on line 117.

    All three need to be updated:

    // Line 163 — paginated query
    SELECT id, guild_id, user_id, user_tag, action, target_type, target_id, details, ip_address, created_at
    
    // Line 220 — export query  
    SELECT id, guild_id, user_id, user_tag, action, target_type, target_id, details, ip_address, created_at
    
    // Line 117-127 — rowsToCsv headers
    const headers = ['id', 'guild_id', 'user_id', 'user_tag', 'action', 'target_type', 'target_id', 'details', 'ip_address', 'created_at'];

    The frontend AuditEntry interface in web/src/app/dashboard/audit-log/page.tsx (line 27) and the table's User cell (line 430) should also be updated to include and display user_tag.

  2. src/api/routes/auditLog.js, line 81-88 (link)

    endDate parsed as UTC midnight, excluding same-day entries

    new Date('2024-03-07') resolves to 2024-03-07T00:00:00.000Z (UTC midnight). The resulting filter created_at <= '2024-03-07T00:00:00.000Z' will exclude virtually all entries from March 7 — only those logged at exactly midnight UTC survive. A user selecting "March 7" as their end date expects to see all entries from that day.

    The end date should be normalized to the end of the day before constructing the ISO string:

    if (typeof query.endDate === 'string') {
      const end = new Date(query.endDate);
      if (!Number.isNaN(end.getTime())) {
        end.setUTCHours(23, 59, 59, 999);
        conditions.push(`created_at <= ${paramIndex}`);
        params.push(end.toISOString());
        paramIndex++;
      }
    }

Last reviewed commit: 6af57fd

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/dbMaintenance.js`:
- Around line 143-151: The current maintenance uses
getConfig()?.auditLog?.retentionDays as a single global value and calls
purgeOldAuditLogs(pool, auditRetentionDays), which ignores per-guild overrides
and deletes across the whole audit_logs table; change this to resolve retention
per guild and purge per-guild. Specifically, iterate all guilds (or read
guild-scoped settings) to get each guild's auditLog.retentionDays, then call a
per-guild purge function (e.g., extend purgeOldAuditLogs to accept guildId and
retentionDays or create purgeOldAuditLogsForGuild(pool, guildId, retentionDays))
and make sure the deletion SQL in purgeOldAuditLogs includes WHERE guild_id = ?
so each guild's retention window is honored.

In `@tests/modules/auditLogger.test.js`:
- Line 8: Remove the unused beforeEach import from the test module's import list
so only actually used helpers are imported (leave afterEach, describe, expect,
it, vi intact); locate the import statement that currently includes beforeEach
and delete that symbol from the destructured imports to satisfy the
noUnusedImports lint rule.

In `@tests/utils/dbMaintenance.test.js`:
- Around line 10-19: The test suite mocks purgeOldAuditLogs but never asserts
that runMaintenance() calls it with the configured retention, so add an
assertion that the mocked purgeOldAuditLogs was invoked with the retentionDays
value from the mocked getConfig (90); locate the mock for purgeOldAuditLogs in
tests/utils/dbMaintenance.test.js and after invoking runMaintenance() assert
something like purgeOldAuditLogs was called once and with the numeric retention
(90) so the audit cleanup integration is covered (use the purgeOldAuditLogs mock
reference and the runMaintenance invocation already present in the test).

In `@web/src/components/dashboard/config-editor.tsx`:
- Around line 2040-2047: The AuditLogSection is rendered directly which breaks
the shared SettingsFeatureCard/search jump contract (no feature-audit-log anchor
and no advanced panel auto-open for audit-log-retention); replace the direct
render with the reusable SettingsFeatureCard pattern: wrap AuditLogSection
inside a SettingsFeatureCard (provide header, master toggle, and separate
Basic/Advanced blocks) so the card emits the expected DOM anchor
(feature-audit-log) and advanced region; ensure audit-log-retention is
registered as an advanced hit and that the SettingsFeatureCard receives props to
auto-open its advanced panel when a search hit targets audit-log-retention;
preserve existing props (draftConfig, saving) and keep updateAuditLogField calls
wired to the inner controls.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 48d97c86-abac-46b9-b0da-9b49c1046082

📥 Commits

Reviewing files that changed from the base of the PR and between f53f0be and 2ecab60.

📒 Files selected for processing (10)
  • migrations/013_audit_log.cjs
  • src/modules/auditLogger.js
  • src/utils/dbMaintenance.js
  • tests/modules/auditLogger.test.js
  • tests/utils/dbMaintenance.test.js
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/types/config.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{js,cjs,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

Use ESM only with import/export syntax, never CommonJS except in migration files (.cjs)

Files:

  • src/modules/auditLogger.js
  • tests/modules/auditLogger.test.js
  • migrations/013_audit_log.cjs
  • src/utils/dbMaintenance.js
  • tests/utils/dbMaintenance.test.js
**/*.{js,mjs,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,mjs,jsx,ts,tsx}: Use single quotes for strings in code, double quotes only allowed in JSON files
Always end statements with semicolons
Use 2-space indentation, enforced by Biome

Files:

  • src/modules/auditLogger.js
  • web/src/components/dashboard/config-workspace/types.ts
  • tests/modules/auditLogger.test.js
  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • src/utils/dbMaintenance.js
  • tests/utils/dbMaintenance.test.js
  • web/src/types/config.ts
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-editor.tsx
src/**/*.{js,mjs,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{js,mjs,jsx,ts,tsx}: Use src/logger.js Winston logger singleton, never use console.* methods
Use safe Discord message methods: safeReply(), safeSend(), safeEditReply() instead of direct Discord.js methods
Use parameterized SQL queries, never string interpolation for database queries

Files:

  • src/modules/auditLogger.js
  • src/utils/dbMaintenance.js
src/modules/**/*.{js,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

Create new modules for features with corresponding config sections in config.json and entries in SAFE_CONFIG_KEYS

Files:

  • src/modules/auditLogger.js
{.env*,README.md,src/**/!(*.test).{js,ts}}

📄 CodeRabbit inference engine (CLAUDE.md)

Remove GUILD_ID from shared environment variables in production/deployment configurations; preserve dev-only guild-scoped deploy support via CLI flag --guild-id <guild_id>

Files:

  • src/modules/auditLogger.js
  • src/utils/dbMaintenance.js
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Next.js 16 web dashboard uses App Router with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design

Files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • web/src/types/config.ts
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-editor.tsx
web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx}: Web dashboard config editor must use category workspace navigation with categories: AI & Automation, Onboarding & Growth, Moderation & Safety, Community Tools, Support & Integrations located in web/src/components/dashboard/config-workspace/
Config editor must implement metadata-driven config search with cross-category quick jump, focus/scroll targeting, and auto-open advanced sections when search hits advanced controls
Refactor config feature presentation to use reusable SettingsFeatureCard pattern with structure: header + master toggle + Basic/Advanced blocks
Config editor save contract must maintain: global save/discard, diff-modal confirmation, per-section PATCH batching, and partial-failure behavior

Files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-workspace/config-categories.ts
tests/**/*.{js,mjs,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain 80% code coverage threshold minimum, never lower this threshold

Files:

  • tests/modules/auditLogger.test.js
  • tests/utils/dbMaintenance.test.js
migrations/**/*.cjs

📄 CodeRabbit inference engine (AGENTS.md)

Use .cjs file extension for database migrations, use sequential migration numbering (001, 002, etc.) with node-pg-migrate

Files:

  • migrations/013_audit_log.cjs
migrations/[0-9]*_*.cjs

📄 CodeRabbit inference engine (CLAUDE.md)

Database migrations must be sequentially numbered with non-conflicting IDs; rename conflicting migration files to resolve out-of-order execution errors (e.g., migrations/004_*.cjsmigrations/007_*.cjs, etc.)

Files:

  • migrations/013_audit_log.cjs
🧠 Learnings (7)
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx} : Refactor config feature presentation to use reusable `SettingsFeatureCard` pattern with structure: header + master toggle + Basic/Advanced blocks

Applied to files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx} : Web dashboard config editor must use category workspace navigation with categories: `AI & Automation`, `Onboarding & Growth`, `Moderation & Safety`, `Community Tools`, `Support & Integrations` located in `web/src/components/dashboard/config-workspace/`

Applied to files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx} : Config editor must implement metadata-driven config search with cross-category quick jump, focus/scroll targeting, and auto-open advanced sections when search hits advanced controls

Applied to files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.test.{js,jsx,ts,tsx} : Config editor tests must cover manual-save workspace behavior (not autosave assumptions), category switching, search functionality, and dirty badges

Applied to files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Applies to src/api/utils/configAllowlist.js : Maintain `SAFE_CONFIG_KEYS` for writable config sections via API and `READABLE_CONFIG_KEYS` for read-only sections, add new config sections to SAFE to enable saves

Applied to files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Applies to migrations/**/*.cjs : Use `.cjs` file extension for database migrations, use sequential migration numbering (001, 002, etc.) with node-pg-migrate

Applied to files:

  • migrations/013_audit_log.cjs
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx} : Config editor save contract must maintain: global save/discard, diff-modal confirmation, per-section PATCH batching, and partial-failure behavior

Applied to files:

  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-editor.tsx
🪛 GitHub Actions: CI
src/modules/auditLogger.js

[warning] 28-28: assist/source/organizeImports: The imports/exports are not sorted. Safe fix suggested.

tests/modules/auditLogger.test.js

[error] 8-8: lint/correctness/noUnusedImports: Unused imports detected.

web/src/components/dashboard/config-sections/AuditLogSection.tsx

[error] 62-66: format: Formatter would have printed different content. Ensure code is formatted as expected.

src/utils/dbMaintenance.js

[warning] 11-11: assist/source/organizeImports: The imports/exports are not sorted. Safe fix suggested.

🔇 Additional comments (2)
src/modules/auditLogger.js (1)

74-89: The logAuditEvent() function in src/modules/auditLogger.js is fully implemented with parameterized SQL and is not actually called anywhere in the codebase. There is no auto-logging of request bodies in this PR—the routes that handle config updates (src/api/routes/config.js, src/api/routes/guilds.js) do not invoke logAuditEvent(). Therefore, the scenario described in the review (config edits storing plaintext API keys in audit logs) is not realized, and no redaction logic is needed at this time. If future changes add calls to logAuditEvent() with raw request payloads, redaction should be considered then.

			> Likely an incorrect or invalid review comment.
web/src/components/dashboard/config-workspace/config-categories.ts (1)

334-351: The audit log search metadata is properly integrated into the config system.

The search items audit-log-enabled and audit-log-retention correctly map to the audit-log feature ID, which is included in the moderation-safety category. The config editor's forceOpenAdvancedFeatureId logic (lines 308–319 in config-editor.tsx) detects when a search result targets an advanced control and auto-opens the Advanced block via the forceOpenAdvanced prop on SettingsFeatureCard. The retention control's placement inside the Advanced block and the section's conditional rendering on feature visibility are wired correctly, so search results will properly focus and auto-expand the retention setting.

@github-project-automation github-project-automation bot moved this from Backlog to In Review in Volvox.Bot Mar 7, 2026
- middleware: include user_tag in audit log inserts
- middleware: pass userTag from session to insertAuditEntry
- dbMaintenance: add comment clarifying global vs per-guild retention scope
- auditLogger.test.js: remove unused beforeEach import
- dbMaintenance.test.js: add assertion that runMaintenance calls purgeOldAuditLogs with retentionDays
- config-editor: wrap AuditLogSection in SettingsFeatureCard for search/jump contract
- audit-log/page.tsx: biome-ignore on skeleton index keys
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/api/middleware/auditLog.js (1)

103-120: 🛠️ Refactor suggestion | 🟠 Major

Deduplicate the audit_logs insert contract.

This file now owns a second copy of the column list and parameter ordering that already exists in src/modules/auditLogger.js. user_tag had to be patched in both places in this PR; the next schema change can easily update one path and miss the other. Route middleware writes through the shared logger or extract a common insert helper.

Also applies to: 252-261

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

In `@src/api/middleware/auditLog.js` around lines 103 - 120, The insert
SQL/parameter contract for audit_logs is duplicated here; replace the inline
pool.query in audit middleware with the shared insert helper from
src/modules/auditLogger.js (e.g., import and call insertAuditLog or the existing
logAudit function) so there’s a single canonical SQL/parameter ordering; ensure
the helper handles guildId -> 'global' fallback, userTag nulling,
JSON.stringify(details), and ipAddress nulling and update the other duplicate
(lines ~252-261) to call the same helper instead of re-duplicating the INSERT
and parameter list.
♻️ Duplicate comments (3)
src/modules/auditLogger.js (1)

113-124: ⚠️ Potential issue | 🟠 Major

Guild-scoped retention cannot be enforced with this purge API.

purgeOldAuditLogs() only takes retentionDays and deletes without a guild_id predicate, so any caller applying a guild override will still purge every guild's rows older than that threshold. If auditLog.retentionDays is configurable per guild, this helper needs a guild-scoped variant; otherwise the setting should be global-only.

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

In `@src/modules/auditLogger.js` around lines 113 - 124, The purgeOldAuditLogs
function currently deletes audit rows by retentionDays across all guilds (see
purgeOldAuditLogs and its pool.query DELETE) which prevents enforcing per-guild
retention; either implement a guild-scoped variant or make retentionDays
global-only: add an optional guildId parameter (e.g., guildId) to
purgeOldAuditLogs and update the SQL to include "AND guild_id = $2" (and pass
guildId) so callers can purge only that guild, or if per-guild retention is
unsupported, document and enforce global-only behavior by removing any
guild-specific override code paths and keeping purgeOldAuditLogs strictly
global.
tests/utils/dbMaintenance.test.js (1)

21-21: ⚠️ Potential issue | 🟠 Major

Re-import auditLogger inside beforeEach() after calling vi.resetModules().

The top-level import at line 21 is captured before the module cache is reset. When beforeEach() clears modules and re-imports dbMaintenance, the mock factory creates a fresh auditLogger instance. The assertion at line 53 then checks a stale reference while the code under test calls the new one.

Fix
-import * as auditLogger from '../../src/modules/auditLogger.js';
-
 describe('runMaintenance', () => {
   let runMaintenance;
+  let auditLogger;
   let mockPool;
   let logger;
 
   beforeEach(async () => {
     vi.resetModules();
     vi.clearAllMocks();
     logger = await import('../../src/logger.js');
+    auditLogger = await import('../../src/modules/auditLogger.js');
     const mod = await import('../../src/utils/dbMaintenance.js');
     runMaintenance = mod.runMaintenance;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/utils/dbMaintenance.test.js` at line 21, The top-level import of
auditLogger is captured before vi.resetModules(), causing the test to assert
against a stale instance; remove the module-level import of auditLogger and
instead import it inside beforeEach() after calling vi.resetModules() and
re-importing dbMaintenance so the test uses the fresh mocked auditLogger
instance (ensure any assertions referencing auditLogger use the re-imported
variable and that dbMaintenance is imported after vi.resetModules()).
web/src/components/dashboard/config-editor.tsx (1)

2040-2058: ⚠️ Potential issue | 🟠 Major

Remove duplicate onEnabledChangeSettingsFeatureCard already handles the master toggle.

Line 2046 wires onEnabledChange on the SettingsFeatureCard, which provides the master enable/disable toggle. Line 2053 passes the same callback to AuditLogSection, which renders a second toggle internally. This creates two UI controls for the same setting.

Once AuditLogSection is refactored to remove its internal Card/Switch, drop the onEnabledChange prop here as well:

♻️ Proposed fix after AuditLogSection refactor
           {activeCategoryId === 'moderation-safety' && visibleFeatureIds.has('audit-log') && (
             <SettingsFeatureCard
               featureId="audit-log"
               title="Audit Log"
               description="Record admin actions taken via the dashboard (config changes, XP adjustments, warnings)."
               enabled={draftConfig.auditLog?.enabled ?? true}
               onEnabledChange={(v) => updateAuditLogField('enabled', v)}
               disabled={saving}
               forceOpenAdvanced={forceOpenAdvancedFeatureId === 'audit-log'}
               basicContent={
                 <AuditLogSection
                   draftConfig={draftConfig ?? {}}
                   saving={saving}
-                  onEnabledChange={(v) => updateAuditLogField('enabled', v)}
                   onRetentionDaysChange={(days) => updateAuditLogField('retentionDays', days)}
                 />
               }
             />
           )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/config-editor.tsx` around lines 2040 - 2058, The
AuditLogSection toggle is being wired twice: SettingsFeatureCard already
receives the master onEnabledChange via featureId="audit-log", so remove the
duplicate onEnabledChange prop passed to AuditLogSection in this JSX block; keep
other props (draftConfig, saving, onRetentionDaysChange) and continue using
updateAuditLogField for retention and the enabled state
(draftConfig.auditLog?.enabled) only where needed, ensuring SettingsFeatureCard
remains responsible for the master toggle.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@node_modules`:
- Line 1: The repository accidentally tracks a placeholder file named
node_modules which prevents pnpm from creating the real node_modules directory;
remove that tracked file from git (git rm --cached node_modules or git rm
node_modules then commit), add node_modules to .gitignore to prevent re-adding,
and push the commit so CI/preview can create the real node_modules tree with
pnpm install.

In `@web/node_modules`:
- Line 1: Remove the tracked placeholder file named "web/node_modules" from
version control so the package manager can create an actual directory;
specifically, delete the tracked file (the placeholder "web/node_modules") and
commit the removal, and ensure your .gitignore allows the node_modules directory
to be untracked (verify that "web/node_modules/" is not accidentally ignored or
listed as a file), then push the commit to remove the placeholder from the repo.

In `@web/src/components/dashboard/config-sections/AuditLogSection.tsx`:
- Around line 34-91: AuditLogSection currently renders its own Card, CardHeader,
CardTitle, description and the enable Switch which duplicates the outer
SettingsFeatureCard; remove the outer <Card> wrapper, the
<CardHeader>/<CardTitle>/<CardDescription> block and the enable Switch (the
onEnabledChange prop is already handled by SettingsFeatureCard), and leave only
the inner controls: the retention Label/paragraph, the Input bound to
retentionDays with onRetentionDaysChange, the retention helper text and preserve
the disabled logic that uses saving || !enabled; keep the same props (enabled,
retentionDays, onRetentionDaysChange, saving) and ensure the component returns
just the CardContent-equivalent block (no extra wrapping Card) so it integrates
cleanly as basicContent in SettingsFeatureCard.

---

Outside diff comments:
In `@src/api/middleware/auditLog.js`:
- Around line 103-120: The insert SQL/parameter contract for audit_logs is
duplicated here; replace the inline pool.query in audit middleware with the
shared insert helper from src/modules/auditLogger.js (e.g., import and call
insertAuditLog or the existing logAudit function) so there’s a single canonical
SQL/parameter ordering; ensure the helper handles guildId -> 'global' fallback,
userTag nulling, JSON.stringify(details), and ipAddress nulling and update the
other duplicate (lines ~252-261) to call the same helper instead of
re-duplicating the INSERT and parameter list.

---

Duplicate comments:
In `@src/modules/auditLogger.js`:
- Around line 113-124: The purgeOldAuditLogs function currently deletes audit
rows by retentionDays across all guilds (see purgeOldAuditLogs and its
pool.query DELETE) which prevents enforcing per-guild retention; either
implement a guild-scoped variant or make retentionDays global-only: add an
optional guildId parameter (e.g., guildId) to purgeOldAuditLogs and update the
SQL to include "AND guild_id = $2" (and pass guildId) so callers can purge only
that guild, or if per-guild retention is unsupported, document and enforce
global-only behavior by removing any guild-specific override code paths and
keeping purgeOldAuditLogs strictly global.

In `@tests/utils/dbMaintenance.test.js`:
- Line 21: The top-level import of auditLogger is captured before
vi.resetModules(), causing the test to assert against a stale instance; remove
the module-level import of auditLogger and instead import it inside beforeEach()
after calling vi.resetModules() and re-importing dbMaintenance so the test uses
the fresh mocked auditLogger instance (ensure any assertions referencing
auditLogger use the re-imported variable and that dbMaintenance is imported
after vi.resetModules()).

In `@web/src/components/dashboard/config-editor.tsx`:
- Around line 2040-2058: The AuditLogSection toggle is being wired twice:
SettingsFeatureCard already receives the master onEnabledChange via
featureId="audit-log", so remove the duplicate onEnabledChange prop passed to
AuditLogSection in this JSX block; keep other props (draftConfig, saving,
onRetentionDaysChange) and continue using updateAuditLogField for retention and
the enabled state (draftConfig.auditLog?.enabled) only where needed, ensuring
SettingsFeatureCard remains responsible for the master toggle.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b09303f7-90c3-487d-be57-951a9c76a099

📥 Commits

Reviewing files that changed from the base of the PR and between 2ecab60 and 4e21887.

📒 Files selected for processing (11)
  • TASK.md
  • node_modules
  • src/api/middleware/auditLog.js
  • src/modules/auditLogger.js
  • src/utils/dbMaintenance.js
  • tests/modules/auditLogger.test.js
  • tests/utils/dbMaintenance.test.js
  • web/node_modules
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,mjs,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,mjs,jsx,ts,tsx}: Use single quotes for strings in code, double quotes only allowed in JSON files
Always end statements with semicolons
Use 2-space indentation, enforced by Biome

Files:

  • web/src/app/dashboard/audit-log/page.tsx
  • src/utils/dbMaintenance.js
  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • src/api/middleware/auditLog.js
  • tests/utils/dbMaintenance.test.js
  • src/modules/auditLogger.js
  • tests/modules/auditLogger.test.js
  • web/src/components/dashboard/config-editor.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Next.js 16 web dashboard uses App Router with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design

Files:

  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • web/src/components/dashboard/config-editor.tsx
**/*.{js,cjs,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

Use ESM only with import/export syntax, never CommonJS except in migration files (.cjs)

Files:

  • src/utils/dbMaintenance.js
  • src/api/middleware/auditLog.js
  • tests/utils/dbMaintenance.test.js
  • src/modules/auditLogger.js
  • tests/modules/auditLogger.test.js
src/**/*.{js,mjs,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{js,mjs,jsx,ts,tsx}: Use src/logger.js Winston logger singleton, never use console.* methods
Use safe Discord message methods: safeReply(), safeSend(), safeEditReply() instead of direct Discord.js methods
Use parameterized SQL queries, never string interpolation for database queries

Files:

  • src/utils/dbMaintenance.js
  • src/api/middleware/auditLog.js
  • src/modules/auditLogger.js
{.env*,README.md,src/**/!(*.test).{js,ts}}

📄 CodeRabbit inference engine (CLAUDE.md)

Remove GUILD_ID from shared environment variables in production/deployment configurations; preserve dev-only guild-scoped deploy support via CLI flag --guild-id <guild_id>

Files:

  • src/utils/dbMaintenance.js
  • src/api/middleware/auditLog.js
  • src/modules/auditLogger.js
tests/**/*.{js,mjs,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain 80% code coverage threshold minimum, never lower this threshold

Files:

  • tests/utils/dbMaintenance.test.js
  • tests/modules/auditLogger.test.js
src/modules/**/*.{js,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

Create new modules for features with corresponding config sections in config.json and entries in SAFE_CONFIG_KEYS

Files:

  • src/modules/auditLogger.js
🧠 Learnings (8)
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to {.env*,README.md,src/**/!(*.test).{js,ts}} : Remove `GUILD_ID` from shared environment variables in production/deployment configurations; preserve dev-only guild-scoped deploy support via CLI flag `--guild-id <guild_id>`

Applied to files:

  • src/utils/dbMaintenance.js
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx} : Config editor save contract must maintain: global save/discard, diff-modal confirmation, per-section PATCH batching, and partial-failure behavior

Applied to files:

  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx} : Refactor config feature presentation to use reusable `SettingsFeatureCard` pattern with structure: header + master toggle + Basic/Advanced blocks

Applied to files:

  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx} : Config editor must implement metadata-driven config search with cross-category quick jump, focus/scroll targeting, and auto-open advanced sections when search hits advanced controls

Applied to files:

  • web/src/components/dashboard/config-sections/AuditLogSection.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Applies to tests/**/*.{js,mjs,jsx,ts,tsx} : Maintain 80% code coverage threshold minimum, never lower this threshold

Applied to files:

  • tests/utils/dbMaintenance.test.js
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{js,jsx,ts,tsx} : Web dashboard config editor must use category workspace navigation with categories: `AI & Automation`, `Onboarding & Growth`, `Moderation & Safety`, `Community Tools`, `Support & Integrations` located in `web/src/components/dashboard/config-workspace/`

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-07T15:34:56.495Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-07T15:34:56.495Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.test.{js,jsx,ts,tsx} : Config editor tests must cover manual-save workspace behavior (not autosave assumptions), category switching, search functionality, and dirty badges

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Applies to src/api/utils/configAllowlist.js : Maintain `SAFE_CONFIG_KEYS` for writable config sections via API and `READABLE_CONFIG_KEYS` for read-only sections, add new config sections to SAFE to enable saves

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
🪛 GitHub Actions: Railway Preview Deploy
node_modules

[error] 1-1: pnpm install --frozen-lockfile failed with ENOTDIR: not a directory when attempting to mkdir for 'node_modules' (exit code 236).

🪛 markdownlint-cli2 (0.21.0)
TASK.md

[warning] 3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 9-9: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 24-24: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 29-29: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 35-35: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 40-40: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 47-47: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 54-54: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 58-58: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 62-62: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 66-66: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🔇 Additional comments (3)
TASK.md (1)

1-72: LGTM — task planning document is clear and comprehensive.

The static analysis warnings about blank lines around headings are minor formatting nits and don't affect the document's utility. If you want to silence them, add a blank line before each ### heading (e.g., before lines 9, 24, 29, etc.).

web/src/components/dashboard/config-editor.tsx (2)

117-117: LGTM — 'auditLog' correctly added to known sections.

The type guard now properly recognizes the new config section.


699-707: LGTM — updateAuditLogField follows established patterns.

The callback is consistent with other field updaters like updateModerationField and correctly wires to updateDraftConfig.

Copilot AI review requested due to automatic review settings March 7, 2026 22:26
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@BillChirico BillChirico merged commit d693f8e into main Mar 7, 2026
10 of 16 checks passed
@BillChirico BillChirico deleted the feat/issue-123 branch March 7, 2026 22:50
@github-project-automation github-project-automation bot moved this from In Review to Done in Volvox.Bot Mar 7, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2026

🧹 Preview Environment Cleaned Up

The Railway preview environment for this PR has been removed.

Environment: pr-260

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: medium Medium priority scope: dashboard Web dashboard type: feature New feature

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Web dashboard: audit log — track all admin actions with attribution

3 participants