chore: tasks rework#88
Conversation
WalkthroughAdds a configurable AI daily report prompt to runtime config and .env example. Introduces production-only guards with logging across several scheduled tasks, skipping execution outside production. The daily-report task now uses the configurable prompt. No public API surface changes beyond the new config key. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Cron as Scheduler
participant Task as Task Runner
participant Env as process.env
participant Log as Logger
Cron->>Task: run()
Task->>Env: Read NODE_ENV
alt NODE_ENV !== "production"
Task->>Log: info("Skipping task in non-production environment")
Task-->>Cron: { result: true }
else production
Task->>Task: Execute existing task logic
Task-->>Cron: { result: true }
end
sequenceDiagram
autonumber
actor Cron as Scheduler
participant Daily as daily-report.ts
participant Config as runtimeConfig.ai
participant OpenAI as AI Client
participant TG as Telegram
Cron->>Daily: run()
Daily->>Config: get dailyReportPrompt
Daily->>Daily: collect completed tasks
Daily->>OpenAI: create message (system: dailyReportPrompt)
OpenAI-->>Daily: response
Daily->>TG: send final message
Daily-->>Cron: { result: true }
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web-app/server/tasks/kitchen/revenue-update.ts (1)
21-31: Critical Fix Required: Compute True UTC Week Bounds & Confirm Inclusive End Date
- The current code’s comment “use UTC+0 timezone” is misleading. date-fns’ startOfWeek/endOfWeek always operate in the system’s local time zone; shifting the Date by getTimezoneOffset doesn’t change that (runebook.dev).
- The repository method listRevenuesByKitchenForPeriod uses a
<= date(end)comparison, so the end date is already treated as inclusive (en.wikipedia.org). There’s no need to push the boundary to the next Monday for exclusivity.Apply the following diff to compute exact UTC week boundaries and simplify end-date handling:
- // From this monday to sunday (use UTC+0 time zone) - const now = new Date() - const utcNow = new Date(now.getTime() + now.getTimezoneOffset() * 60000) - - const thisMonday = startOfWeek(utcNow, { weekStartsOn: 1 }) - const thisSunday = endOfWeek(utcNow, { weekStartsOn: 1 }) - - // Previous week - const utcWeekAgo = new Date(utcNow.getTime() - 7 * 24 * 60 * 60 * 1000) - const prevMonday = startOfWeek(utcWeekAgo, { weekStartsOn: 1 }) - const prevSunday = endOfWeek(utcWeekAgo, { weekStartsOn: 1 }) + // From this Monday to Sunday (UTC) + const now = new Date() + const day = (now.getUTCDay() + 6) % 7 // Mon=0 … Sun=6 + const thisMonday = new Date(Date.UTC( + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate() - day, + 0, 0, 0, 0 + )) + const thisSunday = new Date(Date.UTC( + thisMonday.getUTCFullYear(), + thisMonday.getUTCMonth(), + thisMonday.getUTCDate() + 6, + 23, 59, 59, 999 + )) + + // Previous week (UTC) + const prevMonday = new Date(Date.UTC( + thisMonday.getUTCFullYear(), + thisMonday.getUTCMonth(), + thisMonday.getUTCDate() - 7, + 0, 0, 0, 0 + )) + const prevSunday = new Date(Date.UTC( + prevMonday.getUTCFullYear(), + prevMonday.getUTCMonth(), + prevMonday.getUTCDate() + 6, + 23, 59, 59, 999 + ))apps/web-app/server/tasks/kitchen/rating-update.ts (1)
21-26: Guard against empty points to avoid “reduce of empty array” and NaN updates.If no points pass the filter, reduce() will throw, and even if it didn’t, dividing by zero would produce NaN.
Apply this diff:
- const points = kitchen.feedbackPoints.filter((point) => point.rating > 0 && point.reviews >= 5) - const rawRating = points.reduce((acc, point) => acc + point.rating, 0) / points.length + const points = kitchen.feedbackPoints.filter((point) => point.rating > 0 && point.reviews >= 5) + if (points.length === 0) { + continue + } + const rawRating = points.reduce((acc, point) => acc + point.rating, 0) / points.lengthConsider weighting by reviews (sum(rating*reviews)/sum(reviews)) if that better reflects quality.
🧹 Nitpick comments (13)
apps/web-app/.env.example (2)
23-23: Fix dotenv-linter UnorderedKey: place the new key before MODEL (keep AI keys alphabetized).The linter expects keys to be ordered. Move NUXT_AI_DAILY_REPORT_PROMPT above NUXT_AI_MODEL to keep the AI block sorted.
Apply this diff within the AI section:
# AI NUXT_AI_API_KEY= NUXT_AI_BASE_URL= +NUXT_AI_DAILY_REPORT_PROMPT= -NUXT_AI_MODEL= -NUXT_AI_MODEL_PRO= -NUXT_AI_SERVICE_TOKEN= +NUXT_AI_MODEL= +NUXT_AI_MODEL_PRO= +NUXT_AI_SERVICE_TOKEN=
23-24: Clarify multiline prompt usage in .env.If the daily-report prompt is multi-line, .env doesn’t handle raw newlines. Consider documenting escaping (e.g., \n) or adding an alternate “_FILE” variant (NUXT_AI_DAILY_REPORT_PROMPT_FILE) that loads the prompt from a file.
I can add a small helper in nuxt.config.ts to read NUXT_AI_DAILY_REPORT_PROMPT_FILE when present and fall back to NUXT_AI_DAILY_REPORT_PROMPT.
apps/web-app/server/tasks/kitchen/revenue-update.ts (2)
34-38: Fetch week and previous-week revenues concurrently per kitchen.Two independent DB calls can be parallelized to reduce latency.
Apply this diff:
- const revenuesThisWeek = await repository.kitchen.listRevenuesByKitchenForPeriod(kitchen.id, thisMonday, thisSunday) - const revenuesPrevWeek = await repository.kitchen.listRevenuesByKitchenForPeriod(kitchen.id, prevMonday, prevSunday) + const [revenuesThisWeek, revenuesPrevWeek] = await Promise.all([ + repository.kitchen.listRevenuesByKitchenForPeriod(kitchen.id, thisMonday, thisSunday), + repository.kitchen.listRevenuesByKitchenForPeriod(kitchen.id, prevMonday, prevSunday), + ])
49-50: Make the update log actionable or switch to debug.The commented logger line won’t help in prod incidents. Either enable it at debug level or remove it.
- // logger.log(`Kitchen ${kitchen.id}: Revenue updated from ${kitchen.revenueForThisWeek} to ${revenueForThisWeek}`) + // logger.debug(`Kitchen ${kitchen.id}: Revenue updated from ${kitchen.revenueForThisWeek} to ${revenueForThisWeek}`)apps/web-app/server/tasks/task/auto-create.ts (3)
31-31: Date uses UTC midnight; verify intended timezone.toISOString().split('T')[0] yields a UTC date. If “days from now” should be local-calendar based, prefer formatting in local tz.
- const date = creator.templateDate ? new Date(new Date().getTime() + Number(creator.templateDate) * 24 * 60 * 60 * 1000).toISOString().split('T')[0] : null + // If the date must be in local calendar (yyyy-MM-dd): + const localNow = new Date(Date.now() + Number(creator.templateDate) * 24 * 60 * 60 * 1000) + const date = creator.templateDate + ? `${localNow.getFullYear()}-${String(localNow.getMonth() + 1).padStart(2, '0')}-${String(localNow.getDate()).padStart(2, '0')}` + : nullAlternatively, use date-fns format(localNow, 'yyyy-MM-dd') if available.
51-71: Naive cron parsing; consider a parser or broaden support.Only exact numeric values or '' are supported; no ranges (1-5), lists (1,3,5), or steps (/5). That can surprise operators.
I can wire up cron-parser (no runtime overhead off-schedule) or extend isCronDue to support common patterns while keeping zero deps.
51-62: Unify language in comments.Comments are partially in Russian; consider English for consistency across the repo.
apps/web-app/server/tasks/kitchen/average-update.ts (2)
13-16: Production guard looks good.Matches the PR pattern. Minor style nit: in this file the guard is inside try/catch while auto-create places it before try. Consider standardizing placement across tasks.
20-33: Potential throughput improvement: batch updates or parallelize per metric.Per-metric DB calls are sequential; if metrics is large, this will be slow.
Sketch:
- Gather all totals/checks first using Promise.all.
- Use a bulk update if repository supports it, otherwise parallelize with a sane concurrency limit (e.g., p-limit 5).
I can provide a p-limit based refactor if desired.
apps/web-app/server/tasks/kitchen/rating-update.ts (1)
34-35: Use consistent log level.Other tasks use info; prefer logger.info for parity and to honor log-level filtering.
- logger.log(`Kitchen ${kitchen.id}: Rating updated from ${kitchen.rating} to ${updatedRating}`) + logger.info(`Kitchen ${kitchen.id}: Rating updated from ${kitchen.rating} to ${updatedRating}`)apps/web-app/server/tasks/ai/daily-report.ts (3)
1-1: Importingprocessis unnecessary in Node/Nitro contexts.
processis globally available; keep it simple and drop the import.Apply this diff:
-import process from 'node:process'
14-17: Production-only guard: prefer lower log level and explicit skip signal.Reduce log noise and make the outcome machine-readable with a
skippedflag.Apply this diff:
- if (process.env.NODE_ENV !== 'production') { - logger.info('Skipping task in non-production environment') - return { result: true } - } + if (process.env.NODE_ENV !== 'production') { + logger.debug(`Skipping ai:daily-report in ${process.env.NODE_ENV} environment`) + return { result: true, skipped: true } + }Confirm that any upstream task runner/metrics ingestion handles an extra
skipped: truefield without breaking dashboards.
35-47: Guard against empty system prompt and add model fallback.If
ai.dailyReportPromptis unset/blank (empty default in config), the system message becomes empty, which degrades output quality. Also, fall back toai.modelwhenai.modelProisn’t configured.Apply this diff:
- const response = await client.chat.completions.create({ - model: ai.modelPro, - messages: [ - { - role: 'system', - content: ai.dailyReportPrompt, - }, - { - role: 'user', - content: JSON.stringify(preparedTasks), - }, - ], - }) + const systemPrompt = (ai.dailyReportPrompt || '').trim() + const messages = [ + ...(systemPrompt ? [{ role: 'system' as const, content: systemPrompt }] : []), + { role: 'user' as const, content: JSON.stringify(preparedTasks) }, + ] + const response = await client.chat.completions.create({ + model: ai.modelPro || ai.model, + messages, + })Optionally, warn when the prompt is missing so it’s visible in logs:
if (!systemPrompt) logger.warn('ai.dailyReportPrompt is empty; sending request without system instruction')
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
apps/web-app/.env.example(1 hunks)apps/web-app/nuxt.config.ts(1 hunks)apps/web-app/server/tasks/ai/daily-report.ts(2 hunks)apps/web-app/server/tasks/kitchen/average-update.ts(1 hunks)apps/web-app/server/tasks/kitchen/rating-update.ts(2 hunks)apps/web-app/server/tasks/kitchen/revenue-update.ts(1 hunks)apps/web-app/server/tasks/task/auto-create.ts(2 hunks)
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
apps/web-app/.env.example
[warning] 23-23: [UnorderedKey] The NUXT_AI_DAILY_REPORT_PROMPT key should go before the NUXT_AI_MODEL key
(UnorderedKey)
⏰ 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: build
🔇 Additional comments (4)
apps/web-app/server/tasks/kitchen/revenue-update.ts (1)
14-17: Production guard is correct and in the right place.Early exit prevents accidental data churn in non-prod. Consistent with the PR’s pattern.
apps/web-app/server/tasks/task/auto-create.ts (1)
12-15: Production guard placement LGTM.Skip happens before any delay or I/O—good.
apps/web-app/server/tasks/kitchen/rating-update.ts (1)
13-16: Production guard is correct.Early exit mirrors other tasks and avoids unnecessary DB work in non-prod.
apps/web-app/server/tasks/ai/daily-report.ts (1)
6-6: Dedicated logger looks good.Naming/tagging the logger for this task improves traceability. No changes needed.
| baseUrl: '', | ||
| apiKey: '', | ||
| serviceToken: '', | ||
| dailyReportPrompt: '', |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Good addition; server-only placement is correct.
Adding ai.dailyReportPrompt under runtimeConfig (server-side) avoids leaking prompt text to the client. One follow-up: because it defaults to an empty string, make sure the task guards against empty/whitespace prompts (see task review).
If not already done, ensure .env.example includes the matching key and casing:
🏁 Script executed:
#!/bin/bash
# Verify example env contains the new key
rg -n --line-number -C1 '^NUXT_AI_DAILY_REPORT_PROMPT=' apps/web-app/.env.exampleLength of output: 145
Verify .env.example and enforce non-empty dailyReportPrompt
- ✅ Confirmed that
NUXT_AI_DAILY_REPORT_PROMPTis defined inapps/web-app/.env.example(line 23). ⚠️ Please add validation aroundruntimeConfig.ai.dailyReportPromptto ensure it isn’t empty or whitespace-only (e.g. throw a clear error or fallback to a safe default) before sending it to the AI.
🤖 Prompt for AI Agents
In apps/web-app/nuxt.config.ts around line 21,
runtimeConfig.ai.dailyReportPrompt is currently allowed to be empty; add a
validation step during config/build initialization to trim the value and ensure
it is not empty or whitespace-only, and if it is invalid either throw a clear
error (with guidance to set NUXT_AI_DAILY_REPORT_PROMPT) or assign a safe
default prompt before any code sends it to the AI; implement the check where
runtimeConfig is constructed so downstream consumers always receive a validated,
non-empty string.



Summary by CodeRabbit