Skip to content

Add deterministic AST audit and comprehensive /testall runtime simulation with rotating logs and reports#140

Merged
gamblecodezcom merged 2 commits into
mainfrom
codex/perform-full-structural-audit-for-runewager
Mar 6, 2026
Merged

Add deterministic AST audit and comprehensive /testall runtime simulation with rotating logs and reports#140
gamblecodezcom merged 2 commits into
mainfrom
codex/perform-full-structural-audit-for-runewager

Conversation

@gamblecodezcom
Copy link
Copy Markdown
Owner

@gamblecodezcom gamblecodezcom commented Mar 4, 2026

User description

Motivation

  • Provide a deterministic AST audit and bring RUNEWAGER_FUNCTIONALITY_MAP.md/docs/INDEX.md into sync with runtime state and command registrations.
  • Add a self-contained, admin-only /testall runner that can simulate every known command and representative callback payloads without hitting external APIs to improve observability and safety.
  • Harden /testall logging so CI/VPS boots are resilient when rotation dependencies are missing and include rollback/snapshot guarantees for runtime stores.

Description

  • Added deterministic AST audit tooling: scripts/ast_audit.js that emits reports/ast_audit.json and reports/structural_audit.md.
  • Implemented an in-process /testall harness in index.js with: admin-only guard (isAdmin(ctx)), ephemeral runtime snapshot/rollback helpers (createTestAllStateSnapshot / restoreTestAllStateSnapshot), synthetic bot.handleUpdate command/callback simulation, Telegram API stubbing during simulation, deterministic report persistence (reports/testall_report.json|.md|.txt), and batched admin DM delivery (guarded by env flags).
  • Added rotating-log support for /testall via lazy-loaded rotating-file-stream with a fallback append/prune writer to avoid boot crashes; encapsulated in createTestAllRotatingLogger.
  • Added CI-friendly wrapper and runtime harness: scripts/testall_runtime.js (runtime simulator) and scripts/run_testall.sh (wrapper to run AST + runtime testall, rotate wrapper logs and guarantee TXT fallback).
  • Synchronized documentation and maps: updated docs/INDEX.md, docs/features/10-deploy-ops.md, and RUNEWAGER_FUNCTIONALITY_MAP.md entries describing /testall behavior, rotation policy, invariants, and command normalization note (/Aa coverage).
  • Updated BOT_KNOWN_COMMANDS in index.js to include newly-tracked commands and aliases and added a guarded module.exports = { bot } when TESTALL_EXPORTS=1.
  • Added package.json/package-lock.json changes to include rotating-file-stream and development AST helpers (@babel/*) as dev dependencies.

Testing

  • Ran npm test, which passed: 60/60 tests successful.
  • Executed node scripts/ast_audit.js, which completed and wrote reports/ast_audit.json and reports/structural_audit.md (audit artifacts generated).
  • Executed node scripts/testall_runtime.js, which completed normally and wrote reports/testall_report.json, reports/testall_report.md, and reports/testall_report.txt (runtime simulation captured a small number of expected async send errors under the CI stub but the harness exited successfully).

Codex Task

Summary by Sourcery

Add deterministic AST-based command/callback audit tooling and a comprehensive, admin-only /testall runtime simulation with rotating logs and persisted reports to improve observability, safety, and CI/ops workflows.

New Features:

  • Introduce AST audit script that scans commands, callbacks, middleware, and privilege guards and emits structured JSON and markdown reports.
  • Add a standalone runtime simulation harness and shell wrapper that exercise all commands and callbacks via synthetic updates and write deterministic /testall reports and TXT fallbacks.
  • Extend /testall into a self-contained admin-only diagnostics runner that executes AST and runtime audits, snapshots and restores bot state, and sends summarized admin DMs with report links.
  • Add rotating log support dedicated to /testall, with daily/size-based rotation, gzip compression, and retained history referenced from reports.

Enhancements:

  • Expand the known command set to include additional admin and user commands/aliases while documenting intentional normalization behavior and fallback callback observability.
  • Improve the unmatched callback fallback handler with structured logging of bounded callback context for better debugging.
  • Document the new /testall workflow, artifacts, and rotating-log policy in the deployment and index docs, and align the functionality map with the audited structure.

Build:

  • Add rotating-file-stream as a runtime dependency and Babel parser/traverse/types as dev dependencies to support the audit tooling.

Documentation:

  • Update deployment operations, index, and functionality map documentation to describe the AST audit, /testall runtime simulation, artifacts, and logging/rollback invariants.

CodeAnt-AI Description

Add deterministic AST audit and a self-contained /testall runtime simulator with safe logging and rollback

What Changed

  • Admin-only /testall now runs a deterministic AST audit and an in-process runtime simulation that exercises all known commands and representative callback payloads without calling external APIs; it produces JSON/MD/TXT reports in reports/.
  • /testall captures API interactions using Telegram stubs, preserves and restores a snapshot of runtime stores so tests don't persist state, and sends a concise DM report to configured admins when triggered from the bot.
  • Logging during /testall uses a rotating-file-stream when available and falls back to a safe append/prune writer to avoid boot/crash failures; retained test logs are referenced in the generated reports.
  • Added scripts to generate the AST structural audit and to run the runtime simulation (scripts/ast_audit.js, scripts/testall_runtime.js, scripts/run_testall.sh) and included deterministic audit output under reports/.
  • Global fallback callback now logs unhandled callback payloads (bounded length) for observability; test harness exports bot for CI use.

Impact

✅ Clearer structural audit reports for command/callback coverage
✅ Fewer boot failures when rotating-log dependency is missing during CI/VPS startup
✅ Shorter admin debugging time with per-run reports and safe, non-persistent runtime tests

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

Summary by CodeRabbit

  • New Features

    • Admin-only TestAll: deterministic runtime simulation, state snapshot/rollback, and multi-format reports with chunked admin delivery.
  • Documentation

    • Expanded deployment/ops and feature docs with TestAll workflow, artifact descriptions, and CI wrapper usage.
  • Chores

    • Added rotating log retention, fallback logging, and a CI-safe wrapper for reliable runs.
  • Bug Fixes

    • Hardened startup/reporting paths, Telegram DM chunking, and CI smoke resilience.

@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

Implements a deterministic AST-based audit pipeline and a comprehensive, admin-only /testall runtime simulation harness, including rotating logs, snapshot/rollback of in-memory stores, CI-safe wrappers, and synchronized documentation and dependency updates.

Sequence diagram for the updated /testall end-to-end diagnostics flow

sequenceDiagram
  actor Admin
  participant Telegram as TelegramServer
  participant Bot as RunewagerBot_index_js
  participant Logger as TestAllRotatingLogger
  participant Snapshot as TestAllStateSnapshot
  participant AST as scripts_ast_audit_js
  participant Runtime as scripts_testall_runtime_js
  participant FS as ReportsFilesystem

  Admin->>Telegram: /testall command
  Telegram->>Bot: update(message:/testall)

  Bot->>Bot: isAdmin(ctx)
  alt not admin
    Bot-->>Telegram: ignore (no diagnostics)
  else admin
    Bot->>Logger: createTestAllRotatingLogger()
    Logger-->>Bot: rotatingLogger
    Bot->>Telegram: reply "Running /testall — full internal diagnostics started."
    Bot->>Logger: log(info, testall_started)

    Bot->>Snapshot: createTestAllStateSnapshot()
    Snapshot-->>Bot: snapshot

    Bot->>Bot: stub ctx.reply/sendMessage/etc to sink
    Bot->>Bot: stub bot.telegram methods to in-process recorder

    loop for each step
      Bot->>Telegram: progress reply "Running test i/N — title"
      Bot->>Bot: execute step function
      alt AST audit step
        Bot->>AST: runTestAllExternalAudit() via child_process.execFile
        AST-->>Bot: writes ast_audit.json, structural_audit.md
      else Command simulation step
        Bot->>Bot: safeInvokeCommand for each BOT_KNOWN_COMMANDS
      else Callback simulation step
        Bot->>Runtime: read ast_audit.json, derive callback samples
        Bot->>Bot: safeInvokeAction via bot.handleUpdate(callback_query)
      else FSM/pagination/admin-flow steps
        Bot->>Bot: simulate pagination/admin callbacks and commands
      end
      Bot->>Logger: log(step_started/step_finished)
    end

    Bot->>Snapshot: restoreTestAllStateSnapshot(snapshot)

    Bot->>FS: write reports/testall_report.json|.md|.txt
    Bot->>Logger: listLogArtifacts()
    Logger-->>Bot: latest & retained testall.log* paths

    Bot->>Bot: restore real bot.telegram methods

    Bot->>Telegram: reply quick summary "TestAll complete — X passed, Y warnings, Z failures."

    alt TESTALL_ADMIN_DM enabled and triggered from in-bot /testall
      Bot->>Bot: escapeMarkdownV2(dmReport)
      loop for each adminId in ADMIN_IDS
        Bot->>Telegram: sendMessage(adminId, dmReportChunks, MarkdownV2)
      end
    end

    Bot->>Logger: close()
  end
Loading

Sequence diagram for scripts/testall_runtime.js deterministic simulation

sequenceDiagram
  participant CI as CI_or_Shell
  participant Runtime as scripts_testall_runtime_js
  participant Bot as RunewagerBot_index_js
  participant FS as ReportsFilesystem

  CI->>Runtime: node scripts/testall_runtime.js
  Runtime->>Runtime: set env (CI, DISABLE_RUNTIME, TESTALL_EXPORTS, TESTALL_ADMIN_DM, ADMIN_IDS, BOT_TOKEN, DEVICE)

  Runtime->>FS: read reports/ast_audit.json
  alt missing or invalid AST report
    Runtime-->>CI: stderr JSON { status:error, message }
    Runtime->>Runtime: process.exit(1)
  else AST OK
    Runtime->>Bot: require('../index.js') (bot export)

    Runtime->>Runtime: override process.exit to throw

    Runtime->>Bot: stub bot.telegram.callApi
    Runtime->>Bot: stub bot.telegram methods (sendMessage, editMessageText, answerCallbackQuery, etc.)

    Runtime->>Runtime: derive commandSet from ast.commands
    Runtime->>Runtime: derive callbackSet from callback emitters, actions, regex samples

    loop for each command in commandSet
      Runtime->>Bot: bot.handleUpdate(message:"/command")
      Bot-->>Runtime: simulated handling
      Runtime->>Runtime: record apiCalls, responsePreview, errors
    end

    loop for each callbackData in callbackSet
      Runtime->>Bot: bot.handleUpdate(callback_query:data)
      Bot-->>Runtime: simulated handling
      Runtime->>Runtime: record apiCalls, fallbackHandled, responsePreview, errors
    end

    Runtime->>Runtime: collect asyncErrors from uncaughtException/unhandledRejection

    Runtime->>FS: write reports/testall_report.json
    Runtime->>FS: write reports/testall_report.md
    Runtime->>FS: write reports/testall_report.txt

    Runtime->>Runtime: restore original process.exit
    Runtime-->>CI: stdout JSON { status, json, md, txt }
  end
Loading

Class-style diagram for /testall diagnostics, snapshot/rollback, and logging components

classDiagram
  class RunewagerBot_index_js {
    +Set BOT_KNOWN_COMMANDS
    +bot TelegrafBot
    +command testall(ctx)
    +runTestAllExternalAudit()
    +createTestAllStateSnapshot()
    +restoreTestAllStateSnapshot(snapshot)
    +chunkTextForTelegram(text, limit)
    +createTestAllRotatingLogger()
    +isAdmin(ctx)
  }

  class TestAllRotatingLogger {
    +String dir
    +String latest
    +Boolean usingFallback
    +String sessionTag
    +log(level, message, extra)
    +listLogArtifacts()
    +close()
  }

  class TestAllStateSnapshot {
    +Array userEntries
    +Object promoStore
    +Number giveawayCounter
    +Array giveawayRunning
    +Array giveawayHistory
    +Object tipsStore
    +Array promoHistoryEntries
    +Array smartButtonEntries
    +Array sshvSessionEntries
  }

  class AstAuditScript {
    +listFiles(dir)
    +parseFile(file)
    +getCalleeName(node)
    +evalStaticString(node)
    +collectCommandsActions()
    +writeJsonReport()
    +writeMarkdownReport()
  }

  class TestAllRuntimeScript {
    +dispatchCommand(cmd)
    +dispatchCallback(data)
    +sampleFromRegex(regexLiteral)
    +recordApi(method, payload)
    +makeTelegramStub(name)
    +generateSummary()
    +writeJsonReport()
    +writeMarkdownReport()
    +writeTxtReport()
  }

  class RunTestAllShellWrapper {
    +rotate_wrapper_logs()
    +configureEnvForCI()
    +invokeAstAudit()
    +invokeRuntimeSim()
  }

  class data_logs_testall {
    +String path = "data/logs/testall"
  }

  class reports_dir {
    +String path = "reports"
  }

  RunewagerBot_index_js --> TestAllRotatingLogger : creates
  RunewagerBot_index_js --> TestAllStateSnapshot : creates
  RunewagerBot_index_js --> AstAuditScript : execFile
  RunewagerBot_index_js --> TestAllRuntimeScript : execFile

  TestAllRuntimeScript --> RunewagerBot_index_js : require bot (TESTALL_EXPORTS=1)
  RunTestAllShellWrapper --> AstAuditScript : runs
  RunTestAllShellWrapper --> TestAllRuntimeScript : runs
  TestAllRotatingLogger --> data_logs_testall : writes
  AstAuditScript --> reports_dir : writes
  TestAllRuntimeScript --> reports_dir : writes
Loading

File-Level Changes

Change Details Files
Add deterministic AST audit tooling and structural reports for commands/actions/callbacks.
  • Introduce scripts/ast_audit.js using @babel/parser/traverse to walk all JS/TS files and collect bot.command/bot.action/Markup.button.callback usage and middleware ordering.
  • Compute and persist a normalized audit model (commands, BOT_KNOWN_COMMANDS, callbacks, scenes, async .then-without-.catch, admin-guard heuristics) into reports/ast_audit.json.
  • Generate a long-form human-readable structural_audit.md summarizing command map, callback map, FSM status, middleware order, async handling, and evidence sections.
scripts/ast_audit.js
reports/ast_audit.json
reports/structural_audit.md
Implement an external AST+runtime audit runner and a fully in-process /testall harness with snapshot/rollback and Telegram stubbing.
  • Add runTestAllExternalAudit() in index.js to exec ast_audit.js and scripts/testall_runtime.js with CI-friendly env and return a normalized status payload.
  • Completely refactor bot.command('testall') handler to be admin-only via isAdmin(ctx), drive a multi-step test pipeline with progress messages, and record per-step metadata into an in-memory report object.
  • Introduce createTestAllStateSnapshot/restoreTestAllStateSnapshot to clone and later restore key in-memory stores (users, promo, giveaways, tips, promo history, smart buttons, SSHV sessions) around the /testall run.
  • Stub ctx.reply/sendMessage/answerCbQuery/editMessageText during simulation to a sink to avoid real user-visible side effects while still logging messages.
  • Stub bot.telegram methods (sendMessage, editMessageText, answerCallbackQuery, sendPhoto, etc.) to record synthetic API calls and allow safe bot.handleUpdate-based simulation of commands and callbacks.
  • Add safeInvokeCommand and safeInvokeAction helpers to replay synthetic updates for every BOT_KNOWN_COMMANDS entry and AST-discovered actions (including regex callbacks via sampleRegexCallback), collecting per-item results and fallback hits.
  • Persist rich JSON/Markdown/TXT reports (testall_report.json
.md
Add a dedicated rotating logger for /testall with a robust fallback writer and link its artifacts into reports.
  • Add createTestAllRotatingLogger() in index.js that lazily requires rotating-file-stream, configures a daily/10MB gzip-compressed rotation policy under data/logs/testall/, and falls back to a simple append+prune implementation if the dependency is unavailable.
  • Log step lifecycle events, command/callback results, and final status into the rotating logger, tagging each run with a unique sessionTag and exposing listLogArtifacts() to reference recent log files in the testall report.
  • Ensure logger write failures are swallowed so /testall cannot crash the runtime if disk or deps misbehave, and close the underlying stream at the end of /testall runs.
index.js
data/logs/testall/
Provide a standalone runtime simulator for CI (scripts/testall_runtime.js) and a shell wrapper to orchestrate AST+runtime audits.
  • Implement scripts/testall_runtime.js which imports the bot via TESTALL_EXPORTS, enforces CI-safe env vars, stubs all bot.telegram methods (including callApi) to a local recorder, and uses ast_audit.json to derive the set of commands and callback payloads to simulate.
  • Simulate commands via bot.handleUpdate(message) and callbacks via bot.handleUpdate(callback_query), collecting per-item stats (apiCalls, response previews, fallback detection) and asyncErrors from uncaughtException/unhandledRejection handlers.
  • Emit a summarized JSON report plus a Markdown table (testall_report.md) and a minimal TXT summary, then write a small JSON status object to stdout for runTestAllExternalAudit() consumers.
  • Add scripts/run_testall.sh, which rotates its own wrapper logs (gzip-compressing older logs), sets CI/TESTALL_* envs, runs ast_audit.js + testall_runtime.js, and guarantees a testall_report.txt artifact for CI or VPS usage.
scripts/testall_runtime.js
scripts/run_testall.sh
reports/testall_report.json
reports/testall_report.md
reports/testall_report.txt
data/logs/testall/
Extend BOT_KNOWN_COMMANDS and improve fallback callback observability and exports for testing.
  • Add missing command strings (admin_log, promo_cooldown, discord_stats, gw_graphic, stuck, fixaccount, discord_confirm, mygiveaways, checkin, top, boostmeter, eligible, gwhistory, promocheck, support) to BOT_KNOWN_COMMANDS with a comment about '/A' being normalized to 'a'.
  • Enhance the generic fallback bot.action(/.*/, ...) handler to log a structured warn-level event with userId and bounded callbackData before replying and answering the callback query.
  • Gate module.exports = { bot } on TESTALL_EXPORTS to enable import from test harnesses without exposing the bot object by default.
index.js
Document and codify the /testall workflow, rotating log policy, and command/callback invariants in docs and QA context.
  • Update docs/features/10-deploy-ops.md to describe the extended /testall behavior, enumerate generated artifacts, document the rotating-log policy, and add scripts/run_testall.sh to the deploy/ops tooling table.
  • Update docs/INDEX.md with new command/action counts, document command/callback audit invariants (BOT_KNOWN_COMMANDS sync, intentional /A→a normalization, fallback handler logging, absence of scenes), and describe the new comprehensive /testall runner and wrapper.
  • Expand RUNEWAGER_FUNCTIONALITY_MAP.md to reflect the upgraded /testall semantics, log retention, CI hardening, and wrapper behavior, including multiple dated log entries for these changes.
  • Append detailed session notes in AGENTS/SESSION_LOG.md describing the multiple phases of AST/index/map sync, /testall simulation extension, rotating logs, CI smoke fixes, and follow-up hardening.
  • Refresh QA context JSONs (qa/context/, qa/state/) if necessary to align with new capabilities (as seen in diff).
docs/features/10-deploy-ops.md
docs/INDEX.md
RUNEWAGER_FUNCTIONALITY_MAP.md
AGENTS/SESSION_LOG.md
qa/context/bot_capabilities.json
qa/context/repo_info.json
qa/state/provider_status.json
Add dependencies required for AST parsing and log rotation.
  • Add rotating-file-stream as a runtime dependency for /testall rotating logger support.
  • Add @babel/parser, @babel/traverse, and @babel/types as devDependencies for the AST audit tooling in scripts/ast_audit.js.
  • Update package-lock.json accordingly to lock the new dependencies.
package.json
package-lock.json

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

📝 Walkthrough

Walkthrough

Adds a self-contained TestAll diagnostic system: AST static audit, deterministic runtime command/callback simulation with mocked Telegram calls, state snapshot/rollback, rotating log retention, multi-format report artifacts, admin-only execution, and a CI-safe wrapper for deterministic runs.

Changes

Cohort / File(s) Summary
Core bot/test harness
index.js
Introduced test-harness APIs and flow: runTestAllExternalAudit, createTestAllStateSnapshot, restoreTestAllStateSnapshot, chunkTextForTelegram, createTestAllRotatingLogger; admin-guarded /testall orchestration, deterministic reports (JSON/MD/TXT), snapshot/rollback, rotating-logger integration, and conditional export via TESTALL_EXPORTS.
Audit & runtime scripts
scripts/ast_audit.js, scripts/testall_runtime.js, scripts/run_testall.sh
New AST static-audit script and runtime simulator that mock Telegram, execute command/callback suites, capture API calls and async errors, and write reports; shell wrapper orchestrates runs, env toggles, log rotation, and TXT fallbacks for CI.
Reports & evidence
reports/structural_audit.md, reports/testall_report.json, reports/testall_report.md, reports/testall_report.txt
New static and runtime artifacts: structural AST audit and full test run summaries with per-command/callback results, API call previews, async error records, invariant summary and rate-limiter snapshots.
Logging & rate limiting
package.json, rateLimiter.js
Added rotating-file-stream dependency; rateLimiter expanded with settings() and throttleWithConfig() plus exported API surface for instrumentation and wait reporting.
QA/context & small state files
qa/context/bot_capabilities.json, qa/context/repo_info.json, qa/state/provider_status.json
Populated bot_capabilities manifest (commands, callbacks, onboarding, mapExcerpt), minor repo_info newline, and provider_status timestamp/formatting changes.
Docs & maps
docs/INDEX.md, docs/features/10-deploy-ops.md, RUNEWAGER_FUNCTIONALITY_MAP.md, AGENTS/SESSION_LOG.md
Documented TestAll workflow, audit invariants, artifact locations, rotating log policy, CI wrapper usage, and updated functionality map to reflect new runner and artifacts.

Sequence Diagram

sequenceDiagram
    participant Admin as Admin User
    participant Bot as Bot (/testall)
    participant State as State Snapshot Manager
    participant AST as AST Audit
    participant Runner as Runtime Harness
    participant Logger as Rotating Logger
    participant Reports as Report Writer
    participant Telegram as Telegram API (mock)

    Admin->>Bot: /testall
    Bot->>Bot: isAdmin(ctx)
    alt admin verified
        Bot->>State: createTestAllStateSnapshot()
        State-->>Bot: snapshot
        Bot->>AST: runTestAllExternalAudit()
        AST->>Reports: write structural audit
        AST-->>Bot: audit results
        Bot->>Runner: execute commands & callbacks
        Runner->>Logger: open rotating logger
        Runner->>Telegram: mock/send API calls (record)
        Telegram-->>Runner: mock responses/errors
        Runner->>Reports: write JSON/MD/TXT reports
        Runner-->>Bot: results
        Bot->>State: restoreTestAllStateSnapshot()
        State-->>Bot: restored
        Bot->>Bot: chunkTextForTelegram()
        Bot->>Telegram: send admin DM (chunked)
        Telegram-->>Bot: delivery ack
        Bot-->>Admin: summary
    else access denied
        Bot-->>Admin: deny
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Hop, hop—I parsed the trees today,
I snapshot states and rolled away;
Commands all danced, callbacks took flight,
Logs spinning round in gzip delight;
TestAll sings: reports ready—hop to the light!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and specifically describes the main changes: deterministic AST audit, comprehensive /testall runtime simulation with rotating logs and reports. It is concise, clear, and directly summarizes the primary additions in the changeset.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/perform-full-structural-audit-for-runewager

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
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 2 issues, and left some high level feedback:

  • In the /testall handler, reportData.summary never defines commandsPassed, callbacksPassed, or warnings, but those fields are later used to render the legacy summary string, so the quick summary will show incorrect/NaN values; consider computing pass/warning counts explicitly when building summary and referencing those instead.
  • The /testall flow globally stubs and later restores bot.telegram.* methods on the shared bot instance, which can affect concurrent updates while a long test run is in progress; it may be safer to run the harness in a separate process (only) or to isolate stubbing to a dedicated bot instance used solely for simulation.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the `/testall` handler, `reportData.summary` never defines `commandsPassed`, `callbacksPassed`, or `warnings`, but those fields are later used to render the legacy summary string, so the quick summary will show incorrect/NaN values; consider computing pass/warning counts explicitly when building `summary` and referencing those instead.
- The `/testall` flow globally stubs and later restores `bot.telegram.*` methods on the shared bot instance, which can affect concurrent updates while a long test run is in progress; it may be safer to run the harness in a separate process (only) or to isolate stubbing to a dedicated bot instance used solely for simulation.

## Individual Comments

### Comment 1
<location path="index.js" line_range="14208-14209" />
<code_context>
-    `Duration: ${(durationMs / 1000).toFixed(1)}s  |  ${totalPassed}/${totalCount} checks passed`,
-    `Summary: TestAll complete — ${totalPassed} passed, ${totalWarnings} warnings, ${totalFailed} failures.`,
+  if (originalCtxFns.reply) {
+    const totalPassed = reportData.summary
+      ? reportData.summary.commandsPassed + reportData.summary.callbacksPassed
+      : (reportData.commandResults.filter((r) => r.ok).length + reportData.callbackResults.filter((r) => r.ok && !r.skipped).length);
+    const totalWarnings = reportData.summary
</code_context>
<issue_to_address>
**issue (bug_risk):** Summary computation assumes fields that are never set, which can lead to NaN/undefined in the final /testall message.

Because `reportData.summary` is copied from `totals` and doesn’t define `commandsPassed`, `callbacksPassed`, or `warnings`, the ternary always takes the `summary` branch once it exists, producing `undefined + undefined` and `undefined`. These then surface as `NaN passed` / `undefined warnings` in the legacy summary. You’ll want to either populate `commandsPassed` / `callbacksPassed` / `warnings` when constructing `summary`, or avoid using `summary` here and always recompute these values from `commandResults` / `callbackResults` / `steps`.
</issue_to_address>

### Comment 2
<location path="docs/features/10-deploy-ops.md" line_range="144" />
<code_context>
+
+### Rotating log policy for `/testall`
+
+- `/testall` writes per-step progress and errors to rotating file stream: `data/logs/testall/testall.log`.
+- Rotation triggers: daily interval **or** size threshold (`10M`).
+- Rotated files are gzip compressed and old files are pruned automatically (`maxFiles` retention).
</code_context>
<issue_to_address>
**suggestion (typo):** Add an article before “rotating file stream” to make the sentence grammatically complete.

Consider phrasing this as “writes per-step progress and errors to a rotating file stream” for clearer grammar.

```suggestion
- `/testall` writes per-step progress and errors to a rotating file stream: `data/logs/testall/testall.log`.
```
</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 +14208 to +14209
const totalPassed = reportData.summary
? reportData.summary.commandsPassed + reportData.summary.callbacksPassed
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): Summary computation assumes fields that are never set, which can lead to NaN/undefined in the final /testall message.

Because reportData.summary is copied from totals and doesn’t define commandsPassed, callbacksPassed, or warnings, the ternary always takes the summary branch once it exists, producing undefined + undefined and undefined. These then surface as NaN passed / undefined warnings in the legacy summary. You’ll want to either populate commandsPassed / callbacksPassed / warnings when constructing summary, or avoid using summary here and always recompute these values from commandResults / callbackResults / steps.


### Rotating log policy for `/testall`

- `/testall` writes per-step progress and errors to rotating file stream: `data/logs/testall/testall.log`.
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 (typo): Add an article before “rotating file stream” to make the sentence grammatically complete.

Consider phrasing this as “writes per-step progress and errors to a rotating file stream” for clearer grammar.

Suggested change
- `/testall` writes per-step progress and errors to rotating file stream: `data/logs/testall/testall.log`.
- `/testall` writes per-step progress and errors to a rotating file stream: `data/logs/testall/testall.log`.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Mar 4, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Regex Safety
    The code assumes pattern.value is a string like /body/flags and slices it to construct RegExp instances without validating the format and without error handling. Malformed or unexpected pattern strings could make new RegExp(...) throw and crash the script. Also lastIndexOf('/') can return -1 and lead to incorrect slices.

  • Possible Self-scan
    The file-tree traversal (listFiles) currently walks the repository root and only excludes node_modules and .git. Generated artifacts and runtime folders (for example reports, data, dist, caches) will be scanned and can cause false positives, noisy results, or the audit to re-scan its own outputs. This also risks the audit reading files it just wrote and changing results between runs.

Comment thread scripts/ast_audit.js
Comment on lines +97 to +101
if (callee?.endsWith('.then')) {
const parent = p.parentPath?.node;
const chainedCatch = parent?.type === 'CallExpression' && getCalleeName(parent.callee)?.endsWith('.catch');
if (!chainedCatch) data.promises.thenNoCatch.push({ file: rfile, line, callee });
}
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: The detection of .then() calls without .catch() only inspects the immediate parent node, so normal promise chains like promise.then(...).catch(...) (where .catch is called on the result of .then) are incorrectly treated as missing a catch, filling the thenNoCatch report with false positives; the fix is to walk up through any MemberExpression wrappers to find an actual .catch() call in the chain before flagging it. [logic error]

Severity Level: Major ⚠️
- ⚠️ AST audit over-reports missing .catch on safe chains.
- ⚠️ /testall runtime summary mis-states asyncThenWithoutCatch count.
- ⚠️ Admins may chase non-issues in deploy diagnostics.
Suggested change
if (callee?.endsWith('.then')) {
const parent = p.parentPath?.node;
const chainedCatch = parent?.type === 'CallExpression' && getCalleeName(parent.callee)?.endsWith('.catch');
if (!chainedCatch) data.promises.thenNoCatch.push({ file: rfile, line, callee });
}
if (callee?.endsWith('.then')) {
// Walk up past MemberExpression wrappers to see if this .then() is followed by a .catch()
let cursor = p.parentPath;
while (cursor && cursor.node.type === 'MemberExpression') {
cursor = cursor.parentPath;
}
const maybeCall = cursor?.node;
const chainedCatch = maybeCall?.type === 'CallExpression' && getCalleeName(maybeCall.callee)?.endsWith('.catch');
if (!chainedCatch) data.promises.thenNoCatch.push({ file: rfile, line, callee });
}
Steps of Reproduction ✅
1. From the repo root (`/workspace/Runewager`), run the AST audit script `node
scripts/ast_audit.js` (entry point at `scripts/ast_audit.js:44-46`), which parses all
`*.js`/`*.ts` files and traverses them using the `CallExpression` visitor at
`scripts/ast_audit.js:74-102`.

2. During traversal, the visitor hits the Promise chain in `_globalSlot` inside
`rateLimiter.js` (shown in `rateLimiter.js:40-80` via tool read): `_globalTail =
_globalTail.then(async () => { ... }).catch(() => { /* Never break the global chain */
});`. For the inner `.then(...)` call, `getCalleeName` returns a string ending in `.then`,
so the condition at `scripts/ast_audit.js:97` (`if (callee?.endsWith('.then'))`) is
satisfied.

3. For that `.then` call, `p.parentPath.node` is a `MemberExpression` representing the
chained `.catch` call (`<CallExpression for .then>.catch`). The current implementation at
`scripts/ast_audit.js:98-99` only inspects this immediate parent; because it is a
`MemberExpression` (not a `CallExpression`), `parent?.type === 'CallExpression'` is false
and `chainedCatch` is computed as `false` even though the `.catch()` call is present one
level up in the chain.

4. As a result, the `.then` in `_globalSlot` (and similar chains like
`prevTail.then(...).catch(...)` in `enqueue` within `rateLimiter.js:40-80`) is incorrectly
pushed into `data.promises.thenNoCatch` at `scripts/ast_audit.js:100`. This propagates
into `reports/ast_audit.json` (`promises.thenNoCatch` array referenced in
`scripts/testall_runtime.js:275`) and surfaces in the `/testall` runtime summary as
`.then() without direct .catch()` counts, generating false positives for promise chains
that do have a `.catch()` attached.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** scripts/ast_audit.js
**Line:** 97:101
**Comment:**
	*Logic Error: The detection of `.then()` calls without `.catch()` only inspects the immediate parent node, so normal promise chains like `promise.then(...).catch(...)` (where `.catch` is called on the result of `.then`) are incorrectly treated as missing a catch, filling the `thenNoCatch` report with false positives; the fix is to walk up through any `MemberExpression` wrappers to find an actual `.catch()` call in the chain before flagging it.

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.
👍 | 👎

}

(async () => {
const commandSet = Array.from(new Set(ast.commands.map((c) => c.cmd && c.cmd.value).filter(Boolean))).sort();
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: The runtime harness currently includes the /testall command in commandSet, so when it simulates all commands it will invoke the /testall handler defined in index.js, which in turn calls runTestAllExternalAudit() and spawns a new scripts/testall_runtime.js process again; when run via scripts/run_testall.sh (where ADMIN_IDS defaults to 1), this creates unbounded recursive /testall invocations and child processes instead of a single self-test run. Excluding testall from the simulated command set avoids this recursive self-invocation while still exercising all other commands. [logic error]

Severity Level: Critical 🚨
-`/testall` admin diagnostic spawns nested `testall_runtime` child processes.
-`scripts/run_testall.sh` CI wrapper risks long hangs/timeouts.
- ⚠️ `/testall` internal report generation becomes slow and error-prone.
- ⚠️ Deploy-ops diagnostics in `docs/features/10-deploy-ops.md` unreliable.
Suggested change
const commandSet = Array.from(new Set(ast.commands.map((c) => c.cmd && c.cmd.value).filter(Boolean))).sort();
const commandSet = Array.from(new Set(
ast.commands
.map((c) => c.cmd && c.cmd.value)
.filter((value) => value && value !== 'testall') // avoid recursively invoking /testall, which re-runs this harness
)).sort();
Steps of Reproduction ✅
1. Generate the AST audit so the runtime harness has command metadata by running `node
scripts/ast_audit.js` from the project root. This writes `reports/ast_audit.json`, whose
`commands` section includes an entry for the `testall` command (`file: "index.js", line:
13831, value: "testall"`) as seen at `reports/ast_audit.json:880-919`.

2. Run the runtime simulator via the documented CI wrapper `scripts/run_testall.sh` (see
`scripts/run_testall.sh:35-55`, which sets `CI=true`, `DISABLE_RUNTIME=1`,
`TESTALL_EXPORTS=1` and then executes `node scripts/ast_audit.js` followed by `node
scripts/testall_runtime.js`), or directly invoke `node scripts/testall_runtime.js`.

3. Inside `scripts/testall_runtime.js`, the harness reads `reports/ast_audit.json` into
`ast` (lines 21-31) and builds `commandSet` from all discovered commands using the line in
question (`scripts/testall_runtime.js:228-229`), so `commandSet` contains `"testall"`
along with all other commands.

4. The harness iterates `commandSet` and, for each command, calls `dispatchCommand(cmd)`
(`scripts/testall_runtime.js:244-248`); for `cmd === 'testall'`, `dispatchCommand`
constructs a synthetic update with message text `"/testall"` and calls `await
bot.handleUpdate(...)` (`scripts/testall_runtime.js:142-156`), which routes to the real
`/testall` handler registered in `index.js:252-703`.

5. The `/testall` handler in `index.js` runs its first step, `"AST Audit Step"`, calling
`const externalAudit = await runTestAllExternalAudit();` (`index.js:478-482`).
`runTestAllExternalAudit` is defined at `index.js:29-68` and uses
`execFileAsync(process.execPath, [runtimeScript], { ..., env: envBase })` to spawn a new
`node scripts/testall_runtime.js` child process (`runtimeScript` is set to
`path.join(__dirname, 'scripts', 'testall_runtime.js')` at `index.js:38-52`).

6. That child process executes the same `scripts/testall_runtime.js` entrypoint, again
loading `../index.js` (which registers the `/testall` handler) and again building
`commandSet` from `ast.commands` including `"testall"`
(`scripts/testall_runtime.js:21-31`, `228-229`). When the child harness reaches
`"testall"` in its own `commandSet`, it repeats step 4 and step 5, causing another
`runTestAllExternalAudit()` call that spawns yet another `scripts/testall_runtime.js`
process.

7. Because there is no guard to stop `/testall` from invoking the external harness when
that harness is already running, this process recursion continues: each
`scripts/testall_runtime.js` instance eventually dispatches `/testall`, whose handler
(`index.js:252-703`) spawns another `scripts/testall_runtime.js` via
`runTestAllExternalAudit()`. The recursion only terminates when the `execFileAsync`
timeout (180 seconds as specified at `index.js:52`) is hit, causing the outer `/testall`
step to fail and the overall harness run to be slow and error-prone instead of completing
a single deterministic pass.

8. Excluding `"testall"` from `commandSet` (as in the improved code) keeps the runtime
harness from simulating the `/testall` command itself; `/testall` remains usable in the
real bot and continues to call `runTestAllExternalAudit()`, but the external harness will
no longer re-enter itself recursively through `/testall`.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** scripts/testall_runtime.js
**Line:** 229:229
**Comment:**
	*Logic Error: The runtime harness currently includes the `/testall` command in `commandSet`, so when it simulates all commands it will invoke the `/testall` handler defined in `index.js`, which in turn calls `runTestAllExternalAudit()` and spawns a new `scripts/testall_runtime.js` process again; when run via `scripts/run_testall.sh` (where `ADMIN_IDS` defaults to `1`), this creates unbounded recursive `/testall` invocations and child processes instead of a single self-test run. Excluding `testall` from the simulated command set avoids this recursive self-invocation while still exercising all other commands.

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.
👍 | 👎

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Mar 4, 2026

CodeAnt AI finished reviewing your PR.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (8)
qa/context/bot_capabilities.json (2)

434-434: Optional: make excerpt truncation explicit.

mapExcerpt currently ends mid-token; adding an explicit suffix (e.g., … [truncated]) would reduce ambiguity when reading QA artifacts.

Based on learnings: Any added/removed flow in the Runewager bot must be reflected in RUNEWAGER_FUNCTIONALITY_MAP.md in the same change set.

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

In `@qa/context/bot_capabilities.json` at line 434, Update the "mapExcerpt" value
in qa/context/bot_capabilities.json to explicitly indicate the excerpt is
truncated by appending a suffix such as "… [truncated]" (so consumers know the
text ends intentionally mid-document). While editing, verify any newly added or
removed bot flows in your change are also reflected in
RUNEWAGER_FUNCTIONALITY_MAP.md so the excerpt and source-of-truth file remain
consistent.

2-2: Consider making generatedAt optional for committed artifacts.

Using a wall-clock timestamp here creates constant non-functional diffs. If this file is checked into git, consider gating generatedAt behind an env flag (or omitting it) to keep artifact diffs deterministic.

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

In `@qa/context/bot_capabilities.json` at line 2, Make the generatedAt field
optional to avoid noisy diffs: change the artifact generation code that writes
the JSON to only include the "generatedAt" key when an environment flag is set
(e.g., INCLUDE_GENERATED_AT=true) or when running a non-committed/debug build;
update the serializer that emits the "generatedAt" property so it checks
process.env.INCLUDE_GENERATED_AT (or equivalent config) before adding the
"generatedAt" key to the output JSON, and ensure committed artifacts omit this
field by default.
scripts/testall_runtime.js (1)

228-344: Restore process.exit in a finally block.

If the async runner throws before completion, process.exit may stay monkey-patched for the rest of the process lifetime.

♻️ Proposed hardening
-(async () => {
+(async () => {
+  try {
   const commandSet = Array.from(new Set(ast.commands.map((c) => c.cmd && c.cmd.value).filter(Boolean))).sort();
@@
-  process.exit = originalProcessExit;
   console.log(JSON.stringify({ status: summary.status, json: reportJsonPath, md: reportMdPath, txt: reportTxtPath }, null, 2));
+  } finally {
+    process.exit = originalProcessExit;
+  }
 })();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/testall_runtime.js` around lines 228 - 344, The IIFE currently
restores process.exit only at the end so an exception leaves the monkey-patch in
place; wrap the main async body of the IIFE in try { ... } finally {
process.exit = originalProcessExit; } (or add a finally block around the await
dispatch loops and report generation) so that process.exit is always restored
even if dispatchCommand/dispatchCallback or file writes throw; reference the
existing identifiers process.exit, originalProcessExit, the top-level async ()
=> { ... } IIFE, and the dispatchCommand/dispatchCallback/report write sections
when making the change.
reports/testall_report.md (1)

125-127: Callback table "Fallback" column may be misleading.

The table shows ✅ in the "Fallback" column for all callbacks, but the JSON report shows fallbackHandled: false for all entries. The checkmark appears to indicate "OK" status rather than "fallback was triggered."

This is a generator output issue — if intentional, consider renaming the column to "Handled" or "Status" for clarity in scripts/testall_runtime.js.

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

In `@reports/testall_report.md` around lines 125 - 127, The report generator is
rendering a misleading "Fallback" column with a checkmark regardless of
fallbackHandled value; update the generator in scripts/testall_runtime.js so the
table cell uses the actual fallbackHandled boolean from the JSON (show ✅ only
when fallbackHandled === true) or rename the header to a neutral term like
"Handled" or "Status" and adjust the cell text accordingly; locate the
table-generation logic that emits the header string "Fallback" and the cell
rendering for admin_auth_bypass (and other callbacks) and change the header or
conditional rendering to reflect fallbackHandled accurately.
scripts/run_testall.sh (1)

53-54: Node script execution relies on set -e for error handling.

With set -euo pipefail at the top, failures in ast_audit.js or testall_runtime.js will cause immediate script termination. This is correct behavior for a CI workflow.

However, consider adding explicit error context for debugging:

💡 Optional enhancement for better error diagnostics
-  node scripts/ast_audit.js
-  node scripts/testall_runtime.js
+  echo "Running AST audit..."
+  node scripts/ast_audit.js || { echo "AST audit failed"; exit 1; }
+  echo "Running runtime simulation..."
+  node scripts/testall_runtime.js || { echo "Runtime simulation failed"; exit 1; }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/run_testall.sh` around lines 53 - 54, Because run_testall.sh uses set
-euo pipefail, failures in the node scripts will abort without context; update
run_testall.sh to explicitly capture and report which script failed by checking
the exit status after invoking node (for node scripts/ast_audit.js and node
scripts/testall_runtime.js) or by wrapping each invocation with a short
conditional that prints a descriptive error message including the script name
and exit code before exiting non‑zero, so CI logs show clear diagnostic context
for failures.
AGENTS/SESSION_LOG.md (1)

81-106: Session entry headers use ## instead of ###.

Earlier entries (lines 8, 51, 67) use ### YYYY-MM-DD — Title format, but newer entries (lines 81, 89, 95, 100, 106) use ## YYYY-MM-DD — Title. Consider maintaining consistent heading levels for the append-only log.

📝 Suggested fix for header consistency
-## 2026-03-04 — /testall comprehensive self-contained runner hardening
+### 2026-03-04 — /testall comprehensive self-contained runner hardening

Apply similar changes to lines 89, 95, 100, and 106.

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

In `@AGENTS/SESSION_LOG.md` around lines 81 - 106, The session log uses
inconsistent header levels: change the four entries that currently start with
"## 2026-03-04 — /testall comprehensive self-contained runner hardening", "##
2026-03-04 — /testall rotating log retention and report-linking", "## 2026-03-04
— CI smoke fix for rotating logger dependency", and "## 2026-03-04 — /testall
post-review fixes (CI + report delivery + summary format)" to use the same level
as earlier entries (replace the leading "##" with "###") so all timestamps
follow the existing "### YYYY-MM-DD — Title" convention.
index.js (1)

13794-13800: Consider throttling fallback log pruning to reduce I/O overhead.

When using the fallback writer, pruneFallbackLogs(14) is called on every single log write. This triggers readdirSync, sorting, and potential unlinkSync operations for each log line, which can be inefficient during high-frequency logging.

Consider pruning only periodically (e.g., every 100 writes) or at session end.

💡 Proposed throttling approach
+  let fallbackWriteCount = 0;
+  const PRUNE_INTERVAL = 100;
+
   const log = (level, message, extra = {}) => {
     const payload = { ts: new Date().toISOString(), level, sessionTag, message, ...extra };
     const line = `${JSON.stringify(payload)}\n`;
     if (stream) {
       stream.write(line);
       return;
     }
     try {
       fs.appendFileSync(fallbackLogPath, line, 'utf8');
-      if (usingFallback) pruneFallbackLogs(14);
+      fallbackWriteCount += 1;
+      if (usingFallback && fallbackWriteCount % PRUNE_INTERVAL === 0) {
+        pruneFallbackLogs(14);
+      }
     } catch (_) {
       // swallow write errors to avoid impacting /testall flow
     }
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@index.js` around lines 13794 - 13800, The fallback writer currently calls
pruneFallbackLogs(14) on every fs.appendFileSync write, causing heavy I/O;
introduce a throttle (e.g., a module-level counter like fallbackPruneCounter or
a lastPrune timestamp) that increments on each successful append and only calls
pruneFallbackLogs(14) when the counter reaches a threshold (e.g., % 100 === 0)
or when enough time has passed (e.g., > 1 minute), and reset/update the
counter/timestamp afterward; keep the existing usingFallback check and ensure
the counter/timestamp is initialized in the same scope as the writer so pruning
still runs at session end if desired.
docs/INDEX.md (1)

186-186: Label audit counts as generated snapshot metadata.

Consider appending source + generation context (e.g., “from scripts/ast_audit.js on YYYY-MM-DD”) to reduce future confusion when counts drift.

Based on learnings: docs/INDEX.md is treated as operational source-of-truth documentation.

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

In `@docs/INDEX.md` at line 186, Update the generated snapshot metadata line
"Current AST audit baseline: 520 callback emitters, 268 action handlers, ..." in
docs/INDEX.md to append source and generation context (e.g., "from
scripts/ast_audit.js on YYYY-MM-DD"); locate the string in docs/INDEX.md and
modify the generator or manual update so the line includes the script name and
timestamp (ISO date) to clarify provenance whenever the AST audit baseline is
updated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/INDEX.md`:
- Line 6: Update the canonical line-count value in docs/INDEX.md so all
occurrences match; specifically, reconcile the two differing strings "**Bot
version:** 3.0.0 | `index.js`: ~15,100 lines | Commands: 96 registered (`95`
normalized group-command entries) | Action handlers: 268" and the other entry
that reads "`index.js`: ~14,960 lines" by choosing one canonical count (prefer
the updated "~15,100 lines"), and search the file for any other `index.js`
line-count mentions and replace them to be consistent.

In `@index.js`:
- Around line 14207-14221: The summary code is reading
reportData.summary.commandsPassed, callbacksPassed, and warnings which are not
set and cause NaN; update the logic that builds totalPassed, totalWarnings, and
totalFailed to derive values from reportData.commandResults,
reportData.callbackResults, and reportData.steps when the summary fields are
missing (or ensure reportData.summary populates commandsPassed, callbacksPassed,
and warnings beforehand). Specifically, modify the quick-summary calculation
around originalCtxFns.reply/legacyTestAllSummaryFormat to compute totals with
reportData.commandResults.filter(r => r.ok).length,
reportData.callbackResults.filter(r => r.ok && !r.skipped).length, and
reportData.steps.filter(s => s.warning).length (and analogous failing counts)
whenever reportData.summary.commandsPassed/callbacksPassed/warnings are
undefined so the final message always yields "X passed, Y warnings, Z failures."

In `@package.json`:
- Around line 34-37: Update package.json so `@babel/parser` and `@babel/traverse`
are moved from devDependencies into dependencies because /testall runs
scripts/ast_audit.js at production runtime and requires those modules; leave
`@babel/types` in devDependencies (it isn’t imported). Edit the "devDependencies"
and "dependencies" objects to remove the two packages from devDependencies and
add them to dependencies with the same version ranges, ensuring production
installs (npm ci --omit=dev) will include them.

In `@scripts/ast_audit.js`:
- Around line 146-147: The script currently calls
fs.writeFileSync('reports/ast_audit.json', ...) (and the similar write at Line
208) without ensuring the reports/ directory exists; create the reports
directory before any writes by invoking fs.mkdirSync('reports', { recursive:
true }) (or equivalent async mkdir) early in the script (before the
fs.writeFileSync calls) so both fs.writeFileSync usages (the
'reports/ast_audit.json' writes) will never fail on clean runs.
- Line 95: The CallExpression visitor currently checks for callee === 'Stage'
and also has an unreachable callee === 'new Stage' branch; add a NewExpression
visitor that mirrors the CallExpression logic so registrations created via "new
Stage(...)" are captured: detect nodes of type NewExpression where
node.callee.name === 'Stage' and push the same registration into
data.scene.stageRegs (using the same file/line variables), and remove or ignore
the 'new Stage' string branch in the CallExpression check to avoid dead code.

In `@scripts/testall_runtime.js`:
- Line 217: The fallback detection only checks for calls with method
'answerCallbackQuery' but misses the alias 'answerCbQuery', so update the
fallback predicate that computes the variable fallback to also check for calls
where c.method === 'answerCbQuery' (or normalize method aliases before the
check), ensuring previews.some(...) || calls.some(c => (c.method ===
'answerCallbackQuery' || c.method === 'answerCbQuery') && String(c.payload.text
|| '').includes('Action not available anymore')) so both intercepted aliases are
detected and fallbackHandled is reported correctly.

---

Nitpick comments:
In `@AGENTS/SESSION_LOG.md`:
- Around line 81-106: The session log uses inconsistent header levels: change
the four entries that currently start with "## 2026-03-04 — /testall
comprehensive self-contained runner hardening", "## 2026-03-04 — /testall
rotating log retention and report-linking", "## 2026-03-04 — CI smoke fix for
rotating logger dependency", and "## 2026-03-04 — /testall post-review fixes (CI
+ report delivery + summary format)" to use the same level as earlier entries
(replace the leading "##" with "###") so all timestamps follow the existing "###
YYYY-MM-DD — Title" convention.

In `@docs/INDEX.md`:
- Line 186: Update the generated snapshot metadata line "Current AST audit
baseline: 520 callback emitters, 268 action handlers, ..." in docs/INDEX.md to
append source and generation context (e.g., "from scripts/ast_audit.js on
YYYY-MM-DD"); locate the string in docs/INDEX.md and modify the generator or
manual update so the line includes the script name and timestamp (ISO date) to
clarify provenance whenever the AST audit baseline is updated.

In `@index.js`:
- Around line 13794-13800: The fallback writer currently calls
pruneFallbackLogs(14) on every fs.appendFileSync write, causing heavy I/O;
introduce a throttle (e.g., a module-level counter like fallbackPruneCounter or
a lastPrune timestamp) that increments on each successful append and only calls
pruneFallbackLogs(14) when the counter reaches a threshold (e.g., % 100 === 0)
or when enough time has passed (e.g., > 1 minute), and reset/update the
counter/timestamp afterward; keep the existing usingFallback check and ensure
the counter/timestamp is initialized in the same scope as the writer so pruning
still runs at session end if desired.

In `@qa/context/bot_capabilities.json`:
- Line 434: Update the "mapExcerpt" value in qa/context/bot_capabilities.json to
explicitly indicate the excerpt is truncated by appending a suffix such as "…
[truncated]" (so consumers know the text ends intentionally mid-document). While
editing, verify any newly added or removed bot flows in your change are also
reflected in RUNEWAGER_FUNCTIONALITY_MAP.md so the excerpt and source-of-truth
file remain consistent.
- Line 2: Make the generatedAt field optional to avoid noisy diffs: change the
artifact generation code that writes the JSON to only include the "generatedAt"
key when an environment flag is set (e.g., INCLUDE_GENERATED_AT=true) or when
running a non-committed/debug build; update the serializer that emits the
"generatedAt" property so it checks process.env.INCLUDE_GENERATED_AT (or
equivalent config) before adding the "generatedAt" key to the output JSON, and
ensure committed artifacts omit this field by default.

In `@reports/testall_report.md`:
- Around line 125-127: The report generator is rendering a misleading "Fallback"
column with a checkmark regardless of fallbackHandled value; update the
generator in scripts/testall_runtime.js so the table cell uses the actual
fallbackHandled boolean from the JSON (show ✅ only when fallbackHandled ===
true) or rename the header to a neutral term like "Handled" or "Status" and
adjust the cell text accordingly; locate the table-generation logic that emits
the header string "Fallback" and the cell rendering for admin_auth_bypass (and
other callbacks) and change the header or conditional rendering to reflect
fallbackHandled accurately.

In `@scripts/run_testall.sh`:
- Around line 53-54: Because run_testall.sh uses set -euo pipefail, failures in
the node scripts will abort without context; update run_testall.sh to explicitly
capture and report which script failed by checking the exit status after
invoking node (for node scripts/ast_audit.js and node
scripts/testall_runtime.js) or by wrapping each invocation with a short
conditional that prints a descriptive error message including the script name
and exit code before exiting non‑zero, so CI logs show clear diagnostic context
for failures.

In `@scripts/testall_runtime.js`:
- Around line 228-344: The IIFE currently restores process.exit only at the end
so an exception leaves the monkey-patch in place; wrap the main async body of
the IIFE in try { ... } finally { process.exit = originalProcessExit; } (or add
a finally block around the await dispatch loops and report generation) so that
process.exit is always restored even if dispatchCommand/dispatchCallback or file
writes throw; reference the existing identifiers process.exit,
originalProcessExit, the top-level async () => { ... } IIFE, and the
dispatchCommand/dispatchCallback/report write sections when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9d482878-f320-4b55-8ffd-d8a323e4371f

📥 Commits

Reviewing files that changed from the base of the PR and between 3ab3ea3 and c79f804.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (17)
  • AGENTS/SESSION_LOG.md
  • RUNEWAGER_FUNCTIONALITY_MAP.md
  • docs/INDEX.md
  • docs/features/10-deploy-ops.md
  • index.js
  • package.json
  • qa/context/bot_capabilities.json
  • qa/context/repo_info.json
  • qa/state/provider_status.json
  • reports/ast_audit.json
  • reports/structural_audit.md
  • reports/testall_report.json
  • reports/testall_report.md
  • reports/testall_report.txt
  • scripts/ast_audit.js
  • scripts/run_testall.sh
  • scripts/testall_runtime.js

Comment thread docs/INDEX.md
**Last updated:** 2026-03-01
**Bot version:** 3.0.0 | `index.js`: ~15,050 lines | Commands: 95 | Action handlers: 270+
**Last updated:** 2026-03-04
**Bot version:** 3.0.0 | `index.js`: ~15,100 lines | Commands: 96 registered (`95` normalized group-command entries) | Action handlers: 268
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Resolve internal line-count mismatch for index.js.

Line 6 says ~15,100 lines, but Line 174 still says ~14,960 lines. Please keep one canonical value to avoid drift in the index.

Based on learnings: maintain docs/INDEX.md as a universal wiring index/source of truth.

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

In `@docs/INDEX.md` at line 6, Update the canonical line-count value in
docs/INDEX.md so all occurrences match; specifically, reconcile the two
differing strings "**Bot version:** 3.0.0 | `index.js`: ~15,100 lines |
Commands: 96 registered (`95` normalized group-command entries) | Action
handlers: 268" and the other entry that reads "`index.js`: ~14,960 lines" by
choosing one canonical count (prefer the updated "~15,100 lines"), and search
the file for any other `index.js` line-count mentions and replace them to be
consistent.

Comment thread index.js
Comment on lines +14207 to +14221
if (originalCtxFns.reply) {
const totalPassed = reportData.summary
? reportData.summary.commandsPassed + reportData.summary.callbacksPassed
: (reportData.commandResults.filter((r) => r.ok).length + reportData.callbackResults.filter((r) => r.ok && !r.skipped).length);
const totalWarnings = reportData.summary
? reportData.summary.warnings
: reportData.steps.filter((s) => s.warning).length;
const totalFailed = reportData.summary
? reportData.summary.commandsFailed + reportData.summary.callbacksFailed
: (reportData.commandResults.filter((r) => !r.ok).length + reportData.callbackResults.filter((r) => !r.ok && !r.skipped).length);
const quickSummary = `🧪 ${legacyTestAllSummaryFormat
.replace('${totalPassed}', String(totalPassed))
.replace('${totalWarnings}', String(totalWarnings))
.replace('${totalFailed}', String(totalFailed))}`;
try { await originalCtxFns.reply(quickSummary); } catch (_) {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bug: Summary references undefined fields, resulting in NaN values.

The totals object (line 14124-14131) only defines commandsFailed, callbacksFailed, adminFlowsFailed, etc., but the summary calculation attempts to access commandsPassed, callbacksPassed, and warnings, which are never set:

const totalPassed = reportData.summary
  ? reportData.summary.commandsPassed + reportData.summary.callbacksPassed  // undefined + undefined = NaN

Since reportData.summary is always truthy after line 14131, this will produce NaN in the final summary message, violating the required format: "TestAll complete — X passed, Y warnings, Z failures."

🐛 Proposed fix to compute passed/warnings correctly
   const totals = {
     commandsFailed: reportData.commandResults.filter((r) => !r.ok).length,
     callbacksFailed: reportData.callbackResults.filter((r) => !r.ok).length,
     adminFlowsFailed: reportData.adminFlowResults.filter((r) => !r.ok).length,
     callbacksFallbackHandled: reportData.callbackResults.filter((r) => r.fallbackHit).length,
     durationMs: Date.now() - startTs,
+    commandsPassed: reportData.commandResults.filter((r) => r.ok).length,
+    callbacksPassed: reportData.callbackResults.filter((r) => r.ok && !r.skipped).length,
+    warnings: reportData.steps.filter((s) => s.warning).length,
   };
   reportData.summary = totals;

Based on learnings: "/testall updates in the Runewager bot must preserve structured category checks and final summary: TestAll complete — X passed, Y warnings, Z failures."

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

In `@index.js` around lines 14207 - 14221, The summary code is reading
reportData.summary.commandsPassed, callbacksPassed, and warnings which are not
set and cause NaN; update the logic that builds totalPassed, totalWarnings, and
totalFailed to derive values from reportData.commandResults,
reportData.callbackResults, and reportData.steps when the summary fields are
missing (or ensure reportData.summary populates commandsPassed, callbacksPassed,
and warnings beforehand). Specifically, modify the quick-summary calculation
around originalCtxFns.reply/legacyTestAllSummaryFormat to compute totals with
reportData.commandResults.filter(r => r.ok).length,
reportData.callbackResults.filter(r => r.ok && !r.skipped).length, and
reportData.steps.filter(s => s.warning).length (and analogous failing counts)
whenever reportData.summary.commandsPassed/callbacksPassed/warnings are
undefined so the final message always yields "X passed, Y warnings, Z failures."

Comment thread package.json
Comment on lines +34 to +37
"devDependencies": {
"@babel/parser": "^7.29.0",
"@babel/traverse": "^7.29.0",
"@babel/types": "^7.29.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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's understand the repository structure
git ls-files | head -20

Repository: gamblecodezcom/Runewager

Length of output: 475


🏁 Script executed:

# Check package.json structure and dependencies
cat -n package.json

Repository: gamblecodezcom/Runewager

Length of output: 1552


🏁 Script executed:

# Search for /testall implementation and ast_audit references
rg -n "testall|ast_audit" --max-count=20

Repository: gamblecodezcom/Runewager

Length of output: 16326


🏁 Script executed:

# Check prod-run.sh and deployment scripts
git ls-files | grep -E "(prod|deploy|ci)" | head -20

Repository: gamblecodezcom/Runewager

Length of output: 291


🏁 Script executed:

# Check if Babel is actually imported in runtime files
rg -n "@babel/(parser|traverse|types)" --type=js | head -20

Repository: gamblecodezcom/Runewager

Length of output: 207


🏁 Script executed:

# Check prod-run.sh for npm ci --omit=dev usage
cat -n prod-run.sh

Repository: gamblecodezcom/Runewager

Length of output: 23716


🏁 Script executed:

# Check ast_audit.js to see if all three Babel packages are actually used
cat -n scripts/ast_audit.js | head -50

Repository: gamblecodezcom/Runewager

Length of output: 2733


🏁 Script executed:

# Check if `@babel/types` is used in ast_audit.js
rg -n "@babel/types|types\." scripts/ast_audit.js | head -10

Repository: gamblecodezcom/Runewager

Length of output: 50


🏁 Script executed:

# Check if /testall is invoked during production runtime or CI
rg -n "testall|ast_audit" prod-run.sh deploy.sh .github/workflows/deploy.yml .github/workflows/ci.yml

Repository: gamblecodezcom/Runewager

Length of output: 50


🏁 Script executed:

# Check when ast_audit.js runs: is it only at deploy time or at runtime?
rg -n "ast_audit\.js" index.js | head -5

Repository: gamblecodezcom/Runewager

Length of output: 296


🏁 Script executed:

# Check if `@babel/types` is imported anywhere in ast_audit.js
cat -n scripts/ast_audit.js | grep -i "types" | head -20

Repository: gamblecodezcom/Runewager

Length of output: 260


🏁 Script executed:

# Verify /testall command definition in index.js to confirm it's production-facing
rg -B2 -A5 "bot\.command\('testall'" index.js

Repository: gamblecodezcom/Runewager

Length of output: 361


🏁 Script executed:

# Check if /testall appears in any production docs/definitions
rg -n "testall" docs/features/10-deploy-ops.md | head -10

Repository: gamblecodezcom/Runewager

Length of output: 813


🏁 Script executed:

# More thorough check: search entire ast_audit.js for `@babel` imports and usage
cat scripts/ast_audit.js | head -100

Repository: gamblecodezcom/Runewager

Length of output: 5715


🏁 Script executed:

# Check if `@babel/types` is required as a peer dependency or indirect import
grep -r "@babel/types" scripts/ index.js 2>/dev/null || echo "No `@babel/types` found"

Repository: gamblecodezcom/Runewager

Length of output: 89


Move @babel/parser and @babel/traverse to dependencies—they are required at runtime by /testall.

/testall executes scripts/ast_audit.js at production runtime, which imports @babel/parser and @babel/traverse. When prod-run.sh uses npm ci --omit=dev, these devDependencies are excluded, causing a module-not-found error. Note: @babel/types is not imported and should not be moved.

💡 Correct fix (move only the two packages actually used by ast_audit.js)
   "dependencies": {
     "dotenv": "^16.6.1",
     "express": "4.22.1",
     "rotating-file-stream": "^3.2.9",
-    "telegraf": "^4.16.3"
+    "telegraf": "^4.16.3",
+    "@babel/parser": "^7.29.0",
+    "@babel/traverse": "^7.29.0"
   },
   "devDependencies": {
-    "@babel/parser": "^7.29.0",
-    "@babel/traverse": "^7.29.0",
     "@babel/types": "^7.29.0"
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 34 - 37, Update package.json so `@babel/parser` and
`@babel/traverse` are moved from devDependencies into dependencies because
/testall runs scripts/ast_audit.js at production runtime and requires those
modules; leave `@babel/types` in devDependencies (it isn’t imported). Edit the
"devDependencies" and "dependencies" objects to remove the two packages from
devDependencies and add them to dependencies with the same version ranges,
ensuring production installs (npm ci --omit=dev) will include them.

Comment thread scripts/ast_audit.js
if (callee === 'bot.use') data.botUses.push({ file: rfile, line, middleware: getCalleeName(n.arguments[0]) || n.arguments[0]?.type });
if (callee === 'ctx.scene.enter' || callee === 'scene.enter') data.scene.enter.push({ file: rfile, line, scene: evalStaticString(n.arguments[0]) });
if (callee === 'ctx.scene.leave' || callee === 'scene.leave') data.scene.leave.push({ file: rfile, line });
if (callee === 'Stage' || callee === 'new Stage') data.scene.stageRegs.push({ file: rfile, line });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Examine the Stage detection and visitor structure
cat -n scripts/ast_audit.js | head -120

Repository: gamblecodezcom/Runewager

Length of output: 7683


🏁 Script executed:

#!/bin/bash
# Check for NewExpression and CallExpression visitors
rg -n "NewExpression|CallExpression" scripts/ast_audit.js

Repository: gamblecodezcom/Runewager

Length of output: 733


🏁 Script executed:

#!/bin/bash
# Check what visitors are actually defined
rg -n "^\s*(CallExpression|NewExpression|FunctionDeclaration|VariableDeclarator|AssignmentExpression)" scripts/ast_audit.js

Repository: gamblecodezcom/Runewager

Length of output: 157


🏁 Script executed:

#!/bin/bash
# Search for stageRegs references and Stage detection logic
rg -n "stageRegs|Stage.*new|new.*Stage" scripts/ast_audit.js

Repository: gamblecodezcom/Runewager

Length of output: 466


new Stage(...) registrations are never detected because they use NewExpression, not CallExpression.

The code at line 95 is inside a CallExpression visitor, but new Stage(...) creates a NewExpression node that is never visited. The condition callee === 'new Stage' is unreachable dead code. Add a NewExpression visitor to detect Stage registrations using the new keyword:

Proposed fix
   traverse(ast, {
+    NewExpression(p) {
+      const n = p.node;
+      const line = n.loc?.start?.line || 0;
+      if (getCalleeName(n.callee) === 'Stage') {
+        data.scene.stageRegs.push({ file: rfile, line });
+      }
+    },
     FunctionDeclaration(p) {
       if (p.node.id?.name) {
         let found = false;
         p.traverse({ CallExpression(cp) { if (getCalleeName(cp.node.callee) === 'requireAdmin') found = true; } });
         functionAdminMap.set(`${rfile}:${p.node.id.name}`, found);
       }
     },
     CallExpression(p) {
       const n = p.node; const callee = getCalleeName(n.callee); const line = n.loc?.start?.line || 0;
       ...
-      if (callee === 'Stage' || callee === 'new Stage') data.scene.stageRegs.push({ file: rfile, line });
+      if (callee === 'Stage') data.scene.stageRegs.push({ file: rfile, line });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/ast_audit.js` at line 95, The CallExpression visitor currently checks
for callee === 'Stage' and also has an unreachable callee === 'new Stage'
branch; add a NewExpression visitor that mirrors the CallExpression logic so
registrations created via "new Stage(...)" are captured: detect nodes of type
NewExpression where node.callee.name === 'Stage' and push the same registration
into data.scene.stageRegs (using the same file/line variables), and remove or
ignore the 'new Stage' string branch in the CallExpression check to avoid dead
code.

Comment thread scripts/ast_audit.js
Comment on lines +146 to +147
fs.writeFileSync('reports/ast_audit.json', JSON.stringify(report, null, 2));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Create reports/ before writing artifacts to avoid ENOENT on clean runs.

Writes at Line 146 and Line 208 assume reports/ already exists; deterministic audits should ensure the directory exists.

🛠️ Proposed fix
 const report = { scannedFiles:data.files.length, jsTsFiles:data.files.filter(f=>/\.(js|ts)$/.test(f)).length, ...data, missingWhitelist, orphanWhitelist, duplicateCommands, deadButtons, fallbackOnly, fallback, possibleAdminUngated, overlap };
-fs.writeFileSync('reports/ast_audit.json', JSON.stringify(report, null, 2));
+const reportsDir = path.join(root, 'reports');
+fs.mkdirSync(reportsDir, { recursive: true });
+fs.writeFileSync(path.join(reportsDir, 'ast_audit.json'), JSON.stringify(report, null, 2));
@@
-fs.writeFileSync('reports/structural_audit.md', md);
+fs.writeFileSync(path.join(reportsDir, 'structural_audit.md'), md);
 console.log('Wrote reports/ast_audit.json and reports/structural_audit.md');

Also applies to: 208-208

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

In `@scripts/ast_audit.js` around lines 146 - 147, The script currently calls
fs.writeFileSync('reports/ast_audit.json', ...) (and the similar write at Line
208) without ensuring the reports/ directory exists; create the reports
directory before any writes by invoking fs.mkdirSync('reports', { recursive:
true }) (or equivalent async mkdir) early in the script (before the
fs.writeFileSync calls) so both fs.writeFileSync usages (the
'reports/ast_audit.json' writes) will never fail on clean runs.

Comment thread scripts/testall_runtime.js
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (3)
scripts/testall_runtime.js (1)

254-254: ⚠️ Potential issue | 🟠 Major

Fallback detection misses answerCbQuery alias (reporting false negatives).

Line 254 only checks answerCallbackQuery. In this codebase, callback acknowledgements also use answerCbQuery, so fallbackHandled can be undercounted.

💡 Proposed fix
-  const fallback = previews.some((t) => t.includes('That button is no longer active')) || calls.some((c) => c.method === 'answerCallbackQuery' && String(c.payload.text || '').includes('Action not available anymore'));
+  const fallback =
+    previews.some((t) => t.includes('That button is no longer active')) ||
+    calls.some((c) =>
+      (c.method === 'answerCallbackQuery' || c.method === 'answerCbQuery') &&
+      String(c.payload.text || '').includes('Action not available anymore')
+    );
#!/bin/bash
set -euo pipefail

# Verify both callback-answer aliases are used and inspect fallback predicate.
rg -nP '\banswerCbQuery\b|\banswerCallbackQuery\b' index.js telegramSafe.js scripts/testall_runtime.js
sed -n '248,260p' scripts/testall_runtime.js
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/testall_runtime.js` at line 254, The fallback detection currently
only checks for calls with method === 'answerCallbackQuery', missing the
'answerCbQuery' alias; update the predicate that sets the fallback variable
(which inspects previews and calls) to treat both 'answerCallbackQuery' and
'answerCbQuery' as equivalent — e.g., change the calls.some check to match
c.method against both names (or normalize c.method to lowercase and test for
both aliases) so fallback counts include either callback-answer alias.
docs/INDEX.md (1)

6-6: ⚠️ Potential issue | 🟡 Minor

Use one canonical index.js line count across the doc.

Line 6 says ~15,100, but Architecture Notes still shows ~14,960, so the index currently conflicts with itself.

🛠️ Suggested alignment
-- **Single-file monolith:** All logic in `index.js` (~14,960 lines).
+- **Single-file monolith:** All logic in `index.js` (~15,100 lines).

Based on learnings: maintain docs/INDEX.md as a universal wiring index/source of truth.

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

In `@docs/INDEX.md` at line 6, The document shows two conflicting index.js
line-counts: the "**Bot version:**" summary uses "~15,100" while the
"Architecture Notes" section shows "~14,960"; pick one canonical line-count and
update all occurrences to match (search for the exact strings "~15,100" and
"~14,960" and replace with the chosen canonical value), and ensure the "**Bot
version:**" summary and the "Architecture Notes" block are consistent in
docs/INDEX.md so the file serves as the single source of truth.
index.js (1)

14138-14146: ⚠️ Potential issue | 🟠 Major

Quick summary still risks NaN counters due to missing summary fields.

reportData.summary is always truthy, but commandsPassed, callbacksPassed, and warnings are not populated before the quick-summary calculation.

🐛 Proposed fix
     const totals = {
       commandsFailed: reportData.commandResults.filter((r) => !r.ok).length,
       callbacksFailed: reportData.callbackResults.filter((r) => !r.ok).length,
       adminFlowsFailed: reportData.adminFlowResults.filter((r) => !r.ok).length,
       callbacksFallbackHandled: reportData.callbackResults.filter((r) => r.fallbackHit).length,
+      commandsPassed: reportData.commandResults.filter((r) => r.ok && !r.skipped).length,
+      callbacksPassed: reportData.callbackResults.filter((r) => r.ok && !r.skipped).length,
+      warnings: reportData.steps.filter((s) => s.warning).length,
       durationMs: Date.now() - startTs,
     };
@@
-    const totalPassed = reportData.summary
-      ? reportData.summary.commandsPassed + reportData.summary.callbacksPassed
+    const totalPassed = reportData.summary
+      ? ((reportData.summary.commandsPassed ?? 0) + (reportData.summary.callbacksPassed ?? 0))
       : (reportData.commandResults.filter((r) => r.ok).length + reportData.callbackResults.filter((r) => r.ok && !r.skipped).length);
     const totalWarnings = reportData.summary
-      ? reportData.summary.warnings
+      ? (reportData.summary.warnings ?? 0)
       : reportData.steps.filter((s) => s.warning).length;

Based on learnings: "/testall updates in the Runewager bot must preserve structured category checks and final summary: TestAll complete — X passed, Y warnings, Z failures."

Also applies to: 14221-14230

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

In `@index.js` around lines 14138 - 14146, The quick-summary calculation builds
totals from reportData but never ensures summary fields like commandsPassed,
callbacksPassed, and warnings exist, which can produce NaN; before computing
totals (the block that creates totals and assigns reportData.summary) initialize
or compute commandsPassed = reportData.commandResults.length - commandsFailed
(or 0 if missing), callbacksPassed similarly from callbackResults, and warnings
= reportData.warnings || 0, then include these properties in the totals object
and set reportData.summary = totals; also keep the existing status-setting logic
that flips reportData.status = 'issues' when any failed counters are nonzero
(apply the same fix to the other quick-summary block that computes a final
summary).
🧹 Nitpick comments (2)
AGENTS/SESSION_LOG.md (2)

83-85: Consider varying sentence structure.

Three consecutive bullet points begin with "Added," which slightly reduces readability. Consider rephrasing for variety.

✍️ Optional rewording
 - Added ephemeral runtime snapshot + rollback helpers (`createTestAllStateSnapshot`, `restoreTestAllStateSnapshot`) covering user/promo/giveaway/tips/promo-history/smart-button/sshv runtime stores.
-- Added synthetic command/callback simulation through `bot.handleUpdate` for all `BOT_KNOWN_COMMANDS` entries and AST-discovered actions (literal + representative regex callback samples), with fallback/unmatched callback capture.
-- Added deterministic report persistence and batched admin DM summary output for `/testall` (`reports/testall_report.json`, `reports/testall_report.md`).
+- Introduced synthetic command/callback simulation through `bot.handleUpdate` for all `BOT_KNOWN_COMMANDS` entries and AST-discovered actions (literal + representative regex callback samples), with fallback/unmatched callback capture.
+- Implemented deterministic report persistence and batched admin DM summary output for `/testall` (`reports/testall_report.json`, `reports/testall_report.md`).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS/SESSION_LOG.md` around lines 83 - 85, The three bullets in
SESSION_LOG.md all start with "Added", so reword them for variety: keep the same
technical details but modify the sentence openings and structure around the
symbols createTestAllStateSnapshot, restoreTestAllStateSnapshot,
bot.handleUpdate, BOT_KNOWN_COMMANDS, and the reports paths
(reports/testall_report.json, reports/testall_report.md); for example make one
bullet start with a noun phrase (e.g., "Ephemeral runtime snapshot + rollback
helpers: createTestAllStateSnapshot/restoreTestAllStateSnapshot..."), another
with an action phrase for the synthetic command simulation via bot.handleUpdate
and BOT_KNOWN_COMMANDS, and a third that emphasizes output/persistence for
deterministic report persistence and batched admin DM summary
(reports/testall_report.*), ensuring each bullet conveys the same content but
uses varied openings and sentence structures.

81-113: Align entry format with established session log convention.

The six new entries (lines 81-113) deviate from the format established in the file header (lines 3-4) and demonstrated in earlier entries:

  1. Heading level: Uses ## instead of ###
  2. Missing structured fields: Lacks the explicit Scope:, Changes:, Tests:, Map updated:, Index updated:, Open items: sections

This inconsistency makes the log harder to scan. Consider reformatting these entries to match the pattern used in lines 8-80.

📋 Example of aligned format for the first new entry
-## 2026-03-04 — /testall comprehensive self-contained runner hardening
-- Reworked `/testall` in `index.js` to enforce admin-only entry via `isAdmin(ctx)` and run a full internal step registry with progress updates, non-blocking step failure handling, and deterministic report generation.
-- Added ephemeral runtime snapshot + rollback helpers (`createTestAllStateSnapshot`, `restoreTestAllStateSnapshot`) covering user/promo/giveaway/tips/promo-history/smart-button/sshv runtime stores.
-- Added synthetic command/callback simulation through `bot.handleUpdate` for all `BOT_KNOWN_COMMANDS` entries and AST-discovered actions (literal + representative regex callback samples), with fallback/unmatched callback capture.
-- Added deterministic report persistence and batched admin DM summary output for `/testall` (`reports/testall_report.json`, `reports/testall_report.md`).
-- Synced documentation: `docs/INDEX.md` and `RUNEWAGER_FUNCTIONALITY_MAP.md` now reflect the expanded `/testall` behavior and rollback/report invariants.
-- Regenerated AST and runtime reports.
+### 2026-03-04 — /testall comprehensive self-contained runner hardening
+
+**Scope:** `index.js`, `scripts/testall_runtime.js`, `reports/`, `docs/INDEX.md`, `RUNEWAGER_FUNCTIONALITY_MAP.md`
+
+**Changes:**
+- Reworked `/testall` in `index.js` to enforce admin-only entry via `isAdmin(ctx)` and run a full internal step registry with progress updates, non-blocking step failure handling, and deterministic report generation.
+- Added ephemeral runtime snapshot + rollback helpers (`createTestAllStateSnapshot`, `restoreTestAllStateSnapshot`) covering user/promo/giveaway/tips/promo-history/smart-button/sshv runtime stores.
+- Added synthetic command/callback simulation through `bot.handleUpdate` for all `BOT_KNOWN_COMMANDS` entries and AST-discovered actions (literal + representative regex callback samples), with fallback/unmatched callback capture.
+- Added deterministic report persistence and batched admin DM summary output for `/testall` (`reports/testall_report.json`, `reports/testall_report.md`).
+- Synced documentation: `docs/INDEX.md` and `RUNEWAGER_FUNCTIONALITY_MAP.md` now reflect the expanded `/testall` behavior and rollback/report invariants.
+- Regenerated AST and runtime reports.
+
+**Tests:** `npm test` (60/60 pass); generated reports validated
+**Map updated:** yes
+**Index updated:** yes
+**Open items:** none
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS/SESSION_LOG.md` around lines 81 - 113, The six new changelog entries
(titles like "/testall comprehensive self-contained runner hardening", "/testall
rotating log retention and report-linking", etc.) use "##" headings and lack the
standard structured fields; update each entry to use the established "###"
heading level and add the explicit sections "Scope:", "Changes:", "Tests:", "Map
updated:", "Index updated:", and "Open items:" with concise content mirroring
the current prose (e.g., put the bulletized details under "Changes:", any
test/CI notes under "Tests:", and references to docs/AST/runtime updates under
"Map updated:"/"Index updated:"); keep the existing sentences but reorganize
them into those fields for each of the six entries so they match the formatting
pattern used earlier in the document.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@index.js`:
- Around line 13934-13966: The patch currently overwrites global bot.telegram
methods (sendMessage, editMessageText, answerCallbackQuery, deleteMessage,
pinChatMessage, unpinChatMessage, sendPhoto, sendAnimation, sendDocument)
causing real traffic to be routed to test stubs during /testall; fix by scoping
the monkey-patch so originals (originalTelegramMethods) are only replaced for
the duration of a single /testall run and restored immediately after, or by
adding a runtime check inside each patched wrapper (e.g., in the functions
passed to rateLimitedTestallCall or the start/stop testall handler) to forward
to originalTelegramMethods when the current update is not a testall context;
ensure you restore all originalTelegramMethods on test completion and on error
paths so live updates are never left using the test stubs.
- Around line 13773-13785: pruneFallbackLogs currently only deletes extra
rotated files but does not prevent the active "testall.log" from growing; modify
pruneFallbackLogs (and where fallback logging writes) to implement size-based
rollover: when "testall.log" in testAllLogDir exceeds a threshold (e.g., maxSize
bytes) rename the current "testall.log" to a sequenced rotated name like
"testall.log.1", "testall.log.2", etc. (shifting/renaming older rotated files
upward and dropping the oldest if > maxFiles), then create a fresh empty
"testall.log" for new appends; ensure the function operates on the symbols
pruneFallbackLogs, testAllLogDir, and the "testall.log" base name, uses
fs.statSync to check size, fs.renameSync to rotate, and then deletes extras as
it already does so that single-file growth is bounded by rotation and pruning.
- Around line 14122-14126: The admin command loop is causing recursive
self-invocation because safeInvokeCommand('testall') re-enters the same handler;
fix it by preventing re-entry: either filter out 'testall' from adminCommands
before the loop or add a guard in the loop that skips invoking when command ===
'testall' and the current execution context is already running 'testall';
alternatively implement a small reentrancy guard inside safeInvokeCommand (e.g.,
a Set runningCommands that records command names on entry and returns/throws if
the command is already present) and ensure reportData.adminFlowResults still
receives a sensible skipped result for visibility.

In `@RUNEWAGER_FUNCTIONALITY_MAP.md`:
- Line 414: Update the `/testall` entry in RUNEWAGER_FUNCTIONALITY_MAP.md to
list the TXT artifact alongside JSON and MD by changing the sentence that
currently mentions only `reports/testall_report.json` and
`reports/testall_report.md` to include `reports/testall_report.txt`; reference
the `/testall` command and the `reports/testall_report.txt` artifact so the doc
matches the runtime behavior.

---

Duplicate comments:
In `@docs/INDEX.md`:
- Line 6: The document shows two conflicting index.js line-counts: the "**Bot
version:**" summary uses "~15,100" while the "Architecture Notes" section shows
"~14,960"; pick one canonical line-count and update all occurrences to match
(search for the exact strings "~15,100" and "~14,960" and replace with the
chosen canonical value), and ensure the "**Bot version:**" summary and the
"Architecture Notes" block are consistent in docs/INDEX.md so the file serves as
the single source of truth.

In `@index.js`:
- Around line 14138-14146: The quick-summary calculation builds totals from
reportData but never ensures summary fields like commandsPassed,
callbacksPassed, and warnings exist, which can produce NaN; before computing
totals (the block that creates totals and assigns reportData.summary) initialize
or compute commandsPassed = reportData.commandResults.length - commandsFailed
(or 0 if missing), callbacksPassed similarly from callbackResults, and warnings
= reportData.warnings || 0, then include these properties in the totals object
and set reportData.summary = totals; also keep the existing status-setting logic
that flips reportData.status = 'issues' when any failed counters are nonzero
(apply the same fix to the other quick-summary block that computes a final
summary).

In `@scripts/testall_runtime.js`:
- Line 254: The fallback detection currently only checks for calls with method
=== 'answerCallbackQuery', missing the 'answerCbQuery' alias; update the
predicate that sets the fallback variable (which inspects previews and calls) to
treat both 'answerCallbackQuery' and 'answerCbQuery' as equivalent — e.g.,
change the calls.some check to match c.method against both names (or normalize
c.method to lowercase and test for both aliases) so fallback counts include
either callback-answer alias.

---

Nitpick comments:
In `@AGENTS/SESSION_LOG.md`:
- Around line 83-85: The three bullets in SESSION_LOG.md all start with "Added",
so reword them for variety: keep the same technical details but modify the
sentence openings and structure around the symbols createTestAllStateSnapshot,
restoreTestAllStateSnapshot, bot.handleUpdate, BOT_KNOWN_COMMANDS, and the
reports paths (reports/testall_report.json, reports/testall_report.md); for
example make one bullet start with a noun phrase (e.g., "Ephemeral runtime
snapshot + rollback helpers:
createTestAllStateSnapshot/restoreTestAllStateSnapshot..."), another with an
action phrase for the synthetic command simulation via bot.handleUpdate and
BOT_KNOWN_COMMANDS, and a third that emphasizes output/persistence for
deterministic report persistence and batched admin DM summary
(reports/testall_report.*), ensuring each bullet conveys the same content but
uses varied openings and sentence structures.
- Around line 81-113: The six new changelog entries (titles like "/testall
comprehensive self-contained runner hardening", "/testall rotating log retention
and report-linking", etc.) use "##" headings and lack the standard structured
fields; update each entry to use the established "###" heading level and add the
explicit sections "Scope:", "Changes:", "Tests:", "Map updated:", "Index
updated:", and "Open items:" with concise content mirroring the current prose
(e.g., put the bulletized details under "Changes:", any test/CI notes under
"Tests:", and references to docs/AST/runtime updates under "Map updated:"/"Index
updated:"); keep the existing sentences but reorganize them into those fields
for each of the six entries so they match the formatting pattern used earlier in
the document.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0307dc61-bbfc-4a26-b21a-ae619f07a509

📥 Commits

Reviewing files that changed from the base of the PR and between c79f804 and 5b4cf02.

📒 Files selected for processing (12)
  • AGENTS/SESSION_LOG.md
  • RUNEWAGER_FUNCTIONALITY_MAP.md
  • docs/INDEX.md
  • docs/features/10-deploy-ops.md
  • index.js
  • rateLimiter.js
  • reports/ast_audit.json
  • reports/structural_audit.md
  • reports/testall_report.json
  • reports/testall_report.md
  • reports/testall_report.txt
  • scripts/testall_runtime.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • reports/testall_report.json

Comment thread index.js
Comment on lines +13773 to +13785
function pruneFallbackLogs(maxFiles = 14) {
try {
const files = fs.readdirSync(testAllLogDir)
.filter((f) => f.startsWith('testall.log'))
.sort((a, b) => a.localeCompare(b));
const extra = Math.max(0, files.length - maxFiles);
for (let i = 0; i < extra; i += 1) {
fs.unlinkSync(path.join(testAllLogDir, files[i]));
}
} catch (_) {
// non-fatal
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fallback logger does not actually rotate/truncate the active log file.

In fallback mode, writes always append to testall.log; pruneFallbackLogs() only deletes extra files, so single-file growth is unbounded.

🛠️ Proposed fix (size-based rollover in fallback mode)
   const fallbackLogPath = path.join(testAllLogDir, 'testall.log');

+  function rotateFallbackIfNeeded(maxBytes = 10 * 1024 * 1024) {
+    try {
+      const st = fs.existsSync(fallbackLogPath) ? fs.statSync(fallbackLogPath) : null;
+      if (!st || st.size < maxBytes) return;
+      const rotatedName = `testall.log.${new Date().toISOString().replace(/[:.]/g, '-')}`;
+      fs.renameSync(fallbackLogPath, path.join(testAllLogDir, rotatedName));
+      pruneFallbackLogs(14);
+    } catch (_) {
+      // non-fatal
+    }
+  }
+
   const log = (level, message, extra = {}) => {
@@
     try {
+      if (usingFallback) rotateFallbackIfNeeded();
       fs.appendFileSync(fallbackLogPath, line, 'utf8');
-      if (usingFallback) pruneFallbackLogs(14);
+      if (usingFallback) pruneFallbackLogs(14);
     } catch (_) {

Also applies to: 13795-13798

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

In `@index.js` around lines 13773 - 13785, pruneFallbackLogs currently only
deletes extra rotated files but does not prevent the active "testall.log" from
growing; modify pruneFallbackLogs (and where fallback logging writes) to
implement size-based rollover: when "testall.log" in testAllLogDir exceeds a
threshold (e.g., maxSize bytes) rename the current "testall.log" to a sequenced
rotated name like "testall.log.1", "testall.log.2", etc. (shifting/renaming
older rotated files upward and dropping the oldest if > maxFiles), then create a
fresh empty "testall.log" for new appends; ensure the function operates on the
symbols pruneFallbackLogs, testAllLogDir, and the "testall.log" base name, uses
fs.statSync to check size, fs.renameSync to rotate, and then deletes extras as
it already does so that single-file growth is bounded by rotation and pruning.

Comment thread index.js
Comment on lines +13934 to +13966
const originalTelegramMethods = {
sendMessage: bot.telegram.sendMessage,
editMessageText: bot.telegram.editMessageText,
answerCallbackQuery: bot.telegram.answerCallbackQuery,
deleteMessage: bot.telegram.deleteMessage,
pinChatMessage: bot.telegram.pinChatMessage,
unpinChatMessage: bot.telegram.unpinChatMessage,
sendPhoto: bot.telegram.sendPhoto,
sendAnimation: bot.telegram.sendAnimation,
sendDocument: bot.telegram.sendDocument,
};

bot.botInfo = bot.botInfo || { id: 999, is_bot: true, first_name: 'RunewagerTest', username: 'RunewagerTestBot' };
bot.telegram.sendMessage = async (chatId, text, extra = {}) => rateLimitedTestallCall({ method: 'sendMessage', chatId }, async () => {
recordApiCall('sendMessage', { chat_id: chatId, text, ...extra });
testMsgId += 1;
return { message_id: testMsgId, date: Math.floor(Date.now() / 1000), chat: { id: chatId || 1, type: 'private' }, text: text || '' };
});
bot.telegram.editMessageText = async (chatId, messageId, inlineMessageId, text, extra = {}) => rateLimitedTestallCall({ method: 'editMessageText', globalOnly: true }, async () => {
recordApiCall('editMessageText', { chat_id: chatId, message_id: messageId, inline_message_id: inlineMessageId, text, ...extra });
return true;
});
bot.telegram.answerCallbackQuery = async (callbackQueryId, text, extra = {}) => rateLimitedTestallCall({ method: 'answerCallbackQuery', globalOnly: true }, async () => {
recordApiCall('answerCallbackQuery', { callback_query_id: callbackQueryId, text, ...extra });
return true;
});
bot.telegram.deleteMessage = async (...args) => rateLimitedTestallCall({ method: 'deleteMessage', globalOnly: true }, async () => { recordApiCall('deleteMessage', { args }); return true; });
bot.telegram.pinChatMessage = async (...args) => rateLimitedTestallCall({ method: 'pinChatMessage', globalOnly: true }, async () => { recordApiCall('pinChatMessage', { args }); return true; });
bot.telegram.unpinChatMessage = async (...args) => rateLimitedTestallCall({ method: 'unpinChatMessage', globalOnly: true }, async () => { recordApiCall('unpinChatMessage', { args }); return true; });
bot.telegram.sendPhoto = async (chatId, media, extra = {}) => rateLimitedTestallCall({ method: 'sendPhoto', chatId }, async () => { recordApiCall('sendPhoto', { chat_id: chatId, media, ...extra }); testMsgId += 1; return { message_id: testMsgId }; });
bot.telegram.sendAnimation = async (chatId, media, extra = {}) => rateLimitedTestallCall({ method: 'sendAnimation', chatId }, async () => { recordApiCall('sendAnimation', { chat_id: chatId, media, ...extra }); testMsgId += 1; return { message_id: testMsgId }; });
bot.telegram.sendDocument = async (chatId, media, extra = {}) => rateLimitedTestallCall({ method: 'sendDocument', chatId }, async () => { recordApiCall('sendDocument', { chat_id: chatId, media, ...extra }); testMsgId += 1; return { message_id: testMsgId }; });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Global Telegram API monkey-patching can impact live user traffic during /testall.

Overriding bot.telegram.* at process scope affects concurrent real updates while /testall runs, so normal sends can be diverted into test stubs.

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

In `@index.js` around lines 13934 - 13966, The patch currently overwrites global
bot.telegram methods (sendMessage, editMessageText, answerCallbackQuery,
deleteMessage, pinChatMessage, unpinChatMessage, sendPhoto, sendAnimation,
sendDocument) causing real traffic to be routed to test stubs during /testall;
fix by scoping the monkey-patch so originals (originalTelegramMethods) are only
replaced for the duration of a single /testall run and restored immediately
after, or by adding a runtime check inside each patched wrapper (e.g., in the
functions passed to rateLimitedTestallCall or the start/stop testall handler) to
forward to originalTelegramMethods when the current update is not a testall
context; ensure you restore all originalTelegramMethods on test completion and
on error paths so live updates are never left using the test stubs.

Comment thread index.js
Comment on lines +14122 to +14126
const adminCommands = ['admin', 'testall', 'testgiveaway', 'sshv', 'deploy_status', 'verify_bot_setup'];
for (const command of adminCommands) {
// eslint-disable-next-line no-await-in-loop
const result = await safeInvokeCommand(command);
reportData.adminFlowResults.push(result);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Prevent recursive /testall self-invocation during command simulation.

safeInvokeCommand('testall') re-enters the same handler while it is already running, which can cascade into nested /testall runs and runaway execution.

🐛 Proposed fix
   await withStep('Command Tests (BOT_KNOWN_COMMANDS)', async (step) => {
     const commands = Array.from(BOT_KNOWN_COMMANDS.values()).sort();
     for (const command of commands) {
+      if (command === 'testall') {
+        reportData.commandResults.push({
+          command,
+          ok: true,
+          skipped: true,
+          detail: 'Skipped to avoid recursive /testall invocation',
+          apiCalls: 0,
+        });
+        continue;
+      }
       // eslint-disable-next-line no-await-in-loop
       const result = await safeInvokeCommand(command);
       reportData.commandResults.push(result);
@@
-  await withStep('Admin Flow Tests', async (step) => {
-    const adminCommands = ['admin', 'testall', 'testgiveaway', 'sshv', 'deploy_status', 'verify_bot_setup'];
+  await withStep('Admin Flow Tests', async (step) => {
+    const adminCommands = ['admin', 'testgiveaway', 'sshv', 'deploy_status', 'verify_bot_setup'];

Also applies to: 14070-14085

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

In `@index.js` around lines 14122 - 14126, The admin command loop is causing
recursive self-invocation because safeInvokeCommand('testall') re-enters the
same handler; fix it by preventing re-entry: either filter out 'testall' from
adminCommands before the loop or add a guard in the loop that skips invoking
when command === 'testall' and the current execution context is already running
'testall'; alternatively implement a small reentrancy guard inside
safeInvokeCommand (e.g., a Set runningCommands that records command names on
entry and returns/throws if the command is already present) and ensure
reportData.adminFlowResults still receives a sensible skipped result for
visibility.

- 2026-03-04: Merged main branch additions — `telegramSafe.js` (global rate-limit patch via `telegramSafe.init(bot)`), `rateLimiter.js` (per-chat + global Telegram API rate limiter), `backend.js` (companion HTTP service on port 3001, `runewager-endpoint.service` systemd unit), updated `prod-run.sh`, `scripts/runewager_redeploy.sh`, `scripts/rw_cpu_guard.sh`.
- 2026-03-04: /runewager-audit — 17-phase full audit. Findings: 0 critical, 1 warning resolved (dead `adminKeyboard()` function removed — legacy promo keyboard with no callers). Auto-fix applied. 60/60 tests pass post-fix.
- 2026-03-04: Post-audit alignment verification pass — re-ran `node scripts/ast_audit.js` and regenerated `reports/ast_audit.json` + `reports/structural_audit.md`; confirmed command normalization residual is intentional (`/A` handled through lowercase `a` in group guard), fallback callback handler still preserves user-facing recovery while logging bounded unmatched callback context, and no Scene/Stage FSM wiring is currently present by design.
- 2026-03-04: Extended `/testall` to execute AST audit + deterministic internal update simulation (all discovered commands/callbacks), writing `reports/testall_report.json` and `reports/testall_report.md` while preserving existing admin-only behavior and summary format.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Include TXT artifact in the /testall output note for accuracy.

This line currently lists only JSON + MD, but the runtime also writes reports/testall_report.txt.

📝 Suggested doc correction
-- 2026-03-04: Extended `/testall` to execute AST audit + deterministic internal update simulation (all discovered commands/callbacks), writing `reports/testall_report.json` and `reports/testall_report.md` while preserving existing admin-only behavior and summary format.
+- 2026-03-04: Extended `/testall` to execute AST audit + deterministic internal update simulation (all discovered commands/callbacks), writing `reports/testall_report.json`, `reports/testall_report.md`, and `reports/testall_report.txt` while preserving existing admin-only behavior and summary format.

Based on learnings: implement behavior in code first, then sync RUNEWAGER_FUNCTIONALITY_MAP.md to match real behavior.

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

In `@RUNEWAGER_FUNCTIONALITY_MAP.md` at line 414, Update the `/testall` entry in
RUNEWAGER_FUNCTIONALITY_MAP.md to list the TXT artifact alongside JSON and MD by
changing the sentence that currently mentions only `reports/testall_report.json`
and `reports/testall_report.md` to include `reports/testall_report.txt`;
reference the `/testall` command and the `reports/testall_report.txt` artifact
so the doc matches the runtime behavior.

@gamblecodezcom gamblecodezcom merged commit 0ba298e into main Mar 6, 2026
4 checks passed
@gamblecodezcom gamblecodezcom deleted the codex/perform-full-structural-audit-for-runewager branch March 6, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant