Skip to content

Phase g app surfaces - #9

Merged
Bonobo791 merged 3 commits into
mainfrom
phase-g-app-surfaces
Jul 30, 2026
Merged

Phase g app surfaces#9
Bonobo791 merged 3 commits into
mainfrom
phase-g-app-surfaces

Conversation

@Bonobo791

Copy link
Copy Markdown
Owner

No description provided.

@cla-bot cla-bot Bot added the cla-signed label Jul 30, 2026
@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 8ae28df Jul 30, 2026 · 15:45 15:48

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added human-readable relative timestamps across the dashboard, review queue, and audit log.
    • Added confirmation prompts for destructive delete and ban actions.
    • Added success messages after moderation actions.
    • Improved narrow-screen table layouts with stacked, labeled rows.
    • Added responsive dashboard statistics styling and optimized font loading.
    • Enhanced social sharing previews with a large image.
  • Bug Fixes

    • Clarified badge states for pending, completed, and destructive actions.
    • Improved consistency of moderation action responses.
  • Documentation

    • Expanded badge guidance to define the attention state and visual styling.

Walkthrough

The changes add a guarded local database seeding CLI, relative-time formatting, queue confirmation and success states, responsive moderation tables, expanded badge semantics, dashboard status updates, font preloads, and social sharing image metadata.

Changes

Moderation UI updates

Layer / File(s) Summary
Relative-time utility and consumers
src/lib/relative-time.ts, src/lib/relative-time.test.ts, src/routes/(app)/channels/[id]/queue/+page.svelte, src/routes/(app)/channels/[id]/log/+page.svelte, src/routes/(app)/dashboard/+page.svelte
Adds tested relative-time formatting and applies it to queue, audit-log, and dashboard timestamps.
Queue confirmations and action responses
src/routes/(app)/channels/[id]/queue/+page.server.ts, src/routes/(app)/channels/[id]/queue/+page.svelte, src/routes/(app)/channels/[id]/queue/*.test.ts
Adds explicit success payloads, destructive-action confirmation states, success flashes, and corresponding tests.
Badge and responsive moderation layouts
DESIGN.md, src/app.css, src/routes/(app)/channels/[id]/log/+page.svelte
Adds attention badge semantics and styling, classifies dry-run actions as neutral, and enables stacked tables below 760px.
Dashboard status presentation
src/app.css, src/routes/(app)/dashboard/+page.svelte
Adds dashboard stat-row styling, pending queue links, status badges, and formatted last-checked timestamps.

Local development database seeding

Layer / File(s) Summary
Database safety and reset controls
scripts/seed-dev.mjs
Requires a local database, verifies required tables, and supports reset and duplicate-seed checks.
Deterministic demo data generation
scripts/seed-dev.mjs
Seeds deterministic channel, rule, comment, and AI-score data with rule linkage and text-length validation.
Moderation history and CLI output
scripts/seed-dev.mjs
Seeds moderation actions and audit entries, then prints entity counts and the reset command.

Presentation metadata and font loading

Layer / File(s) Summary
Font and social metadata
src/app.html, src/routes/+page.svelte
Preloads two webfonts and adds large-image Open Graph and Twitter metadata.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant QueuePage
  participant QueueActions
  participant act
  QueuePage->>QueuePage: confirm delete or ban
  QueuePage->>QueueActions: POST destructive action
  QueueActions->>act: apply moderation side effect
  act-->>QueueActions: return completion
  QueueActions-->>QueuePage: return success message
  QueuePage-->>QueuePage: render status flash
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too vague to clearly identify the main change in the changeset. Use a concise, specific title that names the primary user-facing change, such as badge/status updates or responsive app surface updates.
Description check ❓ Inconclusive No description was provided, so the PR context is too sparse to assess intent from the text alone. Add a short description summarizing the main UI, badge, and seeding changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch phase-g-app-surfaces

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

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Jul 30, 2026
@sonarqubecloud

Copy link
Copy Markdown

@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

CodeAnt-AI Description

Make moderation surfaces clearer, safer, and easier to use

What Changed

  • Added inline confirmation before deleting a comment or banning an author, including the consequences of each action
  • Shows a clear success message after approve, reject, delete, or ban actions are recorded
  • Displays queue, dashboard, and audit-log timestamps as readable relative times while preserving the exact time on hover
  • Clarified status badges so pending items need attention while completed destructive actions remain neutral
  • Reworks the audit log into labeled cards on narrow screens to prevent cramped or overflowing tables
  • Added a local-only development seed script with representative channels, comments, rules, moderation actions, and audit history
  • Added social preview images and preloaded display fonts for the public landing page
  • Added coverage for relative timestamps and moderation success states

Impact

✅ Fewer accidental comment deletions and bans
✅ Clearer moderation status and action feedback
✅ Usable audit log on mobile

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Summary

This PR implements the core application surfaces for the Moderaty YouTube comment moderation tool. The changes add the landing page, dashboard, queue review interface, audit log, along with comprehensive styling based on the design system, and supporting utilities.

Key additions:

  • Complete design system documentation (DESIGN.md) defining the "cyclorama cue sheet" visual language
  • Landing page with animated gradient background and cue-based navigation
  • Dashboard showing channel stats and moderation counts
  • Review queue with inline confirmation for destructive actions
  • Audit log with responsive stack-table layout
  • CSS implementation with badge variants, responsive tables, and state handling
  • relativeTime utility with tests for human-readable timestamps
  • Development seed script with local-only safety checks
  • Font preloading for improved performance

Code quality observations:

  • Proper error handling and validation throughout
  • Accessibility features (ARIA labels, roles, keyboard navigation)
  • Comprehensive test coverage for actions and UI states
  • Security considerations (local-only seed script, parameterized queries)
  • Responsive design with mobile-friendly layouts

No blocking issues identified. The implementation is production-ready.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 7 minor

Results:
7 new issues

Category Results
CodeStyle 7 minor

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0 (≤ 100 complexity)
Duplication 0 (≤ 1 duplication)

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@Bonobo791
Bonobo791 merged commit 922a055 into main Jul 30, 2026
4 of 5 checks passed
@Bonobo791
Bonobo791 deleted the phase-g-app-surfaces branch July 30, 2026 15:47

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements UI enhancements across various app surfaces, including semantic badges and responsive layouts. While the core features are present, there are critical implementation risks: social media meta tags use relative URLs which will break previews, and the badge logic for 'hold' actions deviates from the design specification. Additionally, while the relativeTime utility is functional, it lacks a transition to absolute dates for older records, potentially reducing readability for long-term audit logs.

A significant concern is the lack of automated testing for the new responsive 'stack-table' transformation and the development seeding script. Furthermore, the PR description is empty, which hinders documentation and review context. Several CSS style violations and opportunities for modernization (Media Queries Level 4) were also identified.

About this PR

  • The PR description is empty. Please provide a brief summary of the changes and the motivation behind them to assist with review and future maintainability.
  • The development seeding script (scripts/seed-dev.mjs) lacks automated tests. Consider adding basic validation to ensure it remains compatible with the database schema as the project evolves.

Test suggestions

  • relativeTime utility correctly calculates buckets (minutes, hours, days, weeks) and handles pluralization
  • relativeTime utility returns the input string unchanged when provided with unparseable date strings
  • Review queue displays an inline confirmation prompt before submitting a 'Delete' or 'Ban' POST request
  • Review queue actions (approve, reject, etc.) return and display success feedback messages
  • Audit log table transforms into a labeled card-like stack layout on screens narrower than 760px
  • Seeding script correctly inserts and resets all database tables for the demo channel via CLI
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Audit log table transforms into a labeled card-like stack layout on screens narrower than 760px
2. Seeding script correctly inserts and resets all database tables for the demo channel via CLI

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread src/routes/+page.svelte
Comment on lines +97 to +98
<meta property="og:image" content="/og.png" />
<meta name="twitter:image" content="/og.png" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM RISK

OpenGraph and Twitter image meta tags require absolute URLs. Use a deploy-time environment variable to prefix the origin (e.g., https://example.com/og.png) to ensure social media previews work correctly.

Comment thread src/app.css

/* stack-table: on narrow screens rows become labeled cards instead of
overflowing columns (audit log) */
@media (max-width: 760px) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM RISK

Suggestion: Modernize the media query using context range notation (width <= 760px). This is part of the Media Queries Level 4 spec and provides better readability than the legacy prefix-based syntax.

This might be a simple fix:

Suggested change
@media (max-width: 760px) {
@media (width <= 760px) {

See Issue in Codacy

Comment thread src/lib/relative-time.ts
if (diff < HOUR) return plural(Math.floor(diff / MINUTE), 'minute');
if (diff < DAY) return plural(Math.floor(diff / HOUR), 'hour');
if (diff < WEEK) return plural(Math.floor(diff / DAY), 'day');
return plural(Math.floor(diff / WEEK), 'week');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚪ LOW RISK

Suggestion: The utility should ideally switch to absolute date strings (e.g., 'Jan 1, 2024') for dates older than 4-8 weeks to maintain better readability in the audit log.

Comment thread src/app.css
@media (max-width: 760px) {
.stack-table thead { display: none; }
.stack-table, .stack-table tbody, .stack-table tr, .stack-table td { display: block; width: 100%; }
.stack-table tr { border-bottom: 1px solid var(--border); padding: 8px 0; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚪ LOW RISK

Nitpick: Expand the declaration block to multiple lines to improve readability and comply with the project's style enforcement.

This might be a simple fix:

Suggested change
.stack-table tr { border-bottom: 1px solid var(--border); padding: 8px 0; }
.stack-table tr {
border-bottom: 1px solid var(--border);
padding: 8px 0;
}

See Issue in Codacy

Comment thread src/app.css
.badge.neutral { background: #f1edf5; color: var(--ink-2); }
.badge.ok { background: var(--brand-soft); color: var(--brand); }
.badge.danger { background: var(--danger-soft); color: var(--danger); }
/* attention = needs a human decision; danger = a destructive action was taken.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚪ LOW RISK

Nitpick: Add an empty line before this comment to separate it from the preceding CSS rule.

This might be a simple fix:

Suggested change
/* attention = needs a human decision; danger = a destructive action was taken.
/* attention = needs a human decision; danger = a destructive action was taken.

See Issue in Codacy

@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Latest suggestions up to commit 8ae28df
CategorySuggestion                                                                                                                                    Severity
Logic error
Rejecting a comment remains an unconfirmed destructive action

The Reject form still submits immediately, while the corresponding server action
makes a final local state change and, outside DRY_RUN, calls YouTube to set the
comment status to rejected. This leaves a destructive moderation action unprotected
by the confirmation flow applied to Delete and Ban, so a mistaken click can
permanently reject a comment without confirmation.

src/routes/(app)/channels/[id]/queue/+page.svelte [72-75]

Why it matters? 🤔
  • ❌ Mistaken Reject clicks can permanently reject comments.
  • ❌ Non-dry-run rejection changes YouTube moderation state immediately.
  • ⚠️ Reject lacks the confirmation protection used by Delete and Ban.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/routes/(app)/channels/[id]/queue/+page.svelte
**Line:** 72:75
**Comment:**
	*Logic Error: The Reject form still submits immediately, while the corresponding server action makes a final local state change and, outside `DRY_RUN`, calls YouTube to set the comment status to rejected. This leaves a destructive moderation action unprotected by the confirmation flow applied to Delete and Ban, so a mistaken click can permanently reject a comment without confirmation.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Critical
Api mismatch
The dashboard displays the newest comment timestamp as the polling time

The dashboard labels ch.cursor as the last check time, but this field stores the
timestamp of the newest comment seen, not the time of the latest polling run. A
channel can therefore display a recent “last checked” value even when the cron job
has not run recently. Use the channel's lastRunAt field for this label, and expose
it from the loader.

src/routes/(app)/dashboard/+page.svelte [61]

Why it matters? 🤔
  • ❌ Dashboard reports comment freshness as polling freshness.
  • ⚠️ Users cannot identify stale cron processing reliably.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/routes/(app)/dashboard/+page.svelte
**Line:** 61:61
**Comment:**
	*Api Mismatch: The dashboard labels `ch.cursor` as the last check time, but this field stores the timestamp of the newest comment seen, not the time of the latest polling run. A channel can therefore display a recent “last checked” value even when the cron job has not run recently. Use the channel's `lastRunAt` field for this label, and expose it from the loader.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Critical
Time/date
Future timestamps are incorrectly rendered as “just now” instead of being identified as future-dated

Future timestamps produce a negative diff, which satisfies diff < MINUTE and is
displayed as “just now.” This hides clock skew or future-dated comment, audit, and
cursor timestamps; handle negative differences separately by returning the raw value
or an explicit future-time label.

src/lib/relative-time.ts [32-33]

Why it matters? 🤔
  • ⚠️ Dashboard cursor timestamps can hide clock skew.
  • ⚠️ Review queue comments can show misleading recency.
  • ⚠️ Audit log entries can appear newer than their timestamps.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/lib/relative-time.ts
**Line:** 32:33
**Comment:**
	*Time Date: Future timestamps produce a negative `diff`, which satisfies `diff < MINUTE` and is displayed as “just now.” This hides clock skew or future-dated comment, audit, and cursor timestamps; handle negative differences separately by returning the raw value or an explicit future-time label.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major
Incomplete implementation
Non-atomic seed reset can leave inconsistent partial demo data after a failure

The reset performs multiple independent deletes without a transaction. If the
process or database fails after one delete, the demo data is left partially reset; a
later seed can then fail because the channel may still exist or related rows may be
missing. Wrap the complete reset operation in a transaction, and apply the same
atomicity to the multi-step seed inserts.

scripts/seed-dev.mjs [58-68]

Why it matters? 🤔
  • ⚠️ Local demo reset can leave partially deleted rows.
  • ⚠️ Reseeding may fail until inconsistent data is manually repaired.
  • ⚠️ Multi-step seed inserts can similarly leave incomplete demo data.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** scripts/seed-dev.mjs
**Line:** 58:68
**Comment:**
	*Incomplete Implementation: The reset performs multiple independent deletes without a transaction. If the process or database fails after one delete, the demo data is left partially reset; a later seed can then fail because the channel may still exist or related rows may be missing. Wrap the complete reset operation in a transaction, and apply the same atomicity to the multi-step seed inserts.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major
Dashboard activity totals omit comments held by moderation rules

Rule-based holds are stored with status held, but the dashboard renders only
pending, rejected, deleted, and approved counts. Consequently, all comments held by
a moderation rule are omitted from the displayed activity totals, leaving users
unable to see that those comments exist from the dashboard.

src/routes/(app)/dashboard/+page.svelte [52-55]

Why it matters? 🤔
  • ❌ Rule-held comments disappear from dashboard activity totals.
  • ⚠️ Users receive an incomplete view of moderation outcomes.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/routes/(app)/dashboard/+page.svelte
**Line:** 52:55
**Comment:**
	*Incomplete Implementation: Rule-based holds are stored with status `held`, but the dashboard renders only `pending`, `rejected`, `deleted`, and `approved` counts. Consequently, all comments held by a moderation rule are omitted from the displayed activity totals, leaving users unable to see that those comments exist from the dashboard.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major
Incorrect condition logic
Held audit entries are incorrectly rendered with neutral styling instead of attention styling

The attention classification omits the valid hold audit action. Held comments
therefore fall through to badge neutral, so entries that require human review are
not styled as attention items. Include hold in the attention condition.

src/routes/(app)/channels/[id]/log/+page.svelte [29]

Why it matters? 🤔
  • ⚠️ Held comments lose their human-review visual indicator.
  • ⚠️ Audit-log users may overlook comments requiring attention.
  • ⚠️ Seeded hold entries also render with neutral styling.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/routes/(app)/channels/[id]/log/+page.svelte
**Line:** 29:29
**Comment:**
	*Incorrect Condition Logic: The attention classification omits the valid `hold` audit action. Held comments therefore fall through to `badge neutral`, so entries that require human review are not styled as attention items. Include `hold` in the attention condition.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Polish app surfaces: badge semantics, relative time, mobile log, dev seed, OG card

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Refine moderation UI semantics (attention vs danger), per-channel stats, and relative timestamps.
• Add inline confirmations + success flashes for destructive queue actions; stack audit log on
 mobile.
• Add local-only dev seed script and improve landing share preview (og:image, font preloads).
Diagram

graph TD
U(("User")) --> D["Dashboard"] --> RT["relativeTime()"]
U --> Q["Review queue"] --> DB[("Local DB")]
U --> L["Audit log"] --> RT
Q --> RT
SD["Dev seed script"] --> DB
U --> M["Landing/SEO meta"]
subgraph Legend
direction LR
_actor(("User")) ~~~ _mod["Module/Page"] ~~~ _db[("Database")]
end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Intl.RelativeTimeFormat for relative timestamps
  • ➕ Locale-aware formatting and pluralization
  • ➕ Less custom logic to maintain
  • ➖ Still needs bucketing logic (minute/hour/day/week) and ‘just now’ special case
  • ➖ Output strings may vary across runtimes/locales, making snapshot-style tests trickier
2. Seed demo data via SQL fixture files instead of JS script
  • ➕ Easier to review the exact rows written and rerun idempotently
  • ➕ Can be wired into local dev bootstrap with fewer runtime dependencies
  • ➖ Harder to embed guardrails like “refuse non-local URL” checks
  • ➖ Less flexible for computed timestamps and cross-table inserts without scripting
3. Centralize badge semantics into a shared mapping (constants/component)
  • ➕ Avoids drift between dashboard/log/queue styling over time
  • ➕ Makes semantics (‘attention’ vs ‘danger’) enforceable in one place
  • ➖ Slightly more abstraction for a small app; may be premature if only a few call sites

Recommendation: The PR’s approach is solid for the current scale: a tiny relativeTime() helper keeps UI consistent and testable; the seed script’s explicit local-DB guardrails reduce risk; and the badge semantics are clarified end-to-end (docs + CSS + usage). The main follow-up to consider is ensuring og:image becomes an absolute URL at deploy time (as noted inline), since many crawlers won’t resolve relative OG image paths reliably.

Files changed (14) +403 / -39 · 1 not counted

Enhancement (9) +155 / -36
app.cssAdd attention badge + dashboard stats row + mobile-stacked tables +32/-0

Add attention badge + dashboard stats row + mobile-stacked tables

• Adds '.badge.attention' (same palette as danger, different meaning) and a '.stats' list layout for per-channel counts. Implements '.stack-table' responsive behavior to convert audit log rows into labeled cards on narrow screens.

src/app.css

app.htmlPreload display fonts +2/-0

Preload display fonts

• Preloads the Saira Condensed and Saira Stencil One font files to reduce layout shift and speed up initial render.

src/app.html

relative-time.tsAdd relativeTime() utility for “N units ago” strings +38/-0

Add relativeTime() utility for “N units ago” strings

• Implements a small formatter for ISO timestamps with minute/hour/day/week buckets and a ‘just now’ case. Safely falls back to returning the original string if parsing fails.

src/lib/relative-time.ts

+page.svelteUse relative times, new badge semantics, and mobile-stacked log table +10/-9

Use relative times, new badge semantics, and mobile-stacked log table

• Updates action-to-badge mapping so queue/pending are ‘attention’ and dry-run is neutral while destructive actions remain danger. Renders created-at as relative time with the full ISO timestamp in the title attribute and adds 'data-label' fields for stacked-table mobile layout.

src/routes/(app)/channels/[id]/log/+page.svelte

+page.server.tsReturn success messages from moderation actions +4/-0

Return success messages from moderation actions

• Each queue action now returns a user-facing success string after completing the moderation action and audit logging. Enables the UI to announce completed actions without guessing state.

src/routes/(app)/channels/[id]/queue/+page.server.ts

+page.svelteInline destructive confirmations + status flash + relative timestamps +45/-19

Inline destructive confirmations + status flash + relative timestamps

• Adds inline confirmation prompts for delete/ban that include the author name, replacing immediate destructive submits. Displays a 'role="status"' flash message for successful actions, and formats published times via 'relativeTime()' while keeping the ISO string in a tooltip.

src/routes/(app)/channels/[id]/queue/+page.svelte

+page.svelteAdd per-channel status line, stats list, and relative last-checked time +20/-7

Add per-channel status line, stats list, and relative last-checked time

• Introduces a per-channel “waiting for review” line with a direct queue link when pending > 0, and moves counts into a '.stats' list with updated badge semantics (pending=attention, rejected/deleted=neutral). Displays ‘last checked’ using 'relativeTime()' and moves the connect button to the bottom of the page.

src/routes/(app)/dashboard/+page.svelte

+page.svelteAdd OG/Twitter large image meta tags +4/-1

Add OG/Twitter large image meta tags

• Switches Twitter card to 'summary_large_image' and adds 'og:image' / 'twitter:image' pointing at '/og.png' (with a note that deploy should make it absolute).

src/routes/+page.svelte

og.pngAdd social share image asset not counted

Add social share image asset

• Adds the 'og.png' image used by OpenGraph/Twitter meta tags for richer link previews.

static/og.png

Tests (3) +52 / -2
relative-time.test.tsAdd unit tests for relative time formatting +36/-0

Add unit tests for relative time formatting

• Adds Vitest coverage for the time-bucket outputs and pluralization, plus a safety test ensuring unparseable inputs return unchanged.

src/lib/relative-time.test.ts

actions.test.tsAssert action success payloads +4/-2

Assert action success payloads

• Updates queue action tests to validate the returned success messages for approve/reject flows (including DRY_RUN behavior).

src/routes/(app)/channels/[id]/queue/actions.test.ts

page-states.test.tsAdd UI-state assertions for confirms + status announcements +12/-0

Add UI-state assertions for confirms + status announcements

• Extends the queue page state tests to ensure destructive actions are confirmed inline and that successful actions are announced with a status flash region.

src/routes/(app)/channels/[id]/queue/page-states.test.ts

Documentation (1) +2 / -1
DESIGN.mdClarify badge semantics (attention vs danger) +2/-1

Clarify badge semantics (attention vs danger)

• Expands the badge variant guidance to introduce an explicit “attention” semantic distinct from “danger”. Documents when to use crimson for pending/needs-decision vs destructive actions already taken.

DESIGN.md

Other (1) +194 / -0
seed-dev.mjsAdd local-only dev seed script for populated walkthrough data +194/-0

Add local-only dev seed script for populated walkthrough data

• Introduces a CLI script that seeds a deterministic demo channel and related rows (rules, comments, moderation actions, audit log). Includes safety checks to refuse non-local DB URLs and a --reset mode to delete only demo-tied rows.

scripts/seed-dev.mjs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/seed-dev.mjs`:
- Around line 58-68: Wrap the reset branch and the subsequent seeding workflow
in a single database write transaction, ensuring all DELETE and INSERT
operations commit together or roll back on any failure. Update the control flow
around the --reset handling and seed operations to use the transaction API,
preserving the existing success message and exit behavior after a committed
reset.

In `@src/app.css`:
- Around line 254-260: Update the .stack-table thead rule to visually hide the
header without using display: none, preserving it in the accessibility tree
while keeping it invisible visually. Leave the existing mobile table layout and
td::before data-label behavior unchanged.

In `@src/routes/`(app)/channels/[id]/queue/+page.server.ts:
- Around line 90-108: Update the reject, del, and ban actions around act so
their success responses use dry-run-specific wording when DRY_RUN is enabled,
accurately indicating that no remote moderation action occurred, while
preserving the existing messages for real executions. Extend the dry-run action
tests to cover delete and ban in addition to reject.

In `@src/routes/`+page.svelte:
- Around line 95-98: Update the og:image and twitter:image meta tags in the page
head to use the deployed origin combined with /og.png, ensuring both social
image URLs are absolute in production. Reuse the existing origin or site-URL
configuration if available rather than hardcoding a host.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 883fb5c2-c617-4a3d-8c9b-d78703873681

📥 Commits

Reviewing files that changed from the base of the PR and between 7f010a1 and 8ae28df.

⛔ Files ignored due to path filters (1)
  • static/og.png is excluded by !**/*.png
📒 Files selected for processing (13)
  • DESIGN.md
  • scripts/seed-dev.mjs
  • src/app.css
  • src/app.html
  • src/lib/relative-time.test.ts
  • src/lib/relative-time.ts
  • src/routes/(app)/channels/[id]/log/+page.svelte
  • src/routes/(app)/channels/[id]/queue/+page.server.ts
  • src/routes/(app)/channels/[id]/queue/+page.svelte
  • src/routes/(app)/channels/[id]/queue/actions.test.ts
  • src/routes/(app)/channels/[id]/queue/page-states.test.ts
  • src/routes/(app)/dashboard/+page.svelte
  • src/routes/+page.svelte

Comment thread scripts/seed-dev.mjs
Comment on lines +58 to +68
if (process.argv.includes('--reset')) {
await client.execute({ sql: 'DELETE FROM audit_log WHERE channel_id = ?', args: [CHANNEL_ID] });
await client.execute({
sql: 'DELETE FROM moderation_actions WHERE channel_id = ?',
args: [CHANNEL_ID]
});
await client.execute({ sql: 'DELETE FROM comments WHERE channel_id = ?', args: [CHANNEL_ID] });
await client.execute({ sql: 'DELETE FROM rules WHERE channel_id = ?', args: [CHANNEL_ID] });
await client.execute({ sql: 'DELETE FROM channels WHERE id = ?', args: [CHANNEL_ID] });
console.log(`Removed all demo rows for ${CHANNEL_ID}.`);
process.exit(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

tarball="$(npm view '`@libsql/client`@0.17.4' dist.tarball)"
curl -fsSL "$tarball" -o "$tmp/client.tgz"
tar -xzf "$tmp/client.tgz" -C "$tmp"

rg -n -C 3 'transaction\s*\(|batch\s*\(' "$tmp/package"

Repository: Bonobo791/Moderaty

Length of output: 23863


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

git ls-files scripts/seed-dev.mjs package.json
echo '--- scripts/seed-dev.mjs ---'
cat -n scripts/seed-dev.mjs | sed -n '1,260p'
echo '--- package.json ---'
cat -n package.json | sed -n '1,220p'

Repository: Bonobo791/Moderaty

Length of output: 11083


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

cat -n scripts/seed-dev.mjs | sed -n '1,260p'

Repository: Bonobo791/Moderaty

Length of output: 9872


🌐 Web query:

@libsql/client transaction file: :memory: docs

💡 Result:

In @libsql/client, using interactive transactions with an in-memory database (:memory:) has historically faced challenges due to how the client manages database connections [1][2][3]. When using the standard:memory: URL, older versions of the library would inadvertently reset the database connection when starting a transaction, causing all previously created tables and data to be lost [1][2][4]. Current Status and Best Practices: 1. Recommended Approach: If you encounter issues where your:memory: database appears empty or throws errors like SQLITE_ERROR: no such table after starting a transaction, you should use a shared cache URI. Configure your client with the following URL: file::memory:?cache=shared [3] 2. Library Updates: Recent updates (e.g., PR #342) have been introduced to address this by keeping the connection handle alive across transactions for in-memory databases [4]. Ensure you are using the latest version of @libsql/client to benefit from these fixes [4]. General Transaction Usage: To perform an interactive transaction, use the client.transaction method [5][6]. It is best practice to wrap the transaction in a try...finally block to ensure it is closed properly [5][6]. Example: const transaction = await client.transaction("write"); try { await transaction.execute("INSERT INTO books (name) VALUES (?)", ["Pride and Prejudice"]); await transaction.commit; } finally { transaction.close; } The transaction method accepts an optional mode ("write", "read", or "deferred") [6][7]. For transactions involving writes, "write" is generally recommended [7]. Always ensure that you call.commit or.rollback and subsequently.close to release resources [5][6].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

tarball="$(npm view '`@libsql/client`@0.17.4' dist.tarball)"
curl -fsSL "$tarball" -o "$tmp/client.tgz"
tar -xzf "$tmp/client.tgz" -C "$tmp"

echo '--- sqlite3 transaction implementation ---'
sed -n '145,220p' "$tmp/package/lib-esm/sqlite3.js"
echo '--- transaction class / close / commit / rollback ---'
sed -n '1,120p' "$tmp/package/lib-esm/sqlite3.js"

Repository: Bonobo791/Moderaty

Length of output: 7527


Wrap reset and seeding in one write transaction. A mid-run failure can leave partial demo rows behind, and a failed reset can leave the database half-cleared, which then breaks the next seed run.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/seed-dev.mjs` around lines 58 - 68, Wrap the reset branch and the
subsequent seeding workflow in a single database write transaction, ensuring all
DELETE and INSERT operations commit together or roll back on any failure. Update
the control flow around the --reset handling and seed operations to use the
transaction API, preserving the existing success message and exit behavior after
a committed reset.

Comment thread src/app.css
Comment on lines +254 to +260
.stack-table thead { display: none; }
.stack-table, .stack-table tbody, .stack-table tr, .stack-table td { display: block; width: 100%; }
.stack-table tr { border-bottom: 1px solid var(--border); padding: 8px 0; }
.stack-table tbody tr:last-child { border-bottom: 0; }
.stack-table td { border-bottom: 0; padding: 3px 0; }
.stack-table td::before {
content: attr(data-label);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep table headers available to assistive technology.

display: none removes the <thead> from the accessibility tree. The data-label pseudo-content is visual only, so screen-reader users lose the Time/Action/Comment column context on mobile. Visually hide the header instead of removing it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app.css` around lines 254 - 260, Update the .stack-table thead rule to
visually hide the header without using display: none, preserving it in the
accessibility tree while keeping it invisible visually. Leave the existing
mobile table layout and td::before data-label behavior unchanged.

Comment on lines +90 to +108
return { success: 'Approved — recorded in audit log.' };
},
reject: async ({ params, request }) => {
const commentId = commentIdFrom(await request.formData());
if (!commentId) return fail(400, { error: 'Invalid comment ID' });
await act(params.id, commentId, 'reject');
return { success: 'Rejected — recorded in audit log.' };
},
del: async ({ params, request }) => {
const commentId = commentIdFrom(await request.formData());
if (!commentId) return fail(400, { error: 'Invalid comment ID' });
await act(params.id, commentId, 'delete');
return { success: 'Deleted — recorded in audit log.' };
},
ban: async ({ params, request }) => {
const commentId = commentIdFrom(await request.formData());
if (!commentId) return fail(400, { error: 'Invalid comment ID' });
await act(params.id, commentId, 'ban');
return { success: 'Author banned — recorded in audit log.' };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make success messages truthful in dry-run mode.

act skips YouTube reject/delete/ban calls under DRY_RUN, but these new messages report completed destructive actions. Return dry-run-specific copy (and cover delete/ban in the dry-run action tests) so operators do not mistake a local simulation for a remote moderation result.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/`(app)/channels/[id]/queue/+page.server.ts around lines 90 - 108,
Update the reject, del, and ban actions around act so their success responses
use dry-run-specific wording when DRY_RUN is enabled, accurately indicating that
no remote moderation action occurred, while preserving the existing messages for
real executions. Extend the dry-run action tests to cover delete and ban in
addition to reject.

Comment thread src/routes/+page.svelte
Comment on lines +95 to +98
<meta name="twitter:card" content="summary_large_image" />
<!-- relative for now — must become an absolute URL at deploy time -->
<meta property="og:image" content="/og.png" />
<meta name="twitter:image" content="/og.png" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== File outline ==\n'
ast-grep outline src/routes/+page.svelte --view expanded || true

printf '\n== Relevant lines in src/routes/+page.svelte ==\n'
nl -ba src/routes/+page.svelte | sed -n '80,110p'

printf '\n== Search for og:image / twitter:image / origin handling ==\n'
rg -n --hidden -S 'og:image|twitter:image|siteUrl|origin|PUBLIC_.*URL|absolute URL|/og\.png|twitter:card' src . 2>/dev/null | sed -n '1,200p'

Repository: Bonobo791/Moderaty

Length of output: 279


🏁 Script executed:

#!/bin/bash
set -euo pipefail
nl -ba src/routes/+page.svelte | sed -n '88,105p'

Repository: Bonobo791/Moderaty

Length of output: 196


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== src/routes/+page.svelte (88-105) ==\n'
sed -n '88,105p' src/routes/+page.svelte

printf '\n== Search for social image / origin handling ==\n'
rg -n -S 'og:image|twitter:image|twitter:card|/og\.png|siteUrl|origin|PUBLIC_.*URL|APP_.*URL|canonical|absolute URL' src . --hidden 2>/dev/null | sed -n '1,240p'

printf '\n== Candidate config files ==\n'
rg -n --files . | rg '(^|/)(svelte\.config\.|vite\.config\.|app\.|hooks\.|env\.)|package\.json|README\.md$'

Repository: Bonobo791/Moderaty

Length of output: 5822


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== README APP_URL context ==\n'
sed -n '1,40p' README.md

printf '\n== Search for public env usage ==\n'
rg -n -S '\$env/dynamic/public|\$env/static/public|APP_URL|PUBLIC_' src README.md .env.example svelte.config.js vite.config.ts package.json 2>/dev/null | sed -n '1,220p'

printf '\n== Relevant app config ==\n'
sed -n '1,220p' svelte.config.js
printf '\n---\n'
sed -n '1,220p' vite.config.ts

Repository: Bonobo791/Moderaty

Length of output: 5800


Use an absolute production URL for the social image. /og.png is still relative here, so some social crawlers may skip the preview. Build both image tags from the deployed origin instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/`+page.svelte around lines 95 - 98, Update the og:image and
twitter:image meta tags in the page head to use the deployed origin combined
with /og.png, ensuring both social image URLs are absolute in production. Reuse
the existing origin or site-URL configuration if available rather than
hardcoding a host.

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 48 rules

Grey Divider


Remediation recommended

1. relativeTime imported via ./ 📘 Rule violation ⚙ Maintainability
Description
src/lib/relative-time.test.ts imports a src/lib module using a relative path (./relative-time)
instead of the $lib alias. This violates the requirement to use $lib for any imports that
resolve into src/lib, reducing consistency and increasing refactor risk.
Code

src/lib/relative-time.test.ts[20]

+import { relativeTime } from './relative-time';
Relevance

●●● Strong

Repo previously accepted switching src/lib imports from relative paths to $lib alias for
consistency.

PR-#2

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2401155 requires that imports whose targets are under src/lib use the $lib
alias rather than .//../ paths. The added test file imports relativeTime from
./relative-time, which resolves into src/lib/relative-time.ts, violating the rule.

Rule 2401155: Use $lib alias instead of relative paths for src/lib imports
src/lib/relative-time.test.ts[19-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new test file under `src/lib/` imports another `src/lib` module via a relative path (`./relative-time`). Compliance requires using the `$lib` alias for any imports that resolve into `src/lib`.

## Issue Context
This impacts maintainability and consistency across the codebase.

## Fix Focus Areas
- src/lib/relative-time.test.ts[19-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Relative OG image URL 🐞 Bug ≡ Correctness
Description
The landing page sets og:image/twitter:image to a root-relative /og.png, which is not reliably
resolved by social crawlers and can result in missing preview images in production.
Code

src/routes/+page.svelte[R95-98]

+	<meta name="twitter:card" content="summary_large_image" />
+	<!-- relative for now — must become an absolute URL at deploy time -->
+	<meta property="og:image" content="/og.png" />
+	<meta name="twitter:image" content="/og.png" />
Relevance

●● Moderate

No close precedent on OG image URLs; team does use absolute URL construction patterns elsewhere, but
may defer.

PR-#4

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR introduces OpenGraph/Twitter image tags using a relative path; crawlers may not resolve these
consistently, causing missing preview images.

src/routes/+page.svelte[83-99]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`og:image` and `twitter:image` are set to `/og.png` (relative). Some social preview systems require or more reliably handle absolute URLs, so previews may render without the image.

### Issue Context
The file `static/og.png` exists and is referenced from the landing page metadata.

### Fix Focus Areas
- src/routes/+page.svelte[83-99]

### Suggested fix
Use an absolute URL at render time (SSR) by prefixing with the request origin (e.g., via `$page.url.origin`) or by passing an explicit `siteOrigin` from a server load/env var, then set:
- `content={`${origin}/og.png`}` for both `og:image` and `twitter:image`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Hold badge misclassified 🐞 Bug ≡ Correctness
Description
badgeClass() on the audit log page does not treat the hold action as an “attention” state, so
system ‘held for review’ audit rows render as neutral despite the new semantics defining attention
as “a human decision is needed.”
Code

src/routes/(app)/channels/[id]/log/+page.svelte[R26-33]

	function badgeClass(action: string): string {
		if (action === 'approve' || action === 'approved') return 'badge ok';
-		if (action === 'queue' || action === 'pending') return 'badge';
-		if (['rejected', 'deleted', 'reject', 'delete', 'ban', 'dry-run'].includes(action))
+		if (action === 'queue' || action === 'pending') return 'badge attention';
+		if (action === 'dry-run') return 'badge neutral';
+		if (['rejected', 'deleted', 'reject', 'delete', 'ban'].includes(action))
			return 'badge danger';
		return 'badge neutral';
Relevance

● Weak

Same request to map audit-log hold to a non-neutral badge was previously rejected in this file.

PR-#7

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The design doc defines attention as “human decision needed,” the backend emits audit-log entries
with action hold, and the UI badge mapping currently doesn’t include hold, so those rows won’t
receive the attention styling.

DESIGN.md[271-274]
src/routes/(app)/channels/[id]/log/+page.svelte[21-34]
src/lib/server/pipeline.ts[259-307]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The audit log’s `badgeClass()` maps `queue`/`pending` to `badge attention` but omits `hold`. Since the system emits audit-log rows with action `hold` (held-for-review), those rows render with the neutral badge instead of the “needs a human decision” tone.

### Issue Context
Design semantics explicitly define **attention = a human decision is needed** for queued items. The backend pipeline writes audit log rows using action values including `hold`.

### Fix Focus Areas
- src/routes/(app)/channels/[id]/log/+page.svelte[21-34]

### Suggested fix
Update `badgeClass()` to classify `hold` (and optionally `held` if it appears) as attention, e.g.:
```ts
if (action === 'queue' || action === 'pending' || action === 'hold') return 'badge attention';
```
Consider adding a small unit/test assertion that `badgeClass('hold')` yields attention (or an equivalent snapshot/DOM expectation).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

// Commercial licensing: contact@marketingprowess.simplelogin.com — see COMMERCIAL.md

import { describe, expect, it } from 'vitest';
import { relativeTime } from './relative-time';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. relativetime imported via ./ 📘 Rule violation ⚙ Maintainability

src/lib/relative-time.test.ts imports a src/lib module using a relative path (./relative-time)
instead of the $lib alias. This violates the requirement to use $lib for any imports that
resolve into src/lib, reducing consistency and increasing refactor risk.
Agent Prompt
## Issue description
A new test file under `src/lib/` imports another `src/lib` module via a relative path (`./relative-time`). Compliance requires using the `$lib` alias for any imports that resolve into `src/lib`.

## Issue Context
This impacts maintainability and consistency across the codebase.

## Fix Focus Areas
- src/lib/relative-time.test.ts[19-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/routes/+page.svelte
Comment on lines +95 to +98
<meta name="twitter:card" content="summary_large_image" />
<!-- relative for now — must become an absolute URL at deploy time -->
<meta property="og:image" content="/og.png" />
<meta name="twitter:image" content="/og.png" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Relative og image url 🐞 Bug ≡ Correctness

The landing page sets og:image/twitter:image to a root-relative /og.png, which is not reliably
resolved by social crawlers and can result in missing preview images in production.
Agent Prompt
### Issue description
`og:image` and `twitter:image` are set to `/og.png` (relative). Some social preview systems require or more reliably handle absolute URLs, so previews may render without the image.

### Issue Context
The file `static/og.png` exists and is referenced from the landing page metadata.

### Fix Focus Areas
- src/routes/+page.svelte[83-99]

### Suggested fix
Use an absolute URL at render time (SSR) by prefixing with the request origin (e.g., via `$page.url.origin`) or by passing an explicit `siteOrigin` from a server load/env var, then set:
- `content={`${origin}/og.png`}` for both `og:image` and `twitter:image`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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

Labels

cla-signed size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant