Skip to content

Claude/plan v3 deployment vp mpw#138

Merged
gamblecodezcom merged 24 commits into
mainfrom
claude/plan-v3-deployment-VPMpw
Mar 4, 2026
Merged

Claude/plan v3 deployment vp mpw#138
gamblecodezcom merged 24 commits into
mainfrom
claude/plan-v3-deployment-VPMpw

Conversation

@gamblecodezcom
Copy link
Copy Markdown
Owner

@gamblecodezcom gamblecodezcom commented Mar 4, 2026

User description

Summary by Sourcery

Add a startup guard against drift between registered bot commands and the BOT_KNOWN_COMMANDS registry, introduce a reusable AI agent documentation and bootstrap system, and harden production runtime selection in the deployment script.

Enhancements:

  • Add a startup drift audit that cross-checks Telegraf bot.command registrations against BOT_KNOWN_COMMANDS and surfaces discrepancies via logs and startup warnings.
  • Resolve and reuse the concrete Node.js binary path in the production run script to keep restarts and nohup fallbacks on a consistent, validated runtime.

Deployment:

  • Update the production run script to resolve NODE_BIN once and reuse it across restart fallbacks, reducing runtime skew risk between different process managers.

Documentation:

  • Add a repo-wide AI agent documentation system including an AI contract, CLAUDE operating manual, functionality map and wiring index templates, todolist template, and session log for tracking AI-assisted work.

Chores:

  • Add an idempotent AGENTS/bootstrap.sh script to auto-create and optionally scan required AI documentation artifacts at the start of a session.

CodeAnt-AI Description

Detect missing bot commands at startup, add a repo-wide AI agent documentation system, and fix deploy runtime lookup

What Changed

  • On startup the bot now checks for commands registered in code but missing from the BOT_KNOWN_COMMANDS registry and logs/admin-notifies any discrepancies
  • Adds a new AGENTS documentation system (AI_CONTRACT, CLAUDE manual, index/map templates, session log) and an idempotent bootstrap script to create missing documentation files and optionally scan the codebase
  • Resolves the exact node binary once in prod-run.sh and reports it so nohup/systemd restart fallbacks use the same validated runtime

Impact

✅ Fewer undocumented bot commands at launch
✅ Clearer repo onboarding via auto-created docs and templates
✅ Fewer deployment runtime mismatches during restart

💡 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.

…urity, 23 Block 1 fixes

Menu lifecycle overhaul:
- replyMenu() TTL auto-vanish via ttlMs parameter
- clearStaleMenuIds() on startup (24h threshold)
- mainMenuSentAt / adminMenuSentAt timestamps on user schema
- sendHelpMenu() fixed: bare ctx.reply() → replyMenu() (panel stacking bug)
- replaceCallbackPanel() fallback now tracks message ID in user state
- Admin mode toggle double-fire fixed

Feature upgrades:
- User giveaway list: sendUserGiveawaysPage() 5/page + 2-min auto-vanish
- Admin giveaway panel: activeGiveawaysKeyboard(page) 5/page pagination
- pmenu_referral sub-menu with share deep-link button
- Admin stats: active window indicator + Refresh button
- Admin health panel: inline memory/errors/users/persist-age/uptime
- Broadcast builder: Preview button sends to admin DM before mass send

Block 1 security & logic fixes:
- getStartAppLink(): encodeURIComponent + regex whitelist
- referralShareHTML(): escapeHtml() on code param
- unwrapTelegramUrl(): safe fallback, scheme whitelist, www.t.me support
- getDiscordLink(): null when not configured (no hardcoded fallback)
- evaluatePendingActionTimeout(): >= boundary, NaN guard, no createdAt mutation
- getPlayLink(): legacy user.playMode fallback restored
- Weighted winner pool: splice-after-pick guarantees termination
- deploy_status + logs: exec() → execFile() (no shell spawn)
- SSHV: rejects null bytes, backticks, $( and ${ before exec
- Startup env warnings: ADMIN_IDS, TELEGRAM_CHANNEL_ID, TELEGRAM_GROUP_ID

Centralized helpers: computeParticipantWeight(), getRealGiveaways(), isNewUserPromoEligible()

Metrics: added runewager_menu_stale_recoveries, pending_actions_timed_out, uptime_seconds

load_tooltips.sh: full rewrite — atomic write, --push/--pull flags, parameterized REPO_DIR,
  HTML-safe tooltips, --dry-run mode, permission checks, guarded git ops

Tests: readdirSync wrapped in try/catch; isCatchAllRegexPattern expanded;
  extractCommandHandlerNames supports let/var/no-semicolon

Infrastructure: pre-deploy-checks gate 3b (menu symbols), CHANGELOG.md created,
  package.json bumped to 3.0.0

All 60 tests pass. node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
… during PR merges

Restores 12 categories of codex changes that were incorrectly overwritten by
"current" (main) when resolving merge conflicts across 6 merge commits.

Security / TLS hardening (index.js):
- Restore resolveTlsCertPathIfAllowed(): multi-directory fallback for TLS certs
  (PROJECT_DIR, certs/, /etc/ssl, /etc/letsencrypt) — was collapsed to PROJECT_DIR only
- Restore isHealthTlsEnabled() to use resolveTlsCertPathIfAllowed()
- Restore requestHealthPayload(): centralized HTTP/HTTPS health fetcher with
  dynamic protocol detection — was inlined with hardcoded https in two places
- Update /health command to use requestHealthPayload() (shows protocol label)
- Update pamenu_tools_health to use requestHealthPayload()
- Update startHealthServer() TLS cert read to use resolveTlsCertPathIfAllowed()

Command injection protection (index.js):
- commandNeedsConfirmation(): restore pipe-to-bash/zsh detection,
  destructive-cmd-piped pattern, redirect pattern — main simplified to only catch "sh"
- commandBlocked(): restore explicit pipe-to-shell blocker pattern

Race condition fixes (index.js):
- deleteEphemeralBonusPrompt(): restore runUserMutation guards for safe
  read-delete-write of ephemeralBonusMsgId/ChatId
- sendEphemeralBonusPrompt(): restore guards (claimedPromo check, active promo lookup),
  per-user dynamic promo lookup via getActivePromoCodeForUser() (was hardcoded
  promoStore.code), "I Have Claimed" callback button, mutation-safe writes
- Add promo_confirm_claimed_next callback handler (button was added, handler missing)

Deploy/runtime hardening (deploy.sh, prod-run.sh, runewager.service):
- deploy.sh: restore data/backups/ mkdir, chown -R APP_NAME, chmod 0750/0640/0600
  permission hardening after npm install
- prod-run.sh: restore chmod 0750 for data/data/backups/logs dirs,
  chmod 0640 for log+session files, chown -R to service user after dir creation
- runewager.service: add UMask=0077 (prevents world-readable files from service)

.gitignore:
- Restore legacy root-level data file patterns: users.json, giveaways.json,
  promo.json, env.json, analytics*.json (pre-data/ directory structure)

All 60 tests pass. node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
- TDZ fix: move pendingActionsTimedOut/menuStaleRecoveries declarations
  before evaluatePendingActionTimeout to eliminate temporal dead-zone risk
- Timeout boundary: revert < to <= so age exactly equal to 15m is NOT
  expired — aligns with /testall check and unit test expectations
- /logs line count: clamp to [1, 200] (adds Math.max(1,...) lower bound
  to reject negative/zero values from user input)
- Health panel: fix _errorRate.windowErrors → _errorRate.count (field
  did not exist; .count is the correct field on _errorRate object)
- /logs fallback: add execFile('tail') fallback when journalctl errors
  with no output (non-systemd systems); uses BOT_LOG_FILE env or default
- executeSshvCommand: fix comment — said "spawn" but code uses exec();
  updated to accurately describe exec with shell:true (admin-only)
- load_tooltips.sh: remove || true that defeated diff --cached check,
  causing .gitignore changes to never be committed on --push

All 60 tests pass. node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
## Helpful Tooltips (was Content Drops)
- Rename all UI strings: Content Drops → Helpful Tooltips throughout
- New settings panel: interval, silent mode, Link Channel/Group
- Target group linking via forwarded message (saves name + ID)
- Dashboard footer shows real group name + ID
- "Show all Helpful Tooltips (N)" button with dynamic count
- Inline button builder: [Label - URL] && [Label2 - URL2] syntax
  - Multiple buttons per row with &&, new line = new row
  - [Open Bot] shorthand for standard Open Bot button
  - Full URL + label validation before save
- postTipToConfiguredTarget uses silentMode flag + parsed buttons
- tipsStore extended with targetGroupTitle and silentMode fields

## Giveaway v3.0+
- Extracted buildGiveawayAnnouncementText + buildGiveawayAnnouncementKeyboard helpers
- scheduleGiveawayRefresh: auto-refresh at 25%, 50%, 75% of duration + re-pin
- scheduleGiveawayReminders overhauled: 10m, 5m, 1min, 30sec, 10→1 countdown
- HTML results format: @handle, SC WON, (2x boost applied), DM tip
- Full admin winner report per winner: TG handle, display name, RW username, prize, boost
- "View Results in Group" deep-link button in admin report
- Winner DMs include SC amount, boost status, RW username
- DM failure tracking with summary count
- giveawayPreflightCheck: validates group linked, warns on missing pin permission
- gwizStart calls preflight before wizard begins

## Scripts
- generate_tooltips.sh: extracts DEFAULT_TIPS_LIST from index.js → data/tooltips.json (atomic, idempotent)
- add_tooltip.sh: appends placeholder tooltip entry, outputs new ID
- deploy.sh: step 3b auto-runs generate_tooltips.sh before service start
- prod-run.sh: step 6b auto-runs generate_tooltips.sh before bot launch

## /testall
- Added Helpful Tooltips System checks (tipsStore shape, count, interval, target, parser)
- Added Giveaway Extended checks (helpers defined, preflight defined)

## Docs
- RUNEWAGER_FUNCTIONALITY_MAP.md: full v3.0+ sync with flowcharts

All 60 tests pass. node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
… linking

- Group command guard middleware: bot.use() intercepts all commands in
  group/supergroup chats and redirects to DM with a deep-link button.
  Passthrough commands with own group logic: link, linkrunewager, giveaway,
  start_giveaway, admin. Suppresses handler execution for all others.

- Onboarding progress bar: onboardingProgressBar(step) renders ●●○○○
  Step N of 5 — Label. showOnboardingPrompt() prepends a Markdown progress
  header (auto-deletes after 8s) before each step-specific prompt.

- Onboarding completion card: shown once (user.onboarding.completionCardShown
  flag) when user reaches the main menu after completing all 5 steps.
  Includes feature summary and Open Menu button.

- Admin System Tools: added 🔗 Group Linking button to
  adminSystemToolsKeyboard() with admin_sys_group_linking action handler
  (renders group linking panel with back-to-system-tools navigation).

- Schema: completionCardShown added to onboarding default + migration guard.
- Map: RUNEWAGER_FUNCTIONALITY_MAP.md fully updated; todolist.md updated.
- All 60 tests pass, node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
…ates

PR #112 review fixes (sourcery-ai):
- R1: tips_cmd_import_batch now uses await_tip_import_batch pending type
  with dedicated JSON-array router handler; proper MarkdownV2 prompt
- R2: generate_tooltips.sh fixes command substitution pollution; use
  RUNEWAGER_APP env var instead of process.argv[1] (undefined in node -)
- R3: add_tooltip.sh fixes shell injection; TOOLTIP_TEXT passed via
  TOOLTIP_TEXT_ENV env var, heredoc uses <<'EOF', process.argv[2] for file
- R4: extend catchAllCases array with (.|\n)*, (.|\n)+, (\.|[\s\S])*;
  add post-whitespace-strip forms to CATCH_ALL_CORES set
- R5: extractCommandHandlerNames test now covers let/var declarations and
  no-semicolon forms (CMD_FOUR/eta, CMD_FIVE/theta)
- R6: RUNEWAGER_FUNCTIONALITY_MAP.md typo "auto-deletes 8s" → "after 8s"

Codebase audit duplicate removal:
- A1: remove dead buildGiveawayAnnouncementText(giveaway, remainingStr)
  at ~12533; keep dynamic version at ~13795
- A2: remove simplified buildGiveawayAnnouncementKeyboard at ~13817
  (wrong tgw_participants_ callback); restore full 5-row version
- A3+A4: remove first duplicate bot.action registrations for
  admin_cat_system and admin_cat_support (identical bodies)

All 60 tests pass, node --check clean, bash -n clean on both scripts.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
Resolved conflicts in favour of our branch (HEAD) for:
- index.js: keep await_tip_import_batch router + handler, keep A1/A2
  duplicate-function removal, keep Content Drops branding from main
- add_tooltip.sh / generate_tooltips.sh: keep env-var injection fixes
- test/smoke.test.js: keep extended catchAllCases + extractCommand fixtures
- RUNEWAGER_FUNCTIONALITY_MAP.md / todolist.md: keep our updated content

Incoming from origin/main: claude-pr-results-20260228_193756Z.md

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
…ry bot start

Every script that starts/restarts the bot now follows the same safe sequence:
  1. git pull origin main (fetch + reset --hard)
  2. generate_tooltips.sh (extracts DEFAULT_TIPS_LIST → data/tooltips.json)
  3. kill any process blocking PORT (default 3000)
  4. start/restart bot

Changes per file:
- prod-run.sh: add step 9c — free_port_if_conflicted() BEFORE step 10 restart
  (port-kill was already present in God-Mode Heal but fired after, not before)
- deploy.sh: add step 3c — inline lsof/fuser port-kill before systemctl start
- start.sh: add git fetch+reset, generate_tooltips, port-kill, stale-PID kill
  before bot launch; replace refuse-on-duplicate with kill-and-continue
- dev-run.sh: add git fetch+reset (best-effort), generate_tooltips, port-kill
  before exec node
- scripts/rollback.sh: add generate_tooltips after npm ci (refreshes from
  rolled-back index.js), add lsof/fuser port-kill before service start
  (no git pull — rollback intentionally targets an older commit)

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
- Escape Markdown special chars in runewagerUsername to prevent parse failures
- Add explicit smoke-test assertions for (.|)* and (.|)+ catch-all forms
- Strip inline comments from .env PORT values in deploy.sh, dev-run.sh, start.sh
  (e.g. PORT=3000 # dev now correctly yields 3000)
- Fix showOnboardingPrompt JSDoc: steps documented as 1–4 → 1–5 (matches impl)
- Upgrade all port-block kill loops to SIGTERM-first then SIGKILL after 2s grace
- Add path + shape validation to generate_tooltips.sh Node extraction block
  (validates RUNEWAGER_APP is absolute .js path; checks array literal size/shape)

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
- Create docs/INDEX.md: exhaustive cross-reference of all 95 commands,
  266 action handlers, and 50+ pending action types mapped to feature docs
- Create docs/features/ with 15 per-feature .md files covering every
  bot subsystem (onboarding, menus, giveaway, bonus, promos, tooltips,
  referral, SSHV, deploy, user lookup, group linking, bug reports,
  announcements, misc)
- Create docs/TODO_FUNCTIONALITY_UPGRADE.md with 14 open stale-menu
  and missing-handler items (walkthrough dead-end, clearOldMenus gaps,
  missing tip_view handler, language stub, broadcastFailedUsers cap)
- Update RUNEWAGER_FUNCTIONALITY_MAP.md section 26 with full docs/ table
  and mandate for future Claude sessions to consult docs/INDEX.md first

Future sessions: read docs/INDEX.md → feature .md → index.js (if needed)

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
… path validation

- index.js: keep MarkdownV2 escaping for runewagerUsername (security fix)
- generate_tooltips.sh: keep path validation (absolute .js, no traversal)
- start.sh / dev-run.sh / scripts/rollback.sh: keep SIGTERM→SIGKILL two-step
- start.sh: keep inline comment stripping in PORT parse
- test/smoke.test.js: keep (.|)* and (.|)+ catch-all cases

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
PR review comment fixes:
- add_tooltip.sh: validate list is Array, filter non-finite IDs before
  computing maxId; throw fast on malformed tooltips.json
- docs/12-group-linking.md: fix entry points table — admin System Tools
  uses admin_sys_group_linking, not admin_cat_system
- docs/15-misc-commands.md: remove ⚠️ walkthrough dead-end note (resolved)

T-01 — Walkthrough system hardened:
- sendWalkthroughStep(): clearOldMenus() before every step send
- Back button disabled on step 1 (first step)
- Next replaced with Finish button on last step (step 35)
- walk_done on last step: sets started=false, fires walkthrough_completed
  analytics, answerCbQuery with success toast, returns to main menu
- New doc: docs/features/16-walkthrough.md

T-02 — clearOldMenus() added to 5 missing locations:
- sendOnboardingReferralPrompt()
- renderSshvConsole()
- renderGroupLinkingTools()
- tips_cmd_edit handler
- tips_cmd_remove handler
(sendGiveawayListPage already used replyMenu() — no change needed)

T-03 — Tooltip view flow implemented:
- tipsDashboardKeyboard(): "👁 View Tooltip" button added (row 4)
- tips_cmd_view handler: clearOldMenus + tipSelectKeyboard('tip_view')
- tip_view_{id} handler: preview card with Prev/Next tip navigation,
  Edit / Toggle / Delete buttons, Back to List, Admin Menu

T-15 — Broadcast failure logging made reliable:
- Removed 500-item cap from broadcastFailedUsers push and load
- Every failure logged via adminLog('broadcast_failure', ...) to
  data/admin-events.log — no silent drops
- /broadcast_failed: chunked output (30/chunk, up to 90 inline);
  overflow note points to log file
- High-failure-rate warning: >20% failure rate sends ⚠️ DM to ADMIN_IDS

Merge conflicts resolved (PRs #112-114):
- index.js: keep MarkdownV2 escaping for runewagerUsername
- generate_tooltips.sh: keep path validation (absolute .js, no traversal)
- start.sh/dev-run.sh/rollback.sh: keep SIGTERM→SIGKILL two-step
- test/smoke.test.js: keep (.|)* and (.|)+ catch-all test cases

Tests: 60/60 pass | node --check: clean | bash -n: all scripts OK

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
Three changes addressing remaining PR #115 review feedback:

1. generate_tooltips.sh — skip guard to preserve runtime tips
   - Skip regeneration if data/tooltips.json already has entries.
   - Prevents admin-added tooltips (broadcast every 4h via bot) from
     being overwritten on every restart or deploy.
   - Add --force flag to override the guard when intentional reset is needed.
   - --dry-run still works regardless of the guard.

2. scripts/helpers/free_port.sh — shared SIGTERM→SIGKILL helper
   - Extracts duplicated port-freeing logic (lsof/fuser, SIGTERM→SIGKILL)
     from start.sh, dev-run.sh, deploy.sh, and scripts/rollback.sh into a
     single reusable helper (sourceable or callable directly).
   - Reduces drift: future tweaks to kill strategy happen in one place.

3. start.sh / dev-run.sh — RUNEWAGER_AUTO_UPDATE gate
   - git fetch + reset --hard origin/main is now conditional on
     RUNEWAGER_AUTO_UPDATE env var (default 1 in prod start.sh,
     default 0 in dev-run.sh).
   - Prevents silent discard of local/staging uncommitted changes.
   - Documented in .env.example.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
1. generate_tooltips.sh — normalize APP_DIR to absolute path
   Move helper function definitions (info/warn/error) before variable
   assignments so error() is available at init time.  Normalize
   RUNEWAGER_DIR → APP_DIR via cd+pwd immediately after assignment so the
   Node.js absolute-path validation (requires '/'-prefixed path) never
   fails when a caller passes a relative RUNEWAGER_DIR.

2. scripts/helpers/free_port.sh — re-query port before SIGKILL
   Extract discovery into _query_port_pids() helper.  After the SIGTERM
   grace period, re-query the port for survivors and only SIGKILL PIDs
   that are still listening — guards against killing an unrelated process
   that reused a PID during the 2 s sleep window.

3. dev-run.sh — read RUNEWAGER_AUTO_UPDATE from .env as fallback
   Parse RUNEWAGER_AUTO_UPDATE from .env before the auto-update guard so
   the flag works even when .env values have not been exported into the
   calling shell.  Use explicit if/else instead of chained && || for the
   destructive git reset --hard command.

4. start.sh — same .env-read fix + explicit if/else for git reset
   Same pattern as dev-run.sh: resolve RUNEWAGER_AUTO_UPDATE from env
   then .env (default 1 for prod), replace the chained git &&/|| with an
   explicit if/else block.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
1. generate_tooltips.sh — case-based arg parser
   Replace the for-loop [[]] checks with a case statement that rejects
   unknown flags (e.g. --froce typo) with a clear error and non-zero exit.

2. generate_tooltips.sh — env-var file paths in Node.js invocations
   The three inline node -e calls that interpolated $TOOLTIPS_FILE /
   $TMP_FILE directly into single-quoted JS strings were fragile for paths
   containing quotes or special characters.  All three now pass the path
   via a dedicated env var (TOOLTIPS_FILE_PATH or VALIDATE_FILE) and read
   process.env inside the script, matching the existing RUNEWAGER_APP pattern.

3. dev-run.sh — non-fatal free_port.sh invocation
   free_port.sh can exit non-zero on benign errors (no lsof/fuser, race
   after SIGTERM) which would abort dev-run.sh under set -eu.  Added
   || echo WARN fallback to mirror the same non-fatal pattern used for the
   tooltip script invocation directly above.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
1. generate_tooltips.sh — trap for TMP_FILE cleanup
   Register 'trap rm -f TMP_FILE EXIT INT TERM' immediately before the
   atomic write section so the temp file is always removed on any exit
   (error, signal, or normal completion).  After a successful mv the path
   no longer exists, so the trap is a safe no-op on the happy path.

2. dev-run.sh — default to merge --ff-only; gate reset --hard behind opt-in
   Auto-update now runs 'git fetch + merge --ff-only' (non-destructive).
   'git reset --hard origin/main' is only executed when RUNEWAGER_FORCE_RESET=1
   is set in the environment or .env, satisfying the "confirm destructive
   operations" guideline.  Fast-forward failure emits a clear warning
   pointing the user to RUNEWAGER_FORCE_RESET.
   Documented in .env.example.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
- Merged main branch: telegramSafe.js, rateLimiter.js, backend.js,
  runewager-endpoint.service, prod-run.sh rewrite, runewager_redeploy.sh,
  rw_cpu_guard.sh
- Removed dead code: legacy adminKeyboard() function (JSDoc + body,
  ~32 lines) — no callers, belonged to removed /admin_menu command
- RUNEWAGER_FUNCTIONALITY_MAP.md: updated last-audited date, added new
  module entries (telegramSafe, rateLimiter, backend, service, scripts),
  added 2026-03-04 audit log entry
- todolist.md: updated last-updated date, added fixed adminKeyboard entry
- All 60 tests pass post-fix

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
…mode

Three code-review fixes:

1. Section 10 (Safe restart): `|| true` swallowed systemctl failures and
   left the bot stopped. Replaced with `if ! systemctl restart ...; then`
   block that falls back to manual kill + nohup when systemd fails.

2. Bare `disown` (non-systemd path, L506): with `set -euo pipefail` a
   failed `disown` (no job control in non-interactive shells) aborted the
   script before post-start health checks and Telegram reporting ran.
   Fixed: `disown || true` in both the fallback and non-systemd paths.

3. Telegram notification: removed `parse_mode=Markdown` (unescaped log
   content and env values can break Markdown rendering / cause truncation).
   Switched to plain text with `--data-urlencode` so special chars in
   the message are safe without manual escaping. Removed unused `_REPORT`
   variable (log tail was computed but never injected into the message).

60/60 tests pass.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
Two related fixes to the group command guard middleware:

1. Skip commands addressed to another bot (@mention): `/warn@otherbot`
   was previously stripped to `warn` before the check, causing Runewager
   to reply "This command works in DM" for every other bot's command.
   Now the @mention is parsed and if it refers to a different bot the
   message is silently ignored.

2. Add BOT_KNOWN_COMMANDS set: unaddressed commands (e.g. bare `/warn`)
   in groups are also silently ignored if Runewager has no handler for
   them. Only commands owned by this bot trigger the DM-redirect reply.

60/60 tests pass.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
…remove

Added rule to both CLAUDE.md and RUNEWAGER_FUNCTIONALITY_MAP.md §25:

Any bot.command() addition or removal in index.js must update the
BOT_KNOWN_COMMANDS set in the same change set. The set gates the group
command guard — omitting it silently breaks the new command in groups
or keeps intercepting a removed one.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
index.js:
- Add 15 missing commands to BOT_KNOWN_COMMANDS (admin_log, stuck,
  fixaccount, discord_confirm, mygiveaways, checkin, top, boostmeter,
  eligible, gwhistory, promocheck, support, promo_cooldown, discord_stats,
  gw_graphic)
- Extract parseGroupCommand() shared helper from group guard middleware
- Add _auditKnownCommandsDrift() startup runtime guard — catches
  BOT_KNOWN_COMMANDS drift at launch and alerts admins via Telegram

prod-run.sh:
- Resolve NODE_BIN once at validation time, use in all nohup fallbacks
- Fix restart fallback race: systemctl stop before nohup, port freed
  before bind, PID re-fetched after failed systemctl restart

AGENTS/ (new — universal AI agent system):
- CLAUDE.md: operating manual with bootstrap protocol, async workflow
  pattern, process manager table (systemd/pm2/docker/supervisor/bare)
- AI_CONTRACT.md: universal contract with bootstrap-first requirement,
  universal wiring index rule, anti-hallucination rules, stack-agnostic
  entry point vocabulary covering all stack types
- MAP_TEMPLATE.md: blank FUNCTIONALITY_MAP for any repo — UI/UX flows,
  state machines, entry point index, all sections stack-agnostic
- INDEX_TEMPLATE.md: full universal wiring index template — commands,
  routes, actions, events, queues, cron, sockets, gestures, IPC
- TODOLIST_TEMPLATE.md: priority-based task board template
- bootstrap.sh: idempotent path creator — ensures all doc files exist,
  creates from templates or stubs, optional --scan and --dry-run

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Mar 4, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Mar 4, 2026

Reviewer's Guide

Adds a startup-time drift guard between Telegraf bot.command registrations and BOT_KNOWN_COMMANDS, standardizes deployment runtime resolution in prod-run.sh, and introduces a reusable AGENTS documentation/contract system (AI_CONTRACT, templates, bootstrap script, and session log) for AI agents in this repo.

Sequence diagram for bot startup BOT_KNOWN_COMMANDS drift guard

sequenceDiagram
    participant NodeProcess
    participant startBot
    participant _auditKnownCommandsDrift
    participant TelegrafBot
    participant BOT_KNOWN_COMMANDS
    participant logEvent
    participant _startupWarnings
    participant AdminNotifier

    NodeProcess->>startBot: invoke()
    startBot->>startBot: loadPersistentData()
    startBot->>startBot: ensureQaArtifacts()
    startBot->>startBot: persistRuntimeState()

    startBot->>_auditKnownCommandsDrift: run drift guard

    _auditKnownCommandsDrift->>TelegrafBot: inspect middleware tree
    _auditKnownCommandsDrift->>TelegrafBot: collect command triggers
    _auditKnownCommandsDrift->>_auditKnownCommandsDrift: merge with _registeredCommands
    _auditKnownCommandsDrift->>BOT_KNOWN_COMMANDS: compare command sets

    alt driftedIn not empty
        _auditKnownCommandsDrift->>logEvent: error BOT_KNOWN_COMMANDS DRIFT missing commands
        _auditKnownCommandsDrift->>_startupWarnings: push warning message
    else driftedOut not empty only
        _auditKnownCommandsDrift->>logEvent: warn BOT_KNOWN_COMMANDS entries not registered
    else no drift
        _auditKnownCommandsDrift->>logEvent: info drift check passed
    end

    startBot->>logEvent: info Starting Runewager Bot
    startBot->>startBot: configureBotSurface()
    startBot->>TelegrafBot: launch()
    TelegrafBot-->>startBot: ready

    startBot->>AdminNotifier: send startup summary with _startupWarnings
Loading

Flow diagram for AGENTS bootstrap.sh session bootstrap

graph TD
    Start[Start AGENTS/bootstrap.sh] --> ParseArgs[Parse CLI args<br/>--dry-run / --scan]

    ParseArgs --> SetFlags[Set DRY_RUN and SCAN flags]

    SetFlags --> EnsureMap[Call ensure_file for<br/>FUNCTIONALITY_MAP.md<br/>using MAP_TEMPLATE.md]
    EnsureMap --> EnsureIndex[Call ensure_file for<br/>docs/INDEX.md<br/>using INDEX_TEMPLATE.md]
    EnsureIndex --> EnsureTodolist[Call ensure_file for<br/>todolist.md<br/>using TODOLIST_TEMPLATE.md]
    EnsureTodolist --> EnsureSessionLog[Call ensure_file for<br/>AGENTS/SESSION_LOG.md<br/>with stub creator]

    EnsureSessionLog --> EnsureFeaturesDir{Does docs/features<br/>exist?}

    EnsureFeaturesDir -->|yes| FeaturesOk[Directory exists]
    EnsureFeaturesDir -->|no and DRY_RUN=1| FeaturesDryRun[Log missing<br/>docs/features and note dry-run]
    EnsureFeaturesDir -->|no and DRY_RUN=0| CreateFeatures[mkdir -p docs/features]

    FeaturesOk --> ScanCheck{SCAN flag set?}
    FeaturesDryRun --> ScanCheck
    CreateFeatures --> ScanCheck

    ScanCheck -->|no| Finish[Print next steps<br/>and exit]

    ScanCheck -->|yes| ScanStart[Run optional codebase scan]

    ScanStart --> ScanEntryPoints[grep for entry point<br/>patterns in source files]
    ScanEntryPoints --> ScanCounts[Report counts of<br/>matching files]
    ScanCounts --> ScanTests[Count and report test files]
    ScanTests --> DetectProcMgr[Detect pm2, systemd,<br/>docker, Procfile presence]
    DetectProcMgr --> Finish
Loading

File-Level Changes

Change Details Files
Add startup drift check to ensure all bot.command registrations are represented in BOT_KNOWN_COMMANDS and wire it into the bot startup sequence.
  • Introduce an internal Set to track registered commands and a helper that inspects Telegraf middleware layers to collect all command triggers.
  • Compare the discovered command names against BOT_KNOWN_COMMANDS, computing commands that are registered but missing from the set and vice versa, with a special-case skip for the start command.
  • Log drift as errors/warnings, push human-readable startup warnings for admins, and log a success message when no drift is detected.
  • Invoke the drift audit helper at the beginning of startBot() before bot.launch(), so discrepancies surface in startup warnings and admin notifications.
index.js
Resolve and propagate a single validated Node binary path for deployment, to keep nohup/systemd fallbacks using the same runtime.
  • Add NODE_BIN resolution via command -v node once near the top of the script after validating node and npm availability.
  • Extend the startup log line to include the Node binary path in addition to Node and npm versions.
  • Prepare for subsequent uses of NODE_BIN in restart/nohup fallbacks to avoid runtime skew between this script and systemd.
prod-run.sh
Introduce a reusable AGENTS documentation and AI contract system to standardize how AI agents work with the repo and keep code, maps, and wiring docs in sync.
  • Add AI_CONTRACT.md that defines the core invariant (code, functionality map, and wiring index must match) plus detailed pre-session, during-session, and post-session rules for AI agents, including anti-hallucination and safety guidelines.
  • Add MAP_TEMPLATE.md, INDEX_TEMPLATE.md, TODOLIST_TEMPLATE.md, and CLAUDE.md to provide stack-agnostic templates and an operating manual for functionality mapping, wiring indexes, task boards, and AI session workflow.
  • Add a bootstrap.sh script that idempotently creates missing documentation artifacts (FUNCTIONALITY_MAP.md, docs/INDEX.md, docs/features/, todolist.md, SESSION_LOG.md) from templates or stubs, with optional --scan and --dry-run modes to summarize entry points/tests and avoid writes.
  • Create SESSION_LOG.md as an append-only session history file, seeded with an initial entry describing the introduction of the drift guard, deployment changes, and AGENTS system, including tests and outstanding follow-ups.
AGENTS/AI_CONTRACT.md
AGENTS/MAP_TEMPLATE.md
AGENTS/bootstrap.sh
AGENTS/CLAUDE.md
AGENTS/INDEX_TEMPLATE.md
AGENTS/TODOLIST_TEMPLATE.md
AGENTS/SESSION_LOG.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 4, 2026

Warning

Rate limit exceeded

@gamblecodezcom has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 49 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b98d33ce-2167-4861-a788-316c7c23ef6e

📥 Commits

Reviewing files that changed from the base of the PR and between acb9629 and 4c4266f.

📒 Files selected for processing (9)
  • AGENTS/AI_CONTRACT.md
  • AGENTS/CLAUDE.md
  • AGENTS/INDEX_TEMPLATE.md
  • AGENTS/MAP_TEMPLATE.md
  • AGENTS/SESSION_LOG.md
  • AGENTS/TODOLIST_TEMPLATE.md
  • AGENTS/bootstrap.sh
  • index.js
  • prod-run.sh
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/plan-v3-deployment-VPMpw

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.

@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Mar 4, 2026
@gamblecodezcom gamblecodezcom merged commit 728a361 into main Mar 4, 2026
5 checks passed
@gamblecodezcom gamblecodezcom deleted the claude/plan-v3-deployment-VPMpw branch March 4, 2026 17:53
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Mar 4, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Node binary consistency
    The script resolves NODE_BIN once but the many nohup fallbacks still call node directly (not the resolved path). This can cause runtime/version skew when systemd and the fallback use different PATHs or node installations.

  • Scan pattern reliability
    The codebase scan loop passes complex regex-like strings to grep via the loop variable. Depending on the shell and grep implementation these patterns may be interpreted unexpectedly or produce false negatives; also repeating a full repo search for every pattern can be slow on large repos.

  • File permissions
    When templates are copied or stubs created the code does not explicitly set file permissions. Templates could carry unexpected executable bits or permissive modes. Newly-created files and directories should be created with predictable, safe permissions (and ownership when appropriate).

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • The _auditKnownCommandsDrift implementation relies on Telegraf’s internal middleware structure (bot.middleware()?.()?.handler?.middleware and layer.triggers), which is fairly brittle; consider encapsulating this behind a clearly isolated utility (with a defensive fallback when the internal shape changes) or, if possible, feeding the known commands through a single registration function instead of introspecting framework internals.
  • In AGENTS/bootstrap.sh, the --scan grep patterns (e.g. "app\.\(get\|post\|put\|delete\|patch\)(") are passed directly to grep -r without -E, so the grouping/alternation syntax won’t behave as intended on all platforms; either simplify to basic regex or explicitly enable extended regex and test the patterns on a typical repo.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `_auditKnownCommandsDrift` implementation relies on Telegraf’s internal middleware structure (`bot.middleware()?.()?.handler?.middleware` and `layer.triggers`), which is fairly brittle; consider encapsulating this behind a clearly isolated utility (with a defensive fallback when the internal shape changes) or, if possible, feeding the known commands through a single registration function instead of introspecting framework internals.
- In `AGENTS/bootstrap.sh`, the `--scan` grep patterns (e.g. `"app\.\(get\|post\|put\|delete\|patch\)("`) are passed directly to `grep -r` without `-E`, so the grouping/alternation syntax won’t behave as intended on all platforms; either simplify to basic regex or explicitly enable extended regex and test the patterns on a typical repo.

## Individual Comments

### Comment 1
<location path="index.js" line_range="14982-14986" />
<code_context>
+  }
+
+  if (driftedIn.length > 0) {
+    const msg = `[BOT_KNOWN_COMMANDS DRIFT] Commands registered via bot.command() but MISSING from BOT_KNOWN_COMMANDS: ${driftedIn.join(', ')}. Add them to the set near line 279 of index.js.`;
+    logEvent('error', msg);
+    _startupWarnings.push(`⚠️ BOT_KNOWN_COMMANDS drift detected — missing: ${driftedIn.join(', ')}`);
</code_context>
<issue_to_address>
**suggestion:** Hard-coding a source line number in the drift message is brittle and will quickly become inaccurate.

Referring to a specific line number will become inaccurate as the file changes and make the message misleading. Instead, reference the symbol or location semantically (e.g., “Add them to BOT_KNOWN_COMMANDS near the top of index.js” or “search for BOT_KNOWN_COMMANDS”) so the guidance remains valid over time.

```suggestion
  if (driftedIn.length > 0) {
    const msg = `[BOT_KNOWN_COMMANDS DRIFT] Commands registered via bot.command() but MISSING from BOT_KNOWN_COMMANDS: ${driftedIn.join(', ')}. Add them to BOT_KNOWN_COMMANDS (search for BOT_KNOWN_COMMANDS in index.js).`;
    logEvent('error', msg);
    _startupWarnings.push(`⚠️ BOT_KNOWN_COMMANDS drift detected — missing: ${driftedIn.join(', ')} (update BOT_KNOWN_COMMANDS; search for BOT_KNOWN_COMMANDS in index.js)`);
  }
```
</issue_to_address>

### Comment 2
<location path="AGENTS/bootstrap.sh" line_range="46-55" />
<code_context>
+info() { echo -e "  ${_cyan}→${_reset}  $*"; }
+
+# ─── Helper: create file from template if missing ───────────────────────────
+ensure_file() {
+  local target="$1"
+  local template="$2"  # may be empty string if no template
+  local description="$3"
+
+  if [[ -f "$target" ]]; then
+    ok "EXISTS   $target"
+    return 0
+  fi
+
+  warn "MISSING  $target  ($description)"
+
+  if (( DRY_RUN )); then
+    info "[DRY-RUN] would create from: ${template:-inline}"
+    return 0
+  fi
+
+  mkdir -p "$(dirname "$target")"
+
+  if [[ -n "$template" && -f "$template" ]]; then
+    cp "$template" "$target"
+    ok "CREATED  $target  (from template)"
+  else
+    # Inline minimal stub when no template exists
+    create_stub "$target" "$description"
+    ok "CREATED  $target  (stub — fill in before committing)"
+  fi
</code_context>
<issue_to_address>
**issue (bug_risk):** Files whose names are not handled in create_stub's case statement will be created empty without any indication.

If `ensure_file` falls back to `create_stub` and the basename isn’t one of the handled cases, the function writes nothing but still logs `CREATED (stub — fill in before committing)`. That misleading success message can mask incorrect use or missing template handling. Either add a default case that writes a generic explanatory stub, or have `create_stub` warn and return non‑zero when it doesn’t recognize the filename.
</issue_to_address>

### Comment 3
<location path="AGENTS/INDEX_TEMPLATE.md" line_range="35" />
<code_context>
+
+| # | Feature | Doc | Role | Status |
+|---|---------|-----|------|--------|
+| 01 | [Feature name] | [docs/features/01-name.md](features/01-name.md) | user/admin/system | ✅ Active |
+
+---
</code_context>
<issue_to_address>
**issue (bug_risk):** The feature doc link target likely needs the `docs/` prefix to match the actual path.

In the Feature Doc Index table, the link text shows `docs/features/01-name.md` but the href is `features/01-name.md`. To avoid a broken link and align with other templates, update the href to `(docs/features/01-name.md)`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread index.js
Comment on lines +14982 to +14986
if (driftedIn.length > 0) {
const msg = `[BOT_KNOWN_COMMANDS DRIFT] Commands registered via bot.command() but MISSING from BOT_KNOWN_COMMANDS: ${driftedIn.join(', ')}. Add them to the set near line 279 of index.js.`;
logEvent('error', msg);
_startupWarnings.push(`⚠️ BOT_KNOWN_COMMANDS drift detected — missing: ${driftedIn.join(', ')}`);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Hard-coding a source line number in the drift message is brittle and will quickly become inaccurate.

Referring to a specific line number will become inaccurate as the file changes and make the message misleading. Instead, reference the symbol or location semantically (e.g., “Add them to BOT_KNOWN_COMMANDS near the top of index.js” or “search for BOT_KNOWN_COMMANDS”) so the guidance remains valid over time.

Suggested change
if (driftedIn.length > 0) {
const msg = `[BOT_KNOWN_COMMANDS DRIFT] Commands registered via bot.command() but MISSING from BOT_KNOWN_COMMANDS: ${driftedIn.join(', ')}. Add them to the set near line 279 of index.js.`;
logEvent('error', msg);
_startupWarnings.push(`⚠️ BOT_KNOWN_COMMANDS drift detected — missing: ${driftedIn.join(', ')}`);
}
if (driftedIn.length > 0) {
const msg = `[BOT_KNOWN_COMMANDS DRIFT] Commands registered via bot.command() but MISSING from BOT_KNOWN_COMMANDS: ${driftedIn.join(', ')}. Add them to BOT_KNOWN_COMMANDS (search for BOT_KNOWN_COMMANDS in index.js).`;
logEvent('error', msg);
_startupWarnings.push(`⚠️ BOT_KNOWN_COMMANDS drift detected — missing: ${driftedIn.join(', ')} (update BOT_KNOWN_COMMANDS; search for BOT_KNOWN_COMMANDS in index.js)`);
}

Comment thread AGENTS/bootstrap.sh
Comment on lines +46 to +55
ensure_file() {
local target="$1"
local template="$2" # may be empty string if no template
local description="$3"

if [[ -f "$target" ]]; then
ok "EXISTS $target"
return 0
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Files whose names are not handled in create_stub's case statement will be created empty without any indication.

If ensure_file falls back to create_stub and the basename isn’t one of the handled cases, the function writes nothing but still logs CREATED (stub — fill in before committing). That misleading success message can mask incorrect use or missing template handling. Either add a default case that writes a generic explanatory stub, or have create_stub warn and return non‑zero when it doesn’t recognize the filename.

Comment thread AGENTS/INDEX_TEMPLATE.md

| # | Feature | Doc | Role | Status |
|---|---------|-----|------|--------|
| 01 | [Feature name] | [docs/features/01-name.md](features/01-name.md) | user/admin/system | ✅ Active |
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): The feature doc link target likely needs the docs/ prefix to match the actual path.

In the Feature Doc Index table, the link text shows docs/features/01-name.md but the href is features/01-name.md. To avoid a broken link and align with other templates, update the href to (docs/features/01-name.md).

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Mar 4, 2026

CodeAnt AI finished reviewing your PR.

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

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants