Skip to content

feat: add atomic release operations and trusted PR dev - #335

Merged
mira-2026 merged 11 commits into
mainfrom
mira/release-ops-mobile-status
Jul 27, 2026
Merged

feat: add atomic release operations and trusted PR dev#335
mira-2026 merged 11 commits into
mainfrom
mira/release-ops-mobile-status

Conversation

@mira-2026

@mira-2026 mira-2026 commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • complete the atomic Dashboard release model with verified current and previous release status, guarded exact-SHA rollback, shared/exclusive lifecycle locking, and automatic restoration when rollback readiness fails
  • add one production-like Dashboard dev mode with hot reload, React Compiler, isolated Dashboard data, a writable workspace snapshot, an isolated scheduler/worker profile, and representative Gateway-backed data
  • add a trusted, managed single-slot PR-dev workflow to the Pull requests page, pinned to the selected PR head SHA and limited to backend-approved authors targeting main
  • keep the production Gateway credential outside candidate code and expose only the required chat/session/read capabilities through a host-owned loopback proxy with redacted config reads
  • repair managed-dev database, disk/network metrics, jobs, OpenClaw cron/settings, logs, frontend/backend identity, Bun-path, and release-manifest behavior
  • consolidate responsive system status in the header, keep chat model/thinking/speed badges on a dedicated mobile row, and polish the approved PR-page controls
  • clarify package scripts, add cached lint/format and changed-test workflows, update compatible dependencies, remove obsolete dev assets, and update the current documentation

Behavior and regression coverage

Release and rollback behavior

The Dashboard reports immutable managed release slots and queues deploy/rollback work through the exclusive job lifecycle. Rollback confirmation binds the full current and target commit SHAs at the API, worker, and lifecycle boundaries. If either slot changes before the release lock is acquired, the rollback fails instead of switching a different release.

Lifecycle status reads share the release lock and wait through active transitions. A rollback swaps the managed symlinks, restarts web and worker, requires commit-bound readiness, and restores the original release automatically if the rollback target does not become ready. Orphan recovery selects the guarded lifecycle from the persisted action context: deploy recovery uses the candidate lifecycle, while manual rollback recovery uses the original/current activation lifecycle that scheduled the rollback.

Development and PR-dev behavior

bun run dev starts the complete local stack from the repository. It uses isolated Dashboard state and workspace data, retains the production login and elevated-auth intervals, runs the isolated scheduler/worker profile, and connects to the live Gateway so agents, sessions, chat, files, cron metadata, and redacted settings remain representative. Synthetic rotating dev logs exercise the complete Logs UI without mounting production logs.

Copied WebAuthn credentials are retained only when the source and dev RP IDs match. Otherwise incompatible credentials are removed and MFA is disabled in the isolated snapshot so local password login and factor enrollment remain possible.

The Pull requests page can start or stop one trusted PR in dev:

  • the selected full PR head SHA is stored in the queued job and revalidated when the worker claims it
  • an exact detached checkout runs in a dedicated worktree
  • Tailscale HTTPS is published only after the managed frontend/backend pair is locally ready
  • Dashboard data and the writable workspace snapshot remain isolated
  • copied PostgreSQL/PgBouncer metrics are retained while isolated SQLite metrics refresh locally
  • disk and network metrics use the preview host filesystem/network view instead of the sandbox root
  • every Dashboard job stays visible, while jobs outside the isolated allowlist remain disabled and cannot execute
  • the production Gateway token is used only by a host-owned capability proxy and is removed from disk after proxy authentication
  • candidate code receives a separate disposable 0600 proxy credential; the production token is never mounted into the sandbox, sent to the browser, or included in a unit command
  • the proxy forwards only the explicit chat/session/read allowlist, redacts config.get, filters unrelated events, and rejects config writes, cron mutations, destructive session calls, and other host capabilities
  • separate resource-limited transient units run the proxy and candidate sandbox for at most four hours
  • stop and status reconciliation clean both units, materialized credentials, and the owned Serve route after normal stop, startup failure, missing/collected units, stale stopping state, or runtime expiry
  • failed cleanup remains retryable instead of being reported as stopped

A pre-existing Serve route that is not owned by the managed preview is rejected with a conflict.

Review findings included

  • append synthetic logs through a no-follow descriptor and enforce the byte cap with the pending record included
  • require copied database snapshots to include bloatEstimates
  • exclude common credential directories, filenames, key formats, and state files from workspace snapshots
  • re-scrub reused database MFA state when the active WebAuthn RP is incompatible and preserve the source RP inside the candidate sandbox
  • reject symlinked state/release directories before any host-side recursive preparation
  • remove a newly created Tailscale Serve route when activation verification fails
  • suppress production Mira task notifications while isolated dev safe mode is active
  • bind queued preview startup to the selected full commit SHA and fail if the PR head changes
  • keep the production Gateway token outside candidate code behind a host-owned capability proxy
  • reconcile preview cleanup when a collected systemd unit disappears
  • retain a retryable failed state when stop cleanup cannot complete
  • preserve failed preview status until cleanup succeeds or an explicit transition begins
  • select the candidate or activation lifecycle from the persisted deploy/rollback action during guarded orphan recovery
  • preserve and startup-validate the external HTTPS scheme forwarded through the local dev proxy
  • size preview execution deadlines to cover their bounded sequential lifecycle operations
  • reuse the exported Gateway pending-request limit in its boundary test
  • disable copied MFA and remove incompatible WebAuthn credentials when the dev RP differs
  • reconcile credentials and the owned Serve route after preview unit exit or expiry
  • let release status wait for an in-flight lifecycle transition
  • keep a global stop action after the preview PR disappears
  • publish Tailscale Serve only after the managed preview is ready
  • derive UI preview eligibility from the backend allowlist
  • bind rollback to the exact current and target SHAs across API and worker execution
  • use the lifecycle action key in recovery messages
  • check both deploy and rollback locks/jobs in recovery and fallback documentation
  • keep rollback systemd unit names distinct
  • use backend-provided commit URLs in the UI
  • read the OpenClaw config through one bounded O_NOFOLLOW descriptor
  • redact singular, plural, compound, and numbered secret-key variants from the dev config snapshot
  • close the SQLite snapshot handle even when transaction setup fails
  • return a bounded 400 response for a null rollback request body

Verification

  • bun run build:frontend
  • bun run build:backend
  • bun run lint:frontend
  • bun run lint:backend
  • bun run format:check
  • frontend: 478 tests, 0 failures, 94.55% line coverage
  • backend: 580 tests, 0 failures, 89.91% source-root line coverage
  • real WebSocket proxy integration runs in an isolated Bun child process to avoid Bun 1.3.14 arm64 coverage teardown crashes
  • coverage output now fails closed if Bun reports a panic despite returning exit code 0
  • staged diff whitespace, runtime-artifact, and credential-signature scans pass
  • production checkout remains clean
  • managed dev server, transient units, and Tailscale :5173 route are stopped
  • GitHub checks and Codex review are clean on final pushed head cb92bc20
  • CodeRabbit completed its substantive review on af4b8fc1; both reported nitpicks are fixed in cb92bc20
  • CodeRabbit's final incremental check reported Review rate limited after those fixes

Risk checklist

  • No persisted schema migration.
  • No production Gateway credential enters candidate code, browser payloads, unit arguments, or the repository.
  • Candidate host capabilities are constrained by sandboxing, an explicit proxy allowlist, redaction, event filtering, and transient-unit resource limits.
  • Cleanup failures remain visible and retryable; missing units and stale transitions reconcile owned credentials and routes.
  • No production merge, deploy, service restart, or preview runtime was performed.

Deployment/operations

  • Production deployment is required only after review and merge through the normal atomic release flow.
  • No secrets, runtime state, database snapshot, workspace snapshot, or production logs are committed.
  • The managed dev server, transient units, and Tailscale :5173 route are stopped.
  • The obsolete detached preview-pr-335 worktree was moved to the system trash after the final reviewed head was confirmed; the canonical branch worktree remains until merge.

Notes for reviewers

Dependency and tooling notes

  • Updated compatible patch/minor releases for @microlink/react-json-view, TanStack DB packages, lucide-react, ESLint, and globals.
  • TypeScript stays on 6.x for the typescript-eslint compiler API; the native TypeScript 7 build package remains separate.
  • ESLint and Prettier use content-based caches. Backend type checking uses incremental build info.
  • bun run test:changed selects frontend and backend tests affected by the Git working-tree diff. Full tests and coverage remain the before-push/CI gates.

Reviewer focus

Please focus on the release lock/rollback recovery contract and the trusted PR-dev boundary: exact-SHA selection, host proxy capability policy, isolated job profile, sandbox invocation, config redaction, token handling, readiness, resource limits, and cleanup ownership.

@mira-2026
mira-2026 requested a review from rajohan as a code owner July 26, 2026 13:05
@mira-2026 mira-2026 added priority: high High-priority work for the next active cycle status: needs-review Ready for human or agent review type: feature Adds user-visible functionality area: ops Operational actions, deploys, services, backups, and health area: frontend Frontend UI, client state, routing, and browser behavior area: backend Backend API, server routes, services, and integrations labels Jul 26, 2026
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds managed release status and rollback, trusted pull-request previews, isolated development tooling, development-safe request controls, frontend integrations, expanded tests, and updated CI and operational documentation.

Changes

Dashboard development and preview infrastructure

Layer / File(s) Summary
Isolated development stack
backend/src/development/*, backend/src/http.ts, backend/src/server.ts, scripts/development*
Adds isolated state preparation, sanitized workspace snapshots, scoped environments, cookie namespacing, loopback binding, job profiles, and Tailscale development routing.
Managed PR previews
backend/src/services/pullRequestPreview*.ts, backend/src/routes/pullRequestRoutes.ts, src/components/features/pullRequests/PullRequestPreviewCard.tsx
Adds trusted PR validation, worktree provisioning, sandboxed preview execution, lifecycle persistence, queued worker actions, API routes, Gateway proxying, and preview status rendering.

Managed release rollback

Layer / File(s) Summary
Release status and rollback pipeline
backend/src/services/pullRequests.ts, backend/src/release*.ts, backend/src/routes/pullRequestRoutes.ts
Adds managed release status, release-lock rollback jobs, readiness-bound detached rollback, restoration on failure, and deploy/rollback lifecycle cleanup.
Frontend release integration
src/hooks/*, src/components/features/pullRequests/ProductionReleasesCard.tsx, src/pages/PullRequests.tsx
Adds release queries, rollback mutations, release-slot rendering, rollback confirmation, preview controls, and cache invalidation.

Safety, verification, and documentation

Layer / File(s) Summary
Safety and system status
backend/src/requestPolicy.ts, backend/src/development/developmentGatewayPolicy.ts, src/components/layout/AppHeader.tsx, src/components/features/chat/ChatHeader.tsx
Blocks unsafe development host and Gateway mutations, adds system-status details, and makes chat badges responsive and testable.
Tests, CI, and documentation
backend/test/*, src/test/*, .github/*, docs/*, README.md, package.json
Adds coverage for previews, rollback, development isolation, safety controls, and status UI while updating scripts, CI commands, release artifacts, and operational guidance.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: type: security, area: openclaw, type: documentation

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main change: atomic release operations plus trusted PR dev support.
Description check ✅ Passed The description includes all required sections and enough concrete detail, with only minor template formatting differences.

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 42a96cf906

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/hooks/usePullRequests.ts
Comment thread src/hooks/usePullRequests.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
backend/src/services/pullRequests.ts (1)

498-537: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hardcoded "Deploy" wording in the release-lock recovery path.

Line 512 writes note: "Deploy execution ended before build completion" unconditionally when a stuck activeJob in "building" status is force-failed here — but activeJob can just as easily be a rollback job (this same function is now shared between deploy and rollback release actions, per the PR's stated goal of treating both as "release actions"). The adjacent cleanupQueuedDeploymentCancellation/cleanupExpiredDeploymentExecution helpers already differentiate deploy vs rollback note text via execution.actionKey; this branch was missed, so an operator recovering from a stuck rollback will see a misleading "Deploy execution ended..." message in the job history/UI.

🐛 Proposed fix
         if (lockExecution && activeJob?.status === "building") {
             writeDeploymentJob({
                 ...activeJob,
-                note: "Deploy execution ended before build completion",
+                note:
+                    lockExecution && activeJob.commit === undefined
+                        ? "Release execution ended before build completion"
+                        : "Release execution ended before build completion",
                 status: "failed",
                 updatedAt: dateToISOString(new Date()),
             });

Since activeJob alone doesn't carry the action type here, prefer reading readDeploymentLockExecution(activeJobId)?.actionKey (already fetched as lockExecution, though its type here is DeploymentLockExecutionRow which may need an actionKey field) to pick deploy- vs rollback-specific wording, mirroring the pattern used a few lines above in cleanupTerminatedDeploymentExecution.

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

In `@backend/src/services/pullRequests.ts` around lines 498 - 537, Update
ensureNoActiveDeployment to choose the recovery note from
lockExecution.actionKey instead of hardcoding “Deploy” wording. Add or expose
actionKey on DeploymentLockExecutionRow if needed, and use the existing
deploy-versus-rollback wording pattern from cleanupTerminatedDeploymentExecution
while preserving the current failure and lock-cleanup behavior.
🧹 Nitpick comments (3)
docs/setup/production-deploy.md (1)

110-177: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Host-local fallback bypasses the exclusive deployment lock.

The fallback script mutates release symlinks and restarts services directly via $DATABASE_PATH/releaseLifecycle.js, without checking or acquiring deployment_lock the way the UI-driven path does via acquireDeploymentLock. The doc only asks the operator to manually confirm nothing is running; a concurrent UI/worker-driven rollback or deploy could race with this script and corrupt the current/previous symlinks.

Consider adding an automated guard (e.g., query deployment_lock/job_executions for an active dashboard.deploy/dashboard.rollback row and abort if found) before proceeding, rather than relying solely on operator discipline.

💡 Suggested guard to add before the rollback commands
+ACTIVE_LOCK="$(
+  sqlite3 "$DATABASE_PATH" "SELECT job_id FROM deployment_lock WHERE id = 1;"
+)"
+if [[ -n "$ACTIVE_LOCK" ]]; then
+  echo "A deployment or rollback action is already in progress ($ACTIVE_LOCK); aborting." >&2
+  exit 1
+fi
+
 env MIRA_DASHBOARD_DB_PATH="$DATABASE_PATH" \
   MIRA_DASHBOARD_RELEASES_ROOT="$RELEASES_ROOT" \
   NODE_ENV=production \
   bun "$CURRENT_LIFECYCLE" rollback
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/setup/production-deploy.md` around lines 110 - 177, Update the
host-local fallback before the first rollback command to automatically inspect
the SQLite state for an active deployment or rollback, including deployment_lock
and running dashboard.deploy/dashboard.rollback job executions, and abort if any
are present. Reuse the existing DATABASE_PATH and lifecycle environment
configuration, and ensure the guard runs before any release symlink mutation or
service restart.
backend/src/services/pullRequests.ts (1)

1750-1841: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rollback's systemd unit is still named mira-dashboard-deploy-*.

scheduleReleaseRollback schedules its detached script under --unit=mira-dashboard-deploy-${job.id} (line 1833), identical to the deploy cutover's unit name pattern. Since job.id is always unique there's no functional collision, but this makes it harder to distinguish an in-flight rollback from a deploy when inspecting systemctl --user list-units or unit logs during an incident — exactly the scenario where quick triage matters most for this feature.

♻️ Proposed fix
     return runCommand(
         "systemd-run",
         [
             "--user",
             "--collect",
-            `--unit=mira-dashboard-deploy-${job.id}`,
+            `--unit=mira-dashboard-rollback-${job.id}`,
             "--description=Mira Dashboard atomic release rollback",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/services/pullRequests.ts` around lines 1750 - 1841, Update the
systemd unit name constructed in scheduleReleaseRollback to use a
rollback-specific pattern, such as a name containing “rollback” instead of
“deploy,” while preserving the existing job.id suffix and all other scheduling
behavior.
src/components/features/pullRequests/ProductionReleasesCard.tsx (1)

42-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Commit link hardcodes the GitHub org/repo instead of reusing a backend-supplied URL.

DeploymentJob.commitUrl (used in RecentDeploysCard in PullRequests.tsx) is already supplied by the backend for deployment commit links. Here the release-slot commit link is instead built client-side from a hardcoded rajohan/Mira-Dashboard path. Consider exposing a commitUrl on DashboardReleaseSummary (mirroring DeploymentJob) for consistency and to avoid duplicating/hardcoding the repo path on the frontend.

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

In `@src/components/features/pullRequests/ProductionReleasesCard.tsx` around lines
42 - 49, The release commit link in ProductionReleasesCard should use a
backend-supplied URL instead of constructing a hardcoded GitHub path. Add and
populate commitUrl on DashboardReleaseSummary, following the existing
DeploymentJob.commitUrl pattern, then bind the anchor’s href to
release.commitUrl while preserving the current link behavior and styling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/src/services/pullRequests.ts`:
- Around line 498-537: Update ensureNoActiveDeployment to choose the recovery
note from lockExecution.actionKey instead of hardcoding “Deploy” wording. Add or
expose actionKey on DeploymentLockExecutionRow if needed, and use the existing
deploy-versus-rollback wording pattern from cleanupTerminatedDeploymentExecution
while preserving the current failure and lock-cleanup behavior.

---

Nitpick comments:
In `@backend/src/services/pullRequests.ts`:
- Around line 1750-1841: Update the systemd unit name constructed in
scheduleReleaseRollback to use a rollback-specific pattern, such as a name
containing “rollback” instead of “deploy,” while preserving the existing job.id
suffix and all other scheduling behavior.

In `@docs/setup/production-deploy.md`:
- Around line 110-177: Update the host-local fallback before the first rollback
command to automatically inspect the SQLite state for an active deployment or
rollback, including deployment_lock and running
dashboard.deploy/dashboard.rollback job executions, and abort if any are
present. Reuse the existing DATABASE_PATH and lifecycle environment
configuration, and ensure the guard runs before any release symlink mutation or
service restart.

In `@src/components/features/pullRequests/ProductionReleasesCard.tsx`:
- Around line 42-49: The release commit link in ProductionReleasesCard should
use a backend-supplied URL instead of constructing a hardcoded GitHub path. Add
and populate commitUrl on DashboardReleaseSummary, following the existing
DeploymentJob.commitUrl pattern, then bind the anchor’s href to
release.commitUrl while preserving the current link behavior and styling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 06df07ae-ecb3-42fd-ada1-1bf650abae06

📥 Commits

Reviewing files that changed from the base of the PR and between 03677b4 and 42a96cf.

📒 Files selected for processing (14)
  • backend/src/routes/pullRequestRoutes.ts
  • backend/src/services/pullRequests.ts
  • backend/test/multiFactorAuth.test.ts
  • backend/test/serviceBehavior.test.ts
  • docs/setup/production-deploy.md
  • src/components/features/chat/ChatHeader.tsx
  • src/components/features/pullRequests/ProductionReleasesCard.tsx
  • src/components/layout/AppHeader.tsx
  • src/hooks/index.ts
  • src/hooks/usePullRequests.ts
  • src/pages/PullRequests.tsx
  • src/test/chatHeader.test.tsx
  • src/test/frontendBehavior.test.tsx
  • src/test/pageBehavior.test.tsx
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Analyze JavaScript and TypeScript
  • GitHub Check: backend-checks
  • GitHub Check: frontend-checks
🔇 Additional comments (27)
src/components/features/chat/ChatHeader.tsx (1)

87-95: LGTM!

Also applies to: 120-120

src/test/chatHeader.test.tsx (1)

65-68: LGTM!

src/components/layout/AppHeader.tsx (4)

2-8: LGTM!


62-80: LGTM!


107-179: LGTM!


227-227: LGTM!

Also applies to: 236-236

backend/test/multiFactorAuth.test.ts (1)

605-612: LGTM!

backend/test/serviceBehavior.test.ts (5)

1596-1717: LGTM!


1719-1847: LGTM!


1878-1878: LGTM!

Also applies to: 1904-1904, 2041-2041


2446-2447: LGTM!

Also applies to: 2475-2476, 2743-2744, 2837-2838


69-92: 🎯 Functional Correctness

No change needed.

The remaining --short HEAD handlers are in locally scoped inline fake git scripts whose callers supply their own full rev-parse HEAD responses as well.

docs/setup/production-deploy.md (1)

89-89: LGTM!

src/test/frontendBehavior.test.tsx (4)

150-156: LGTM!


902-909: LGTM!


3129-3156: LGTM!

Also applies to: 3306-3310


4623-4634: LGTM!

Also applies to: 4676-4680

src/test/pageBehavior.test.tsx (3)

1736-1763: LGTM!


1804-1816: LGTM!


2791-2792: LGTM!

Also applies to: 2820-2830

backend/src/services/pullRequests.ts (1)

20-20: LGTM!

Also applies to: 185-221, 366-388, 401-462, 465-495, 540-565, 602-640, 1391-1434, 1979-2062, 2064-2122, 2217-2277, 2539-2590

backend/src/routes/pullRequestRoutes.ts (2)

4-8: LGTM!


122-142: 🔒 Security & Privacy

No change needed.

/api/pull-requests/releases/rollback is covered by the centralized request-policy MFA checks via requiresRecentMfa(), which gates all /api/pull-requests/ mutations.

src/hooks/usePullRequests.ts (1)

50-71: LGTM!

Also applies to: 80-80, 106-110, 132-132, 160-167, 218-227, 258-267, 283-285, 364-367, 371-389

src/hooks/index.ts (1)

112-113: LGTM!

Also applies to: 125-131

src/components/features/pullRequests/ProductionReleasesCard.tsx (1)

1-41: LGTM!

Also applies to: 50-193

src/pages/PullRequests.tsx (1)

15-15: LGTM!

Also applies to: 24-24, 33-39, 51-56, 409-411, 422-422, 431-434, 545-548, 610-617, 626-627, 686-694, 883-893, 1036-1039

@mira-2026 mira-2026 changed the title feat: add managed release status and rollback controls feat: add atomic release operations and trusted PR dev Jul 26, 2026
Comment thread backend/src/development/developmentOpenClaw.ts Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
docs/setup/production-deploy.md (1)

147-203: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make fallback target selection part of the exclusive transition.

Line 163 captures TARGET_SHA before the rollback command obtains its lock. A deploy/rollback can transition releases after Line 189, causing the unparameterized command at Line 193 to roll back a different pair; the recovery rollback can then undo that concurrent action. Add a guarded lifecycle operation that validates the expected current and target commits atomically under the release lock, and use it here.

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

In `@docs/setup/production-deploy.md` around lines 147 - 203, Make TARGET_SHA
selection and rollback one exclusive lifecycle transition instead of reading it
before the lock. Update the lifecycle operation invoked by this deployment flow
to accept and validate the expected current and target commits under the release
lock, then perform the rollback only when they still match. Replace the
unparameterized rollback calls in the main and recovery paths with this guarded
operation, preserving recovery of CURRENT_SHA after readiness failure.
🧹 Nitpick comments (11)
backend/src/services/pullRequestPreviewHost.ts (1)

423-442: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Corrupt preview state permanently breaks the status endpoint.

readPreviewRecord throws on any unparsable/invalid active-preview.json, and getPullRequestPreviewStatus propagates it, so GET /api/pull-requests/preview (and every start/stop attempt) fails until someone manually deletes the file on the host. Consider treating an unreadable record as "no active preview" (log + quarantine the file) so the slot remains recoverable from the UI.

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

In `@backend/src/services/pullRequestPreviewHost.ts` around lines 423 - 442,
Update readPreviewRecord and its getPullRequestPreviewStatus callers to recover
from invalid or unreadable preview state by logging the error, quarantining the
state file, and returning undefined so the endpoint and start/stop flows treat
it as no active preview. Preserve existing handling for missing files and valid
records.
src/components/layout/AppHeader.tsx (1)

154-167: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant label in the Worker row.

The row already renders Worker on the left, so the value column reads "Worker Worker offline ○". WebSocket/Backend rows use a bare Online/Offline; consider a status-only string here for consistency.

♻️ Suggested change
-                                        {workerStatus.label} {workerStatus.symbol}
+                                        {workerStatus.text} {workerStatus.symbol}

with workerStatus exposing text: "Online" | "Offline" | "Unavailable" alongside the existing label used for the trigger.

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

In `@src/components/layout/AppHeader.tsx` around lines 154 - 167, Update the
Worker status value in the AppHeader status row to render only the status text
and symbol, avoiding the redundant “Worker” label; add or reuse a workerStatus
text value such as Online, Offline, or Unavailable while preserving
workerStatus.label for the trigger and the existing state-based styling.
backend/src/services/pullRequests.ts (2)

1902-1913: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse FULL_COMMIT_SHA_PATTERN instead of re-declaring the literal.

FULL_COMMIT_SHA_PATTERN (Line 81) already encodes this rule, but the rollback and cutover schedulers inline /^[\da-f]{40}$/u in five places. Swapping them keeps the SHA contract in one spot.

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

In `@backend/src/services/pullRequests.ts` around lines 1902 - 1913, Replace every
inline `/^[\da-f]{40}$/u` check in the rollback and cutover scheduler logic,
including the validations around `job.commit` and `originalCommit`, with the
existing `FULL_COMMIT_SHA_PATTERN` symbol. Preserve the current validation
conditions and error behavior while reusing that shared pattern in all five
occurrences.

863-897: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

No negative caching on the public PR fallback.

Only successful responses populate publicPullRequestCache, so a failing or rate-limited GitHub API is re-hit on every dashboard poll. Given the unauthenticated 60 req/hour budget this can wedge the dev fallback for a long window; caching a short-lived failure (or the last good value) would fail softer.

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

In `@backend/src/services/pullRequests.ts` around lines 863 - 897, Update
listPublicDashboardPullRequests and publicPullRequestCache to retain a
short-lived fallback after GitHub fetch, parsing, or validation failures instead
of retrying on every poll. Cache the last successful pull requests or a
short-lived failure/empty result with an appropriate expiration, while
preserving normal cache returns and successful response handling.
backend/test/releaseManager.test.ts (1)

840-860: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test passes even if the transition lock wait regresses.

Nothing asserts the activation was still pending while the shared lock was held, so a build where transitionLockWaitMs is ignored (or the lock isn't taken) would still satisfy this test. Adding a pre-release check on the pending promise would make it a real regression guard. Also, the descriptor leaks if the assertion throws — a try/finally around closeSync would keep the temp root clean.

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

In `@backend/test/releaseManager.test.ts` around lines 840 - 860, Add an explicit
pre-release assertion in the test around runReleaseLifecycleCommand to verify
activation remains pending while the shared lock descriptor from
holdTransitionLock is open, then release the descriptor in a try/finally so
cleanup occurs even if the assertion or final activation expectation fails.
backend/src/releaseLifecycle.ts (1)

73-79: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

status still acquires its shared lock with a zero wait.

activate/rollback now wait up to 30s, but readDashboardReleaseState takes no options, so status fails immediately when a transition holds the exclusive lock. If status is polled by the dashboard during a rollback, that surfaces as a transient error rather than a short wait.

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

In `@backend/src/releaseLifecycle.ts` around lines 73 - 79, Update the status
branch in the release lifecycle handler to read state with the same 30-second
lock wait used by activate and rollback. Extend or reuse
readDashboardReleaseState’s options so status passes the wait configuration
while preserving its no-commit-SHA validation and existing state handling.
backend/src/services/pullRequestPreviews.ts (1)

126-141: 🩺 Stability & Availability | 🔵 Trivial | ⚖️ Poor tradeoff

Long-lived request blocking on the preview start job.

prepareAndStartPullRequestPreview awaits the queued execution for up to 15 minutes on the HTTP request thread. Any intermediary (Tailscale Serve, browser, fetch) will typically time out well before then, leaving the caller without a result even though the job continues. Since the frontend already polls GET /api/pull-requests/preview, consider returning the queued/starting status immediately and letting the poll drive the UI.

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

In `@backend/src/services/pullRequestPreviews.ts` around lines 126 - 141, Update
prepareAndStartPullRequestPreview to return the queued/starting
PullRequestPreviewStatus immediately after enqueueJobExecution, rather than
awaiting waitForJobExecution and previewFromExecution. Preserve the existing job
enqueue configuration and rely on the frontend’s GET /api/pull-requests/preview
polling for completion.
scripts/developmentTailscale.ts (2)

166-172: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Cleanup failure in finally masks the stack's exit code/error.

If disableDevelopmentServe throws (tailscale down, sudo denied), the original runDevelopmentStack result or error is replaced by the teardown error and the serve route stays configured with no clear signal. Log and swallow instead.

♻️ Proposed fix
     } finally {
         if (route.didCreate) {
-            await disableDevelopmentServe(port);
+            try {
+                await disableDevelopmentServe(port);
+            } catch (error) {
+                console.error(
+                    `Failed to remove Tailscale Serve route on port ${port}: ${
+                        error instanceof Error ? error.message : String(error)
+                    }`
+                );
+            }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/developmentTailscale.ts` around lines 166 - 172, Update the finally
cleanup around runDevelopmentStack so failures from disableDevelopmentServe do
not replace its exit code or error. When route.didCreate is true, catch teardown
errors, log them with the existing logging mechanism, and swallow them while
preserving the original stack result or exception.

28-45: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Add a timeout to commandOutput spawns.

commandOutput is used for tailscale status/serve/status and sudo tailscale serve ..., so a wedged process can block the dev entrypoint indefinitely. Pass a timeout option to Bun.spawn, e.g. 15_000 ms, and add a timeout-failure branch to process_.exited.

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

In `@scripts/developmentTailscale.ts` around lines 28 - 45, Update commandOutput
to pass a 15_000 ms timeout option to Bun.spawn, and handle the resulting
timeout failure in process_.exited alongside nonzero exit codes. Preserve the
existing stderr/stdout capture and error reporting for ordinary command
failures.
scripts/developmentFrontend.ts (1)

10-14: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Derive the default cookie namespace from port instead of hardcoding 5173.

developmentBackendEnvironment builds the backend namespace as mira_dashboard_dev_${frontendPort}. If someone runs this proxy with PORT=4173 but without MIRA_DASHBOARD_DEV_COOKIE_NAMESPACE, developmentCookieHeader silently filters out every real cookie and the session never reaches the backend.

♻️ Proposed fix
 const cookieNamespace =
-    process.env.MIRA_DASHBOARD_DEV_COOKIE_NAMESPACE || "mira_dashboard_dev_5173";
+    process.env.MIRA_DASHBOARD_DEV_COOKIE_NAMESPACE || `mira_dashboard_dev_${port}`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/developmentFrontend.ts` around lines 10 - 14, Update the default
value in the cookieNamespace initialization to derive from the parsed port
variable, matching the backend namespace format `mira_dashboard_dev_${port}`
while preserving the explicit MIRA_DASHBOARD_DEV_COOKIE_NAMESPACE override.
backend/src/requestPolicy.ts (1)

499-519: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Third copy of the audit-then-403-deny pattern.

This block duplicates the same audit-write + 503-fallback + 403-JSON shape already used at Lines 463-480 (automation scope denial) and Lines 527-551 (MFA denial). Consider extracting a small helper (e.g. denyWithAudit(actor, request, requestIdentifier, routePath, automationScope, persistAuditEvent, body)) to avoid the three call sites drifting out of sync.

♻️ Example extraction
+function denyWithAudit(
+    actor: AuditActor,
+    request: Request,
+    requestIdentifier: string,
+    routePath: string,
+    automationScope: AutomationScope | undefined,
+    persistAuditEvent: typeof writeAuditEvent,
+    body: Record<string, unknown>
+): Response | undefined {
+    const didRecordDenial = didWriteRequestAudit(
+        actor,
+        "denied",
+        request,
+        requestIdentifier,
+        routePath,
+        403,
+        automationScope,
+        persistAuditEvent
+    );
+    if (!didRecordDenial) {
+        return json({ error: "Audit trail unavailable" }, { status: 503 });
+    }
+    return json(body, { status: 403 });
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/requestPolicy.ts` around lines 499 - 519, Extract the repeated
audit-then-deny behavior from the current block and the existing
automation-scope and MFA denial paths into a shared helper near the relevant
request-policy logic, such as denyWithAudit. Have it perform
didWriteRequestAudit, return the 503 audit-unavailable response on failure, and
otherwise return the supplied 403 JSON body; update all three call sites to use
it while preserving their existing response messages and parameters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/development/developmentOpenClaw.ts`:
- Around line 36-37: Update SENSITIVE_AGENT_CONFIG_KEY so the sensitive-key
keyword alternatives match both singular and plural forms, including keys such
as tokens, apiKeys, and passwords, while preserving separator and end-of-key
boundaries and existing matches like secrets.

In `@backend/src/development/developmentStack.ts`:
- Around line 352-357: Move the `try` in `scrubDevelopmentDatabase` to wrap
`Database` creation and all PRAGMA/`BEGIN IMMEDIATE` setup, with the existing
`finally` closing the handle whenever it is successfully created; ensure setup
errors cannot bypass `database.close()` before the caller removes the staging
file.

In `@backend/src/routes/pullRequestRoutes.ts`:
- Around line 165-173: Update the POST handler’s rollback body validation before
accessing body.targetCommit so a null or non-object JSON body returns the same
400 “Rollback target commit is required” response. Preserve the existing string
validation and valid-body behavior.

---

Outside diff comments:
In `@docs/setup/production-deploy.md`:
- Around line 147-203: Make TARGET_SHA selection and rollback one exclusive
lifecycle transition instead of reading it before the lock. Update the lifecycle
operation invoked by this deployment flow to accept and validate the expected
current and target commits under the release lock, then perform the rollback
only when they still match. Replace the unparameterized rollback calls in the
main and recovery paths with this guarded operation, preserving recovery of
CURRENT_SHA after readiness failure.

---

Nitpick comments:
In `@backend/src/releaseLifecycle.ts`:
- Around line 73-79: Update the status branch in the release lifecycle handler
to read state with the same 30-second lock wait used by activate and rollback.
Extend or reuse readDashboardReleaseState’s options so status passes the wait
configuration while preserving its no-commit-SHA validation and existing state
handling.

In `@backend/src/requestPolicy.ts`:
- Around line 499-519: Extract the repeated audit-then-deny behavior from the
current block and the existing automation-scope and MFA denial paths into a
shared helper near the relevant request-policy logic, such as denyWithAudit.
Have it perform didWriteRequestAudit, return the 503 audit-unavailable response
on failure, and otherwise return the supplied 403 JSON body; update all three
call sites to use it while preserving their existing response messages and
parameters.

In `@backend/src/services/pullRequestPreviewHost.ts`:
- Around line 423-442: Update readPreviewRecord and its
getPullRequestPreviewStatus callers to recover from invalid or unreadable
preview state by logging the error, quarantining the state file, and returning
undefined so the endpoint and start/stop flows treat it as no active preview.
Preserve existing handling for missing files and valid records.

In `@backend/src/services/pullRequestPreviews.ts`:
- Around line 126-141: Update prepareAndStartPullRequestPreview to return the
queued/starting PullRequestPreviewStatus immediately after enqueueJobExecution,
rather than awaiting waitForJobExecution and previewFromExecution. Preserve the
existing job enqueue configuration and rely on the frontend’s GET
/api/pull-requests/preview polling for completion.

In `@backend/src/services/pullRequests.ts`:
- Around line 1902-1913: Replace every inline `/^[\da-f]{40}$/u` check in the
rollback and cutover scheduler logic, including the validations around
`job.commit` and `originalCommit`, with the existing `FULL_COMMIT_SHA_PATTERN`
symbol. Preserve the current validation conditions and error behavior while
reusing that shared pattern in all five occurrences.
- Around line 863-897: Update listPublicDashboardPullRequests and
publicPullRequestCache to retain a short-lived fallback after GitHub fetch,
parsing, or validation failures instead of retrying on every poll. Cache the
last successful pull requests or a short-lived failure/empty result with an
appropriate expiration, while preserving normal cache returns and successful
response handling.

In `@backend/test/releaseManager.test.ts`:
- Around line 840-860: Add an explicit pre-release assertion in the test around
runReleaseLifecycleCommand to verify activation remains pending while the shared
lock descriptor from holdTransitionLock is open, then release the descriptor in
a try/finally so cleanup occurs even if the assertion or final activation
expectation fails.

In `@scripts/developmentFrontend.ts`:
- Around line 10-14: Update the default value in the cookieNamespace
initialization to derive from the parsed port variable, matching the backend
namespace format `mira_dashboard_dev_${port}` while preserving the explicit
MIRA_DASHBOARD_DEV_COOKIE_NAMESPACE override.

In `@scripts/developmentTailscale.ts`:
- Around line 166-172: Update the finally cleanup around runDevelopmentStack so
failures from disableDevelopmentServe do not replace its exit code or error.
When route.didCreate is true, catch teardown errors, log them with the existing
logging mechanism, and swallow them while preserving the original stack result
or exception.
- Around line 28-45: Update commandOutput to pass a 15_000 ms timeout option to
Bun.spawn, and handle the resulting timeout failure in process_.exited alongside
nonzero exit codes. Preserve the existing stderr/stdout capture and error
reporting for ordinary command failures.

In `@src/components/layout/AppHeader.tsx`:
- Around line 154-167: Update the Worker status value in the AppHeader status
row to render only the status text and symbol, avoiding the redundant “Worker”
label; add or reuse a workerStatus text value such as Online, Offline, or
Unavailable while preserving workerStatus.label for the trigger and the existing
state-based styling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 72a30c69-a39f-4b7f-9fcc-f28876926f66

📥 Commits

Reviewing files that changed from the base of the PR and between 42a96cf and ec15c01.

⛔ Files ignored due to path filters (2)
  • public/vite.svg is excluded by !**/*.svg and included by **/*
  • src/assets/react.svg is excluded by !**/*.svg and included by **/*
📒 Files selected for processing (51)
  • .github/CONTRIBUTING.md
  • .github/pull_request_template.md
  • .github/workflows/dashboard-checks.yml
  • README.md
  • backend/package.json
  • backend/src/development/developmentOpenClaw.ts
  • backend/src/development/developmentStack.ts
  • backend/src/http.ts
  • backend/src/lib/values.ts
  • backend/src/releaseLifecycle.ts
  • backend/src/releaseManager.ts
  • backend/src/requestPolicy.ts
  • backend/src/routes/pullRequestRoutes.ts
  • backend/src/server.ts
  • backend/src/services/jobWorker.ts
  • backend/src/services/pullRequestPreviewHost.ts
  • backend/src/services/pullRequestPreviews.ts
  • backend/src/services/pullRequests.ts
  • backend/test/developmentStack.test.ts
  • backend/test/pullRequestPreview.test.ts
  • backend/test/releaseManager.test.ts
  • backend/test/serverStartupPolicy.test.ts
  • backend/test/serviceBehavior.test.ts
  • backend/test/utilityBehavior.test.ts
  • docs/api/endpoints.md
  • docs/architecture/frontend-feature-map.md
  • docs/architecture/overview.md
  • docs/development/local-dev.md
  • docs/development/testing-and-prs.md
  • docs/operations/scheduler-cache-backups.md
  • docs/operations/troubleshooting.md
  • docs/setup/production-deploy.md
  • docs/setup/secrets-and-env.md
  • eslint.config.js
  • package.json
  • scripts/developmentFrontend.ts
  • scripts/developmentStack.ts
  • scripts/developmentTailscale.ts
  • src/components/features/pullRequests/ProductionReleasesCard.tsx
  • src/components/features/pullRequests/PullRequestPreviewCard.tsx
  • src/components/layout/AppHeader.tsx
  • src/components/ui/Dropdown.tsx
  • src/hooks/index.ts
  • src/hooks/usePullRequests.ts
  • src/lib/developmentProxyHeaders.ts
  • src/pages/PullRequests.tsx
  • src/test/developmentProxyHeaders.test.ts
  • src/test/developmentTailscale.test.ts
  • src/test/frontendBehavior.test.tsx
  • src/test/pageBehavior.test.tsx
  • src/test/pullRequestPreviewCard.test.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/hooks/index.ts
  • src/test/pageBehavior.test.tsx
  • src/test/frontendBehavior.test.tsx
  • src/components/features/pullRequests/ProductionReleasesCard.tsx
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: frontend-checks
  • GitHub Check: backend-checks
🧰 Additional context used
🪛 ast-grep (0.44.1)
backend/test/releaseManager.test.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

backend/test/developmentStack.test.ts

[error] 306-306: Avoid SQL injection
Context: snapshot.query(SELECT COUNT(*) AS count FROM ${tableName})
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').

(sql-injection-typescript)

backend/src/releaseManager.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 GitHub Check: CodeQL
backend/src/development/developmentOpenClaw.ts

[failure] 132-132: Potential file system race condition
The file may have changed since it was checked.

🪛 LanguageTool
docs/development/local-dev.md

[grammar] ~87-~87: Ensure spelling is correct
Context: ...space paths at the snapshot. All state roots are owner-only and untracked. Refresh t...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 OpenGrep (1.25.0)
backend/test/developmentStack.test.ts

[ERROR] 307-307: SQL query built via string concatenation or template literal passed to query()/execute(). Use parameterized queries instead.

(coderabbit.sql-injection.raw-query-concat-js)

🔇 Additional comments (38)
backend/src/services/pullRequestPreviewHost.ts (2)

222-321: LGTM!


1189-1254: LGTM!

src/components/layout/AppHeader.tsx (1)

168-189: LGTM!

backend/src/services/pullRequestPreviews.ts (1)

41-56: LGTM!

Also applies to: 70-118, 144-189

backend/src/services/pullRequests.ts (1)

72-85: LGTM!

Also applies to: 177-189, 311-314, 368-411, 521-563, 628-642, 706-728, 792-861, 1133-1139, 1955-1986, 2210-2266, 2362-2437

backend/src/routes/pullRequestRoutes.ts (2)

3-7: LGTM!

Also applies to: 106-133, 192-200


164-182: 🔒 Security & Privacy

Recent MFA coverage is already in place.

/api/pull-requests/releases/rollback is routed through routes.ts and covered by the request policy prefix /api/pull-requests/, so no local handler-level step-up check is required.

backend/src/releaseLifecycle.ts (1)

14-14: LGTM!

Also applies to: 48-70

backend/src/releaseManager.ts (1)

76-76: LGTM!

Also applies to: 1217-1296, 1298-1367

backend/test/serverStartupPolicy.test.ts (1)

873-873: LGTM!

Also applies to: 892-922, 951-1020, 1046-1100

backend/test/serviceBehavior.test.ts (1)

69-76: LGTM!

Also applies to: 1658-1660, 1701-1758, 1772-1816, 1993-2064

backend/test/releaseManager.test.ts (1)

93-113: LGTM!

src/test/developmentTailscale.test.ts (1)

1-60: LGTM!

src/test/developmentProxyHeaders.test.ts (1)

3-29: LGTM!

src/components/ui/Dropdown.tsx (1)

25-25: LGTM!

Also applies to: 38-38, 61-62

src/components/features/pullRequests/PullRequestPreviewCard.tsx (1)

8-121: LGTM!

src/pages/PullRequests.tsx (1)

52-90: LGTM!

Also applies to: 414-470, 645-854, 940-1051, 1187-1210

src/test/pullRequestPreviewCard.test.tsx (1)

7-82: LGTM!

src/hooks/usePullRequests.ts (1)

51-104: LGTM!

Also applies to: 134-199, 252-329, 435-483

docs/operations/scheduler-cache-backups.md (1)

6-9: LGTM!

docs/operations/troubleshooting.md (1)

116-130: LGTM!

docs/setup/secrets-and-env.md (1)

55-66: LGTM!

Also applies to: 149-175

eslint.config.js (1)

36-36: LGTM!

package.json (1)

40-40: 📐 Maintainability & Code Quality

No change needed for the frontend test script.

bunfig.toml sets the root test discovery to src, so test:frontend does not include backend/test/**/*.test.* files.

			> Likely an incorrect or invalid review comment.
backend/src/development/developmentOpenClaw.ts (1)

47-117: LGTM!

Also applies to: 141-228

backend/src/development/developmentStack.ts (1)

88-199: LGTM!

Also applies to: 202-303, 442-549, 551-680, 683-728, 731-775, 779-853

scripts/developmentFrontend.ts (1)

42-50: LGTM!

Also applies to: 76-79

scripts/developmentStack.ts (1)

1-60: LGTM!

scripts/developmentTailscale.ts (1)

57-97: LGTM!

Also applies to: 99-165

backend/test/developmentStack.test.ts (1)

30-150: LGTM!

Also applies to: 152-438, 440-513

backend/package.json (1)

9-20: 📐 Maintainability & Code Quality

No change needed. The package.json scripts and CI/manual command references use scoping routes from package.json (build, deploy:prepare, test, test:backend, format, lint:backend) rather than direct unscoped backend script names.

backend/src/http.ts (1)

5-26: LGTM!

backend/src/lib/values.ts (1)

23-45: LGTM!

src/lib/developmentProxyHeaders.ts (1)

3-16: LGTM!

backend/test/utilityBehavior.test.ts (1)

13-13: LGTM!

Also applies to: 33-59, 91-124, 464-468, 478-568, 671-720, 859-880

backend/src/server.ts (1)

18-22: LGTM!

Also applies to: 79-101, 124-127, 162-169, 195-195

backend/src/requestPolicy.ts (1)

106-125: LGTM!

Also applies to: 161-192

backend/src/services/jobWorker.ts (1)

12-12: LGTM!

Also applies to: 31-39, 55-74

Comment thread backend/src/development/developmentOpenClaw.ts Outdated
Comment thread backend/src/development/developmentStack.ts Outdated
Comment thread backend/src/routes/pullRequestRoutes.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f1327df74

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread backend/src/development/developmentStack.ts
Comment thread backend/src/services/pullRequestPreviewHost.ts Outdated
Comment thread backend/src/releaseLifecycle.ts
Comment thread src/pages/PullRequests.tsx Outdated
Comment thread backend/src/services/pullRequestPreviewHost.ts Outdated
Comment thread src/pages/PullRequests.tsx Outdated
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
backend/src/services/pullRequestPreviewHost.ts (1)

437-458: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Quarantine triggers on transient read failures, not just corruption.

The catch wraps readFileSync too, so an EIO/EACCES blip renames away a valid state file and the preview is reported stopped while the unit is still running. Also, the size guard runs after the whole file is read. Consider statSync for the size check and quarantining only on parse/validation failures.

♻️ Suggested shape
-    try {
-        const content = readFileSync(config.stateFile, "utf8");
-        if (Buffer.byteLength(content) > 256 * 1024) {
-            throw new Error("Dashboard preview state is too large");
-        }
-        return previewRecordFromJson(JSON.parse(content) as unknown);
-    } catch (error) {
+    if (statSync(config.stateFile).size > 256 * 1024) {
+        throw new Error("Dashboard preview state is too large");
+    }
+    const content = readFileSync(config.stateFile, "utf8");
+    try {
+        return previewRecordFromJson(JSON.parse(content) as unknown);
+    } catch (error) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/services/pullRequestPreviewHost.ts` around lines 437 - 458,
Update the state-loading flow around readFileSync and previewRecordFromJson so
transient read failures such as EIO or EACCES return without quarantining the
state file. Use statSync to enforce the 256 KiB limit before reading, and
restrict the quarantine catch to JSON parsing or previewRecordFromJson
validation failures, preserving the existing quarantine behavior for genuinely
invalid state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/releaseManager.ts`:
- Around line 76-77: Validate transitionLockWaitMs before calculating the
contention deadline: reject values that are non-finite or negative, while
preserving valid finite non-negative delays. Apply this validation in the
release-manager transition lock flow where the option is consumed, before the
retry loop or deadline calculation.

In `@backend/src/services/pullRequestPreviewHost.ts`:
- Around line 744-761: Mark the Tailscale Serve route as owned immediately after
the enable command succeeds, before verification in enableTailscaleServe and
before any cleanup can fail. Propagate this attempted-ownership state to the
caller so the outer failure path invokes disableOwnedTailscaleServe with
ownership enabled and persists ownsTailscaleServe: true when the route may still
exist.

---

Nitpick comments:
In `@backend/src/services/pullRequestPreviewHost.ts`:
- Around line 437-458: Update the state-loading flow around readFileSync and
previewRecordFromJson so transient read failures such as EIO or EACCES return
without quarantining the state file. Use statSync to enforce the 256 KiB limit
before reading, and restrict the quarantine catch to JSON parsing or
previewRecordFromJson validation failures, preserving the existing quarantine
behavior for genuinely invalid state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 64c59a36-c7be-4b91-9cb6-22f8013bba0d

📥 Commits

Reviewing files that changed from the base of the PR and between ec15c01 and 42bbbed.

⛔ Files ignored due to path filters (2)
  • backend/bun.lock is excluded by !**/*.lock and included by **/*
  • bun.lock is excluded by !**/*.lock and included by **/*
📒 Files selected for processing (37)
  • .github/dependabot.yml
  • .gitignore
  • .prettierignore
  • README.md
  • backend/config/log-rotation.json
  • backend/eslint.config.js
  • backend/package.json
  • backend/src/development/developmentOpenClaw.ts
  • backend/src/development/developmentStack.ts
  • backend/src/releaseLifecycle.ts
  • backend/src/releaseManager.ts
  • backend/src/requestPolicy.ts
  • backend/src/routes/pullRequestRoutes.ts
  • backend/src/services/pullRequestPreviewHost.ts
  • backend/src/services/pullRequestPreviewPolicy.ts
  • backend/src/services/pullRequestPreviews.ts
  • backend/src/services/pullRequests.ts
  • backend/test/developmentStack.test.ts
  • backend/test/pullRequestPreview.test.ts
  • backend/test/releaseManager.test.ts
  • backend/test/serviceBehavior.test.ts
  • backend/test/utilityBehavior.test.ts
  • backend/tsconfig.json
  • docs/api/endpoints.md
  • docs/development/local-dev.md
  • docs/setup/production-deploy.md
  • docs/setup/secrets-and-env.md
  • eslint.config.js
  • package.json
  • scripts/developmentFrontend.ts
  • scripts/developmentTailscale.ts
  • src/components/features/pullRequests/PullRequestPreviewCard.tsx
  • src/components/layout/AppHeader.tsx
  • src/hooks/usePullRequests.ts
  • src/pages/PullRequests.tsx
  • src/test/frontendBehavior.test.tsx
  • src/test/pullRequestPreviewCard.test.tsx
🚧 Files skipped from review as they are similar to previous changes (24)
  • backend/package.json
  • README.md
  • eslint.config.js
  • src/test/pullRequestPreviewCard.test.tsx
  • docs/api/endpoints.md
  • scripts/developmentFrontend.ts
  • docs/setup/secrets-and-env.md
  • src/components/layout/AppHeader.tsx
  • package.json
  • backend/src/routes/pullRequestRoutes.ts
  • backend/test/utilityBehavior.test.ts
  • docs/development/local-dev.md
  • backend/src/development/developmentOpenClaw.ts
  • backend/src/requestPolicy.ts
  • scripts/developmentTailscale.ts
  • docs/setup/production-deploy.md
  • src/pages/PullRequests.tsx
  • src/test/frontendBehavior.test.tsx
  • backend/src/development/developmentStack.ts
  • src/hooks/usePullRequests.ts
  • backend/test/pullRequestPreview.test.ts
  • backend/test/serviceBehavior.test.ts
  • backend/src/services/pullRequestPreviews.ts
  • backend/src/services/pullRequests.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: backend-checks
  • GitHub Check: frontend-checks
🧰 Additional context used
🪛 ast-grep (0.44.1)
backend/test/releaseManager.test.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

backend/src/releaseManager.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🔇 Additional comments (17)
backend/test/releaseManager.test.ts (1)

429-441: LGTM!

Also applies to: 476-485, 869-889, 891-919

backend/test/developmentStack.test.ts (1)

17-17: LGTM!

Also applies to: 254-259, 278-278, 298-302, 424-497

backend/config/log-rotation.json (1)

2-56: LGTM!

backend/eslint.config.js (1)

22-28: LGTM!

.github/dependabot.yml (1)

4-92: LGTM!

.gitignore (1)

13-13: LGTM!

.prettierignore (1)

1-8: LGTM!

backend/src/releaseManager.ts (4)

79-86: LGTM!


1207-1223: LGTM!


1232-1311: LGTM!


1313-1401: LGTM!

backend/tsconfig.json (1)

11-12: LGTM!

backend/src/services/pullRequestPreviewHost.ts (2)

130-134: LGTM!

Also applies to: 268-269, 682-713, 1106-1149, 1173-1183, 1194-1230, 1266-1297


316-319: 📐 Maintainability & Code Quality

No change needed. The preview stack forwards sourceWebAuthnRpId as MIRA_DASHBOARD_DEV_SOURCE_WEBAUTHN_RP_ID, and the development stack prefers that env var with MIRA_DASHBOARD_WEBAUTHN_RP_ID as the fallback.

backend/src/services/pullRequestPreviewPolicy.ts (1)

1-27: LGTM!

src/components/features/pullRequests/PullRequestPreviewCard.tsx (1)

1-6: LGTM!

Also applies to: 48-57, 79-89

backend/src/releaseLifecycle.ts (1)

38-39: LGTM!

Also applies to: 53-57, 70-107

Comment thread backend/src/releaseManager.ts
Comment thread backend/src/services/pullRequestPreviewHost.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 42bbbed0a5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread backend/src/services/pullRequests.ts Outdated
Comment thread backend/src/services/pullRequestPreviewHost.ts Outdated
Comment thread backend/src/services/pullRequests.ts Outdated
Comment thread backend/src/services/jobWorker.ts
Comment thread backend/src/services/pullRequestPreviewHost.ts
Comment thread backend/src/services/pullRequestPreviewHost.ts Fixed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98eec534ab

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread backend/src/services/pullRequestPreviewHost.ts Outdated
Comment thread src/pages/PullRequests.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

' if [ "$current_commit" = "$candidate_commit" ]; then',
' if [ -e "$releases_root/previous" ] || [ -L "$releases_root/previous" ]; then',
' trusted_release=$(/usr/bin/readlink --canonicalize-existing "$releases_root/previous") || return 1',
" else",
' trusted_release="$candidate_release"',

P1 Badge Use the guarded lifecycle during orphan rollback recovery

Fresh evidence after fixing the normal cutover path is this orphan-recovery branch: on the first deployment from a pre-upgrade release, if the candidate is already current, recovery deliberately selects the previous release's old lifecycle binary and later invokes it as rollback <candidate> <previous>. That binary only accepts argument-free rollback, so an interrupted cutover whose candidate fails readiness cannot restore the previous release; select a lifecycle implementation known to support guarded rollback arguments.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread backend/src/services/pullRequestPreviews.ts Outdated
Comment thread backend/src/services/pullRequestPreviewHost.ts
Comment thread backend/src/services/pullRequestPreviewHost.ts Outdated
Comment thread backend/src/services/pullRequestPreviewHost.ts Outdated
Comment thread backend/src/development/developmentStack.ts Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (2)
backend/src/routes/metricsRoutes.ts (1)

106-107: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reset byte counters before the /proc/net/dev fallback to avoid double-counting.

If the primary /sys/class/net loop partially succeeds (adds bytes for some interfaces) before throwing on a later interface, downloadBytes/uploadBytes retain those partial sums when the code falls through to the /proc/net/dev path, which then adds its own totals on top — inflating the reported network throughput for that sample.

🐛 Proposed fix
             } catch (sysError) {
                 try {
+                    downloadBytes = 0;
+                    uploadBytes = 0;
                     const networkDeviceText = await Bun.file("/proc/net/dev").text();

Also applies to: 133-168

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

In `@backend/src/routes/metricsRoutes.ts` around lines 106 - 107, Reset
downloadBytes and uploadBytes immediately before entering the /proc/net/dev
fallback after the primary /sys/class/net collection fails, so fallback totals
replace any partial sums rather than accumulating on top of them. Update the
fallback flow associated with these counters while preserving the existing
successful primary-path behavior.
backend/src/services/pullRequestPreviewHost.ts (1)

1599-1627: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

A "starting" record is stamped before the slowest startup phase, so status reads can tear down an in-flight start. timestamp is captured at Line 1571 but the record is only written at Line 1627, after ensurePreviewWorktree and installPreviewDependencies. A cold bun install easily exceeds the 75 s PREVIEW_START_RECONCILIATION_GRACE_MS, so the record is already stale when persisted, and the next getPullRequestPreviewStatus call runs cleanupPreviewResources — stopping both units and deleting the gateway credentials — while the start is still running.

  • backend/src/services/pullRequestPreviewHost.ts#L1599-L1627: set startingRecord.updatedAt = new Date().toISOString() immediately before writePreviewRecord (or construct the record at that point) so the grace window starts when the record is written.
  • backend/src/services/pullRequestPreviewHost.ts#L1328-L1356: refresh the record's updatedAt between long startup phases (proxy readiness, sandbox start, frontend readiness) so isRecentStartup tracks real progress rather than a single fixed instant.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/services/pullRequestPreviewHost.ts` around lines 1599 - 1627, The
starting record uses a stale timestamp during long preview startup. In
backend/src/services/pullRequestPreviewHost.ts:1599-1627, refresh
startingRecord.updatedAt immediately before writePreviewRecord; in
backend/src/services/pullRequestPreviewHost.ts:1328-1356, refresh the record
timestamp between proxy readiness, sandbox startup, and frontend readiness
phases so isRecentStartup reflects ongoing progress.
🧹 Nitpick comments (7)
backend/test/databaseOverview.test.ts (1)

476-478: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use an independent oracle for storageBytes.

4096 + overview.sqlite.storageBytes reuses the value being validated, so the test can pass even if the SQLite metric is stale or incorrect. Control the SQLite fixture/mock and assert its expected byte count before checking the aggregate.

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

In `@backend/test/databaseOverview.test.ts` around lines 476 - 478, Update the
test around the aggregate size assertion to configure the SQLite fixture or mock
with a known storage byte count, then assert that expected value through
overview.sqlite.storageBytes before validating totalManagedDatabaseSizeBytes.
Replace the self-referential 4096 + overview.sqlite.storageBytes expression with
an independent expected total derived from the controlled fixture value.
backend/test/pullRequestPreviewGatewayProxy.test.ts (2)

283-290: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

expect inside finally can mask the real failure.

If an assertion in the try block fails, Line 288 may throw first and replace the original error. Move the stop assertion to the end of the try block and keep finally to cleanup only.

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

In `@backend/test/pullRequestPreviewGatewayProxy.test.ts` around lines 283 - 290,
Move the stop-call assertion from the finally block to the end of the associated
try block, after the proxy behavior assertions. Keep finally limited to stopping
the proxy, closing sockets, and removing the temporary root so cleanup cannot
mask the original test failure.

28-72: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Unbounded waits turn regressions into suite hangs.

next() and closed never reject on their own, so a dropped frame surfaces as a test-runner timeout with no indication of which await stalled — and here it happens inside a spawned child, so the parent only sees a truncated tail. Racing each waiter against a short timer with a descriptive message would make failures diagnosable.

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

In `@backend/test/pullRequestPreviewGatewayProxy.test.ts` around lines 28 - 72,
The websocketHarness waits in next() and closed can hang indefinitely when
messages or closure never arrive. Add short timeout races for each pending
next() waiter and for the closed promise, rejecting with descriptive errors that
identify whether a message or socket close was awaited, while preserving
successful message delivery and closure behavior.
backend/src/pullRequestPreviewGatewayProxy.ts (2)

513-539: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Startup depends on a token file the host deletes post-readiness.

optionsFromEnvironment reads MIRA_DASHBOARD_PREVIEW_GATEWAY_UPSTREAM_TOKEN_FILE at boot, and pullRequestPreviewHost.ts removes that file immediately after the proxy reports ready (Line 1642 there). That's safe only because the transient unit is started without Restart=; adding a restart policy later would make the proxy unrecoverable. Worth a comment in the unit definition or here noting the one-shot read contract.

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

In `@backend/src/pullRequestPreviewGatewayProxy.ts` around lines 513 - 539, Add a
concise comment near optionsFromEnvironment or the relevant unit definition
documenting that the upstream token file is read only during startup and may be
deleted after readiness, so the service must not rely on restart-based recovery.
Keep the existing token-loading behavior unchanged.

251-287: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

challengeNonce is issued but never verified.

open broadcasts connect.challenge with the nonce, yet isClientAuthenticated only compares the bare token — the nonce plays no part in the handshake. Either bind the client's connect frame to the nonce, or drop challengeNonce so the handshake doesn't look stronger than it is.

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

In `@backend/src/pullRequestPreviewGatewayProxy.ts` around lines 251 - 287, Update
the authentication handshake spanning the challenge broadcast and
isClientAuthenticated so the nonce issued in connect.challenge is verified as
part of the client’s connect request, binding authentication to that challenge.
Reuse the existing challengeNonce state and request authentication parsing,
reject missing or mismatched nonces, and preserve the current token, upstream,
and successful hello-ok checks.
backend/src/releaseManifest.ts (1)

445-451: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Distinguish the build-time inventory error from the parse-time one.

Both this check and the one at Line 625 throw the identical "Release manifest artifact inventory is invalid". Naming the missing artifact (or at least the phase) makes a failed release build self-diagnosing.

♻️ Proposed tweak
-    if (
-        CURRENT_BUILD_REQUIRED_RELEASE_ARTIFACTS.some(
-            (requiredPath) => !artifactPaths.includes(requiredPath)
-        )
-    ) {
-        throw new TypeError("Release manifest artifact inventory is invalid");
-    }
+    const missingArtifacts = CURRENT_BUILD_REQUIRED_RELEASE_ARTIFACTS.filter(
+        (requiredPath) => !artifactPaths.includes(requiredPath)
+    );
+    if (missingArtifacts.length > 0) {
+        throw new TypeError(
+            `Release build is missing required artifacts: ${missingArtifacts.join(", ")}`
+        );
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/releaseManifest.ts` around lines 445 - 451, Update the validation
around CURRENT_BUILD_REQUIRED_RELEASE_ARTIFACTS to throw a build-time-specific
error instead of the generic artifact inventory message, including the missing
artifact when practical. Keep the parse-time validation near the separate check
unchanged.
backend/src/development/developmentGatewayPolicy.ts (1)

10-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive DEVELOPMENT_GATEWAY_PROXY_METHODS from the browser allowlist.

DEVELOPMENT_ALLOWED_GATEWAY_METHODS allows subscribe/unsubscribe, but the proxy allowlist duplicates methods instead of using them, and its current contents are actually more permissive (sessions.subscribe) while rejecting subscribe/unsubscribe if a browser Gateway call later uses those verbs.

♻️ Derive the proxy set from the browser set
 const DEVELOPMENT_GATEWAY_PROXY_METHODS = new Set([
-    "chat.abort",
-    "chat.history",
-    "chat.send",
-    "config.get",
-    "cron.list",
-    "models.list",
-    "sessions.list",
-    "sessions.patch",
+    ...DEVELOPMENT_ALLOWED_GATEWAY_METHODS,
     "sessions.subscribe",
 ]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/development/developmentGatewayPolicy.ts` around lines 10 - 29,
Update DEVELOPMENT_GATEWAY_PROXY_METHODS to derive from
DEVELOPMENT_ALLOWED_GATEWAY_METHODS instead of maintaining a separate method
list. Preserve the browser allowlist as the single source of truth so subscribe
and unsubscribe remain supported and no additional methods such as
sessions.subscribe are permitted.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/development/developmentStack.ts`:
- Around line 146-159: Update the log append flow around the existing
existsSync/statSync guard and appendFileSync call to serialize the complete log
line before checking the cap. Compute its UTF-8 byte length with
Buffer.byteLength, and append only when the current file size plus the pending
line size is less than or equal to MAX_DEVELOPMENT_LOG_BYTES; otherwise return
without writing.

In `@backend/src/services/databaseOverview.ts`:
- Around line 631-643: Update isDatabaseOverviewSnapshot to also require
candidate.bloatEstimates to be an array before accepting the cached snapshot,
preserving the existing validation checks and ensuring the returned snapshot
satisfies the declared BloatEstimateRow[] field contract.

---

Outside diff comments:
In `@backend/src/routes/metricsRoutes.ts`:
- Around line 106-107: Reset downloadBytes and uploadBytes immediately before
entering the /proc/net/dev fallback after the primary /sys/class/net collection
fails, so fallback totals replace any partial sums rather than accumulating on
top of them. Update the fallback flow associated with these counters while
preserving the existing successful primary-path behavior.

In `@backend/src/services/pullRequestPreviewHost.ts`:
- Around line 1599-1627: The starting record uses a stale timestamp during long
preview startup. In backend/src/services/pullRequestPreviewHost.ts:1599-1627,
refresh startingRecord.updatedAt immediately before writePreviewRecord; in
backend/src/services/pullRequestPreviewHost.ts:1328-1356, refresh the record
timestamp between proxy readiness, sandbox startup, and frontend readiness
phases so isRecentStartup reflects ongoing progress.

---

Nitpick comments:
In `@backend/src/development/developmentGatewayPolicy.ts`:
- Around line 10-29: Update DEVELOPMENT_GATEWAY_PROXY_METHODS to derive from
DEVELOPMENT_ALLOWED_GATEWAY_METHODS instead of maintaining a separate method
list. Preserve the browser allowlist as the single source of truth so subscribe
and unsubscribe remain supported and no additional methods such as
sessions.subscribe are permitted.

In `@backend/src/pullRequestPreviewGatewayProxy.ts`:
- Around line 513-539: Add a concise comment near optionsFromEnvironment or the
relevant unit definition documenting that the upstream token file is read only
during startup and may be deleted after readiness, so the service must not rely
on restart-based recovery. Keep the existing token-loading behavior unchanged.
- Around line 251-287: Update the authentication handshake spanning the
challenge broadcast and isClientAuthenticated so the nonce issued in
connect.challenge is verified as part of the client’s connect request, binding
authentication to that challenge. Reuse the existing challengeNonce state and
request authentication parsing, reject missing or mismatched nonces, and
preserve the current token, upstream, and successful hello-ok checks.

In `@backend/src/releaseManifest.ts`:
- Around line 445-451: Update the validation around
CURRENT_BUILD_REQUIRED_RELEASE_ARTIFACTS to throw a build-time-specific error
instead of the generic artifact inventory message, including the missing
artifact when practical. Keep the parse-time validation near the separate check
unchanged.

In `@backend/test/databaseOverview.test.ts`:
- Around line 476-478: Update the test around the aggregate size assertion to
configure the SQLite fixture or mock with a known storage byte count, then
assert that expected value through overview.sqlite.storageBytes before
validating totalManagedDatabaseSizeBytes. Replace the self-referential 4096 +
overview.sqlite.storageBytes expression with an independent expected total
derived from the controlled fixture value.

In `@backend/test/pullRequestPreviewGatewayProxy.test.ts`:
- Around line 283-290: Move the stop-call assertion from the finally block to
the end of the associated try block, after the proxy behavior assertions. Keep
finally limited to stopping the proxy, closing sockets, and removing the
temporary root so cleanup cannot mask the original test failure.
- Around line 28-72: The websocketHarness waits in next() and closed can hang
indefinitely when messages or closure never arrive. Add short timeout races for
each pending next() waiter and for the closed promise, rejecting with
descriptive errors that identify whether a message or socket close was awaited,
while preserving successful message delivery and closure behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e460e3b8-aa7a-4de9-be30-a3ef0e0a0cd6

📥 Commits

Reviewing files that changed from the base of the PR and between 19a5a9d and e713b44.

📒 Files selected for processing (36)
  • backend/scripts/build.ts
  • backend/src/development/developmentGatewayPolicy.ts
  • backend/src/development/developmentStack.ts
  • backend/src/lib/logRoots.ts
  • backend/src/pullRequestPreviewGatewayProxy.ts
  • backend/src/releaseManifest.ts
  • backend/src/requestPolicy.ts
  • backend/src/routes/logRoutes.ts
  • backend/src/routes/metricsRoutes.ts
  • backend/src/services/cacheRefresh.ts
  • backend/src/services/databaseOverview.ts
  • backend/src/services/logStreams.ts
  • backend/src/services/pullRequestPreviewHost.ts
  • backend/src/services/pullRequestPreviews.ts
  • backend/src/services/pullRequests.ts
  • backend/test/databaseOverview.test.ts
  • backend/test/developmentStack.test.ts
  • backend/test/pullRequestPreview.test.ts
  • backend/test/pullRequestPreviewGatewayProxy.test.ts
  • backend/test/releaseManager.test.ts
  • backend/test/releaseManifest.test.ts
  • backend/test/routeAndServiceBehavior.test.ts
  • backend/test/serviceBehavior.test.ts
  • backend/test/support/releaseFixture.ts
  • backend/test/utilityBehavior.test.ts
  • docs/api/endpoints.md
  • docs/development/local-dev.md
  • docs/setup/secrets-and-env.md
  • scripts/checkTestOutput.ts
  • src/components/layout/AppHeader.tsx
  • src/hooks/useDatabase.ts
  • src/hooks/useLogs.ts
  • src/pages/Database.tsx
  • src/pages/Logs.tsx
  • src/test/frontendBehavior.test.tsx
  • src/test/pageBehavior.test.tsx
🚧 Files skipped from review as they are similar to previous changes (13)
  • docs/api/endpoints.md
  • docs/setup/secrets-and-env.md
  • src/components/layout/AppHeader.tsx
  • src/test/pageBehavior.test.tsx
  • docs/development/local-dev.md
  • backend/src/requestPolicy.ts
  • backend/test/utilityBehavior.test.ts
  • backend/test/pullRequestPreview.test.ts
  • backend/src/services/pullRequestPreviews.ts
  • backend/test/developmentStack.test.ts
  • backend/test/serviceBehavior.test.ts
  • src/test/frontendBehavior.test.tsx
  • backend/src/services/pullRequests.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: frontend-checks
  • GitHub Check: backend-checks
🧰 Additional context used
🪛 ast-grep (0.44.1)
backend/test/releaseManager.test.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 GitHub Check: CodeQL
backend/src/development/developmentStack.ts

[failure] 149-159: Potential file system race condition
The file may have changed since it was checked.

🔇 Additional comments (37)
backend/test/releaseManager.test.ts (1)

93-103: LGTM!

Also applies to: 182-182, 430-442, 477-486, 858-933

backend/test/releaseManifest.test.ts (1)

83-86: LGTM!

Also applies to: 207-207, 239-252, 350-350

backend/test/databaseOverview.test.ts (1)

447-475: LGTM!

Also applies to: 479-480

scripts/checkTestOutput.ts (1)

15-16: LGTM!

backend/test/routeAndServiceBehavior.test.ts (3)

3121-3124: LGTM!


3133-3146: LGTM!


5096-5109: LGTM!

backend/test/support/releaseFixture.ts (1)

54-54: LGTM!

backend/src/development/developmentStack.ts (1)

3-3: LGTM!

Also applies to: 15-15, 25-25, 41-42, 91-145, 160-184, 868-905, 937-937, 953-965, 998-1002

backend/src/lib/logRoots.ts (1)

6-20: LGTM!

backend/src/services/logStreams.ts (1)

298-314: LGTM!

backend/src/routes/logRoutes.ts (1)

36-44: LGTM!

Also applies to: 153-178

backend/src/routes/metricsRoutes.ts (1)

236-243: LGTM!

src/hooks/useLogs.ts (1)

8-11: LGTM!

Also applies to: 50-65, 89-99

src/pages/Database.tsx (1)

54-59: LGTM!

src/pages/Logs.tsx (1)

159-163: LGTM!

Also applies to: 170-203, 217-237, 556-565

backend/src/services/cacheRefresh.ts (4)

14-18: LGTM!

Also applies to: 31-31


2348-2348: LGTM!

Also applies to: 2426-2438


2447-2468: LGTM!


1864-1892: 🗄️ Data Integrity & Integration

No change needed for CacheEntryRow.data.

CacheEntryRow.data is typed as a raw JSON string, so parseJsonField<unknown>(previousEntry?.data || "") is using the correct field type.

			> Likely an incorrect or invalid review comment.
backend/src/services/pullRequestPreviewHost.ts (8)

32-48: LGTM!

Also applies to: 91-96


250-262: LGTM!

Also applies to: 292-351


407-414: LGTM!

Also applies to: 424-479


919-942: LGTM!

Also applies to: 952-999


1030-1031: LGTM!

Also applies to: 1051-1055, 1111-1111


1164-1166: LGTM!

Also applies to: 1180-1226


1392-1472: LGTM!


1612-1618: LGTM!

Also applies to: 1640-1646, 1671-1686, 1712-1738

backend/src/pullRequestPreviewGatewayProxy.ts (4)

27-31: LGTM!

Also applies to: 85-136


138-194: LGTM!


196-249: LGTM!


289-368: LGTM!

Also applies to: 370-402, 481-511

backend/scripts/build.ts (1)

24-24: LGTM!

backend/src/releaseManifest.ts (1)

29-43: LGTM!

Also applies to: 621-621

backend/test/pullRequestPreviewGatewayProxy.test.ts (1)

74-282: LGTM!

backend/src/development/developmentGatewayPolicy.ts (1)

31-47: LGTM!

Also applies to: 64-71

src/hooks/useDatabase.ts (1)

6-7: LGTM!

Comment thread backend/src/development/developmentStack.ts Outdated
Comment thread backend/src/services/databaseOverview.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e713b44f1f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread backend/src/development/developmentOpenClaw.ts
Comment thread backend/src/development/developmentStack.ts
Comment thread backend/src/development/developmentStack.ts Outdated
Comment thread scripts/developmentTailscale.ts Outdated
Comment thread backend/src/requestPolicy.ts
@mira-2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 88f539ce78

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread backend/src/development/developmentStack.ts
Comment thread backend/src/services/pullRequestPreviews.ts Outdated
Comment thread backend/src/services/pullRequests.ts Outdated
Comment thread backend/src/services/pullRequestPreviewHost.ts
@mira-2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/lib/developmentProxyHeaders.ts (1)

3-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider validating publicOrigin once at startup.

new URL(...) plus the scheme check runs on every proxied request/upgrade, and a malformed configured origin fails per-request rather than at boot. Parsing/validating once when the dev server starts gives a clearer failure and avoids repeated work on the request path.

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

In `@src/lib/developmentProxyHeaders.ts` around lines 3 - 13, Move parsing and
HTTP/HTTPS validation of the configured publicOrigin out of
developmentForwardedProtocol and into the development server startup path, so
malformed configuration fails during boot. Reuse the validated origin or
protocol for each request/upgrade, while preserving requestUrl fallback behavior
when publicOrigin is unset.
backend/test/pullRequestPreviewGatewayProxy.test.ts (1)

262-276: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hardcoded 128 duplicates MAX_CLIENT_PENDING_REQUESTS.

If the proxy's limit changes, this stops exercising the boundary (or fails opaquely). Consider exporting the constant from backend/src/pullRequestPreviewGatewayProxy.ts and using it here.

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

In `@backend/test/pullRequestPreviewGatewayProxy.test.ts` around lines 262 - 276,
Replace the hardcoded pending-request value in the test around handleMessage
with the exported MAX_CLIENT_PENDING_REQUESTS constant from
pullRequestPreviewGatewayProxy, exporting that constant from the implementation
if necessary. Preserve the test’s boundary assertion and reset behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/test/pullRequestPreviewGatewayProxy.test.ts`:
- Around line 262-276: Replace the hardcoded pending-request value in the test
around handleMessage with the exported MAX_CLIENT_PENDING_REQUESTS constant from
pullRequestPreviewGatewayProxy, exporting that constant from the implementation
if necessary. Preserve the test’s boundary assertion and reset behavior.

In `@src/lib/developmentProxyHeaders.ts`:
- Around line 3-13: Move parsing and HTTP/HTTPS validation of the configured
publicOrigin out of developmentForwardedProtocol and into the development server
startup path, so malformed configuration fails during boot. Reuse the validated
origin or protocol for each request/upgrade, while preserving requestUrl
fallback behavior when publicOrigin is unset.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 197f74de-19a4-472d-b480-48e38ce91cb2

📥 Commits

Reviewing files that changed from the base of the PR and between e713b44 and af4b8fc.

📒 Files selected for processing (21)
  • backend/src/development/developmentOpenClaw.ts
  • backend/src/development/developmentStack.ts
  • backend/src/pullRequestPreviewGatewayProxy.ts
  • backend/src/requestPolicy.ts
  • backend/src/routes/taskRoutes.ts
  • backend/src/services/databaseOverview.ts
  • backend/src/services/pullRequestPreviewHost.ts
  • backend/src/services/pullRequestPreviews.ts
  • backend/src/services/pullRequests.ts
  • backend/test/databaseOverview.test.ts
  • backend/test/developmentStack.test.ts
  • backend/test/pullRequestPreview.test.ts
  • backend/test/pullRequestPreviewGatewayProxy.test.ts
  • backend/test/routeAndServiceBehavior.test.ts
  • backend/test/serviceBehavior.test.ts
  • backend/test/utilityBehavior.test.ts
  • scripts/developmentFrontend.ts
  • scripts/developmentTailscale.ts
  • src/lib/developmentProxyHeaders.ts
  • src/test/developmentProxyHeaders.test.ts
  • src/test/developmentTailscale.test.ts
🚧 Files skipped from review as they are similar to previous changes (13)
  • backend/test/databaseOverview.test.ts
  • scripts/developmentFrontend.ts
  • backend/test/developmentStack.test.ts
  • backend/src/pullRequestPreviewGatewayProxy.ts
  • backend/src/services/databaseOverview.ts
  • scripts/developmentTailscale.ts
  • backend/test/pullRequestPreview.test.ts
  • backend/src/development/developmentOpenClaw.ts
  • backend/test/utilityBehavior.test.ts
  • backend/src/development/developmentStack.ts
  • backend/src/services/pullRequestPreviews.ts
  • backend/src/services/pullRequestPreviewHost.ts
  • backend/src/services/pullRequests.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: frontend-checks
  • GitHub Check: backend-checks
🔇 Additional comments (10)
backend/src/requestPolicy.ts (1)

172-177: LGTM!

backend/src/routes/taskRoutes.ts (2)

11-11: LGTM!


319-332: LGTM!

backend/test/pullRequestPreviewGatewayProxy.test.ts (2)

13-28: LGTM!


294-351: LGTM!

backend/test/serviceBehavior.test.ts (1)

1918-1975: LGTM!

src/test/developmentProxyHeaders.test.ts (1)

3-29: LGTM!

src/test/developmentTailscale.test.ts (2)

1-108: LGTM!


110-155: LGTM!

backend/test/routeAndServiceBehavior.test.ts (1)

1029-1050: 📐 Maintainability & Code Quality

No change needed.

The length assertion can fail because notifyMira returns immediately when dev safe mode is enabled, while the mocked sendSessionMessage appends synchronously. The inline restore is redundant only in that rememberEnvironment already scheduled cleanup, but it does not introduce a correctness issue.

			> Likely an incorrect or invalid review comment.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: af4b8fc15b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@mira-2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: cb92bc20b1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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

Labels

area: backend Backend API, server routes, services, and integrations area: frontend Frontend UI, client state, routing, and browser behavior area: ops Operational actions, deploys, services, backups, and health priority: high High-priority work for the next active cycle status: needs-review Ready for human or agent review type: feature Adds user-visible functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants