Skip to content

[codex] Port Reborn WebUI projects and settings coverage - #5375

Merged
serrrfirat merged 2 commits into
mainfrom
codex/reborn-webui-v2-projects-settings
Jun 30, 2026
Merged

serrrfirat merged 2 commits into
mainfrom
codex/reborn-webui-v2-projects-settings

Conversation

@ilblackdragon

@ilblackdragon ilblackdragon commented Jun 27, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Ports browser coverage for projects, settings search/provider flows, skills, and tool permissions.
  • Updates project API/page behavior, settings tab routing/search state, tool permission controls, and generated WebUI bundle.
  • Addresses CodeRabbit feedback around failed project chat navigation, settings import error copy, E2E context cleanup, selector centralization, and dialog handling.

Change Type

  • Test coverage and WebUI v2 behavior fix.

Linked Issue

  • None.

Security Impact

  • No auth, secrets, listeners, sandboxing, outbound HTTP, or permission-gate behavior is changed.
  • Tool permission coverage exercises existing UI/API flows without changing trust boundaries.

Trust-Boundary Checklist

  • No new trusted ingress or request minting.
  • No changes to bearer-token, webhook, CORS/origin, body-limit, rate-limit, allowlist, or secret-handling logic.
  • E2E mocks remain local test harness routes only.

Database Impact

  • None. No schema, migration, persistence, PostgreSQL, or libSQL changes.

Blast Radius

  • WebUI v2 projects and settings surfaces.
  • Reborn WebUI v2 E2E scenarios for projects, settings search/provider flows, skills, and tool permissions.

Rollback Plan

  • Revert this PR commit. No migration or data cleanup is required.

Follow-up

  • None planned.

Validation

  • python3 -m py_compile tests/e2e/helpers.py tests/e2e/scenarios/test_reborn_webui_v2_legacy_projects.py tests/e2e/scenarios/test_reborn_webui_v2_legacy_settings_search.py tests/e2e/scenarios/test_reborn_webui_v2_legacy_skills.py tests/e2e/scenarios/test_reborn_webui_v2_legacy_tool_permissions.py
  • node --check crates/ironclaw_webui_v2_static/static/js/pages/projects/projects-page.js crates/ironclaw_webui_v2_static/static/js/pages/settings/hooks/useSettings.js crates/ironclaw_webui_v2_static/static/js/pages/settings/settings-page.js
  • node --test crates/ironclaw_webui_v2_static/static/js/pages/settings/components/tools-tab.test.mjs crates/ironclaw_webui_v2_static/static/js/pages/settings/components/provider-components.test.mjs crates/ironclaw_webui_v2_static/static/js/pages/settings/components/skill-install-panel.test.mjs crates/ironclaw_webui_v2_static/static/js/pages/settings/lib/api-result.test.mjs crates/ironclaw_webui_v2_static/static/js/pages/settings/lib/settings-api.test.mjs crates/ironclaw_webui_v2_static/static/js/pages/settings/lib/llm-providers.test.mjs (51 passed)
  • cargo fmt --all -- --check

@coderabbitai

coderabbitai Bot commented Jun 27, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a settings toolbar with search, import, and export actions.
    • Project chats now open directly to the created thread when available.
    • Project workspaces can now show real thread activity instead of empty placeholder data.
  • Bug Fixes

    • Improved empty-state handling in project lists and search results.
    • Settings now show import errors separately from save errors.
    • Channel names display a more accurate fallback when preferred labels are missing.

Walkthrough

Adds project and settings UI identification attributes, wires project thread listing to the v2 API, changes project chat navigation to thread-path routing, wires a settings toolbar into SettingsPage, and adds Playwright coverage for projects, settings search, skills, and tool permissions.

Changes

Projects Page: API wiring, navigation, and selectors

Layer / File(s) Summary
Project thread API wiring
crates/ironclaw_webui_v2_static/static/js/pages/projects/lib/projects-api.js
fetchProjectThreads(projectId) now calls apiListThreads({ projectId, limit: 200 }), normalizes thread fields with toPageThread, and returns next_cursor.
Project chat navigation
crates/ironclaw_webui_v2_static/static/js/pages/projects/projects-page.js
handleCreateProject and handleStartConversation now route to /chat/<threadId> when a thread id is available.
Projects grid state and selectors
crates/ironclaw_webui_v2_static/static/js/pages/projects/components/projects-grid.js
Projects cards, open-workspace buttons, the grid wrapper, and the search input gain data-testid/data-project-id attributes, and the empty-state branching changes around totalProjects and no-match rendering.
Workspace shell and filesystem entry selectors
crates/ironclaw_webui_v2_static/static/js/pages/projects/components/project-workspace-shell.js, crates/ironclaw_webui_v2_static/static/js/pages/projects/components/project-filesystem-panel.js
The workspace wrapper/title and filesystem entry buttons gain test identifiers plus project/path metadata.
Projects E2E scenarios
tests/e2e/scenarios/test_reborn_webui_v2_legacy_projects.py
Adds mocked projects/thread/files endpoints and scenarios for search, opening workspaces, creating chats, scoped conversations, and file downloads.

Settings Page: toolbar wiring, component selectors, and E2E coverage

Layer / File(s) Summary
Settings toolbar wiring
crates/ironclaw_webui_v2_static/static/js/pages/settings/settings-page.js, crates/ironclaw_webui_v2_static/static/js/pages/settings/hooks/useSettings.js
SettingsPage renders SettingsToolbar, passes export/import/search state, and shows an import error banner; useSettings() now exposes separate saveError and importError fields.
Settings component selectors
crates/ironclaw_webui_v2_static/static/js/pages/settings/components/channels-tab.js, crates/ironclaw_webui_v2_static/static/js/pages/settings/components/tools-tab.js
ExtensionChannelCard prefers channel.display_name in its fallback chain, and ToolRow wraps the lock icon in a test-id-bearing span.
Settings search and provider E2E
tests/e2e/helpers.py, tests/e2e/scenarios/test_reborn_webui_v2_legacy_settings_search.py
Adds new V2 selectors plus mocked settings routes and scenarios for tools, skills, channels, and LLM provider add/edit/delete flows.
Skills lifecycle E2E
tests/e2e/scenarios/test_reborn_webui_v2_legacy_skills.py
Adds mocked skills routes, helper actions, and scenarios for visibility, add/edit/delete, and read-only sources.
Tool permissions E2E
tests/e2e/scenarios/test_reborn_webui_v2_legacy_tool_permissions.py
Adds mocked tool permission routes, real-API helpers, gate streaming support, and scenarios for permission persistence, locked tools, auto-approve toggles, and restart survival.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • nearai/ironclaw#5019: Both PRs touch the Projects frontend API path in projects-api.js, moving the page from stubbed behavior toward real v2-backed data.
  • nearai/ironclaw#5345: Both PRs extend the Reborn WebUI v2 Playwright harness and selector map used by the SPA tests.
  • nearai/ironclaw#5371: This PR’s project-thread fetch path depends on listThreads({ projectId }) support added there.

Suggested reviewers

  • think-in-universe

Poem

A thread now walks the real API road,
And chat takes /chat/<id> as load.
The toolbar blooms, the selectors sing,
With tests that poke at every thing.
From skills to tools, from files to gates,
The UI hums in stateful states.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the PR’s main change: porting Reborn WebUI projects and settings coverage.
Description check ✅ Passed The description covers the required areas and is mostly complete, with only minor template-format omissions.
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.

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several WebChat v2 updates, including project-filtered thread listing, a settings toolbar with search and import/export capabilities, fallback channel display names, and updated navigation logic when creating chat threads. It also adds extensive E2E test coverage for legacy projects, settings search, skills, and tool permissions, along with supporting data-testid attributes. Feedback was provided on projects-page.js to ensure navigation only occurs when a thread ID is successfully created, preventing the app from navigating away and hiding error banners on failure.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +69 to +74
navigate(nextThreadId ? `/chat/${nextThreadId}` : "/chat", {
state: {
composerDraft: t("projects.creationDraft"),
threadId: nextThreadId,
},
});
}, [navigate, threadsState]);
}, [navigate, threadsState, t]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

If threadsState.createThread() fails, an error is caught and chatFlowError is set. However, because navigate is called unconditionally, the application will immediately navigate to /chat, unmounting the ProjectsPage and discarding the local error state before the user can see the feedback banner.

To fix this, we should only navigate if nextThreadId was successfully created, allowing the error banner to remain visible on failure.

    if (nextThreadId) {
      navigate("/chat/" + nextThreadId, {
        state: {
          composerDraft: t("projects.creationDraft"),
        },
      });
    }
  }, [navigate, threadsState, t]);

Base automatically changed from codex/reborn-webui-v2-extensions to main June 30, 2026 10:30
Copilot AI review requested due to automatic review settings June 30, 2026 10:39
@serrrfirat
serrrfirat force-pushed the codex/reborn-webui-v2-projects-settings branch from c7cbcfc to 7250adb Compare June 30, 2026 10:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review is ineligible. To be eligible to request a review, you need a paid Copilot license, or your organization must enable Copilot code review.

@github-actions github-actions Bot added size: M 50-199 changed lines and removed size: XL 500+ changed lines labels Jun 30, 2026

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
crates/ironclaw_webui_v2_static/static/js/pages/projects/projects-page.js (1)

57-74: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

handleCreateProject navigates away even when thread creation fails, discarding the error.

On a createThread() throw, setChatFlowError(...) fires but the unconditional navigate(...) at Line 69 still runs, unmounting ProjectsPage before its FeedbackBanner (Line 169) can render. handleStartConversation correctly keeps navigate inside the try. Mirror that here so failures stay on the projects page with the banner visible.

Proposed fix
-    try {
-      nextThreadId = await threadsState.createThread();
-    } catch (error) {
-      setChatFlowError({
-        type: "error",
-        message: error.message || t("projects.chatAutoFail"),
-      });
-    }
-
-    navigate(nextThreadId ? `/chat/${nextThreadId}` : "/chat", {
-      state: {
-        composerDraft: t("projects.creationDraft"),
-      },
-    });
+    try {
+      nextThreadId = await threadsState.createThread();
+    } catch (error) {
+      setChatFlowError({
+        type: "error",
+        message: error.message || t("projects.chatAutoFail"),
+      });
+      return;
+    }
+
+    navigate(nextThreadId ? `/chat/${nextThreadId}` : "/chat", {
+      state: {
+        composerDraft: t("projects.creationDraft"),
+      },
+    });
🤖 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 `@crates/ironclaw_webui_v2_static/static/js/pages/projects/projects-page.js`
around lines 57 - 74, `handleCreateProject` currently navigates unconditionally
after `threadsState.createThread()`, which causes the page to unmount even when
the thread creation fails. Move the `navigate(...)` call in
`handleCreateProject` so it only runs after a successful `createThread()`
result, matching the `handleStartConversation` pattern, and keep failures on
`ProjectsPage` so `setChatFlowError(...)` can be shown by `FeedbackBanner`.
🤖 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 `@crates/ironclaw_webui_v2_static/static/js/pages/settings/settings-page.js`:
- Around line 111-119: The settings page is showing import failures through the
save-error path, which mislabels the message in the UI. Update the error
handling in settings-page.js so the banner tied to
SettingsToolbar/importSettings does not reuse saveError from useSettings;
instead, split save and import error state in useSettings or render a neutral
settings-operation error when importMutation.error is present. Use the existing
useSettings hook and the settings page banner logic to route import failures to
the correct message.

In `@tests/e2e/scenarios/test_reborn_webui_v2_legacy_projects.py`:
- Around line 66-225: The setup helper leaks a Playwright browser context when
navigation or the initial grid assertion fails before returning. Update
_open_mocked_projects_page() so it always closes the created context on any
exception from page.goto() or the expect(page.locator(...)) wait, while still
returning the context normally on success. Use the existing context and page
setup in _open_mocked_projects_page and keep the cleanup local to this helper so
callers do not have to change.

In `@tests/e2e/scenarios/test_reborn_webui_v2_legacy_settings_search.py`:
- Around line 321-324: The `_provider_card` helper and related v2 settings
locators are hardcoding `data-testid` selectors, which violates the e2e selector
invariant. Move the raw selector strings into `helpers.SEL_V2` and update the
affected helpers and tests to reference those shared constants instead of inline
selectors. Apply the same cleanup pattern to the other v2 UI locators mentioned
in the review, such as the search placeholder and `llm-provider-disclosure`, so
all selectors are sourced consistently from `SEL_V2`.

In `@tests/e2e/scenarios/test_reborn_webui_v2_legacy_skills.py`:
- Around line 203-217: The delete-confirmation dialog handling in
test_reborn_webui_v2_legacy_skills.py currently schedules dialog.accept() on an
unawaited task inside handle_dialog, which can make the test flaky. Update the
dialog callback used with page.once("dialog", ...) so the acceptance runs on the
awaited path for dialog handling rather than via loop.create_task, while keeping
the dialog_future assertion flow in place. Use the existing handle_dialog
callback and dialog.accept call site to locate the fix.

---

Outside diff comments:
In `@crates/ironclaw_webui_v2_static/static/js/pages/projects/projects-page.js`:
- Around line 57-74: `handleCreateProject` currently navigates unconditionally
after `threadsState.createThread()`, which causes the page to unmount even when
the thread creation fails. Move the `navigate(...)` call in
`handleCreateProject` so it only runs after a successful `createThread()`
result, matching the `handleStartConversation` pattern, and keep failures on
`ProjectsPage` so `setChatFlowError(...)` can be shown by `FeedbackBanner`.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dbf347ff-a8c2-4cc3-a86c-e87b87fe9abb

📥 Commits

Reviewing files that changed from the base of the PR and between d052ec1 and 7250adb.

📒 Files selected for processing (12)
  • crates/ironclaw_webui_v2_static/static/js/pages/projects/components/project-filesystem-panel.js
  • crates/ironclaw_webui_v2_static/static/js/pages/projects/components/project-workspace-shell.js
  • crates/ironclaw_webui_v2_static/static/js/pages/projects/components/projects-grid.js
  • crates/ironclaw_webui_v2_static/static/js/pages/projects/lib/projects-api.js
  • crates/ironclaw_webui_v2_static/static/js/pages/projects/projects-page.js
  • crates/ironclaw_webui_v2_static/static/js/pages/settings/components/channels-tab.js
  • crates/ironclaw_webui_v2_static/static/js/pages/settings/components/tools-tab.js
  • crates/ironclaw_webui_v2_static/static/js/pages/settings/settings-page.js
  • tests/e2e/scenarios/test_reborn_webui_v2_legacy_projects.py
  • tests/e2e/scenarios/test_reborn_webui_v2_legacy_settings_search.py
  • tests/e2e/scenarios/test_reborn_webui_v2_legacy_skills.py
  • tests/e2e/scenarios/test_reborn_webui_v2_legacy_tool_permissions.py

Comment thread tests/e2e/scenarios/test_reborn_webui_v2_legacy_projects.py Outdated
Comment thread tests/e2e/scenarios/test_reborn_webui_v2_legacy_settings_search.py
Comment thread tests/e2e/scenarios/test_reborn_webui_v2_legacy_skills.py

@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 (2)
tests/e2e/scenarios/test_reborn_webui_v2_legacy_settings_search.py (1)

235-258: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Return the real LLM snapshot shape from write mocks.

The upstream v2 handlers return LlmConfigSnapshot for provider upsert, active-provider changes, and delete, but these mocks return {provider}, {active}, and {success}. That lets provider flows pass against response bodies production never sends.

Proposed fix
+        def snapshot() -> dict:
+            return {
+                "providers": llm_state["providers"],
+                "active": llm_state.get("active"),
+            }
+
         if path == "/api/webchat/v2/llm/providers" and method == "GET":
             await fulfill_json(
                 route,
-                {
-                    "providers": llm_state["providers"],
-                    "active": llm_state.get("active"),
-                },
+                snapshot(),
             )
             return
@@
-            await fulfill_json(route, {"provider": provider})
+            await fulfill_json(route, snapshot())
             return
@@
-            await fulfill_json(route, {"active": llm_state["active"]})
+            await fulfill_json(route, snapshot())
             return
@@
             llm_state["providers"] = [
                 provider
                 for provider in llm_state["providers"]
                 if provider["id"] != provider_id
             ]
-            await fulfill_json(route, {"success": True})
+            if (llm_state.get("active") or {}).get("provider_id") == provider_id:
+                llm_state["active"] = None
+            await fulfill_json(route, snapshot())
             return

As per coding guidelines, "When mocking a browser/runtime API in a test, the mock signature must match the production call signature, and assertions must cover every argument passed by production code."

Also applies to: 282-295

🤖 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 `@tests/e2e/scenarios/test_reborn_webui_v2_legacy_settings_search.py` around
lines 235 - 258, The mocked write handlers for LLM config changes return
simplified bodies that do not match the real v2 API response shape. Update the
mocks in the test scenario around the provider upsert and active-provider
handlers to return the same LlmConfigSnapshot-style payloads produced by the
upstream endpoints, and apply the same fix to the delete flow referenced in the
review. Keep the existing llm_state updates, but make the responses mirror
production so assertions in the provider flow are exercised against realistic
data.

Source: Coding guidelines

tests/e2e/scenarios/test_reborn_webui_v2_legacy_tool_permissions.py (1)

219-251: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use tests/e2e/helpers.py:sse_stream() for this gate stream
tests/e2e/CLAUDE.md routes raw SSE through the shared helper; this hand-rolled httpx.AsyncClient stream bypasses that aiohttp path and duplicates frame parsing.

🤖 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 `@tests/e2e/scenarios/test_reborn_webui_v2_legacy_tool_permissions.py` around
lines 219 - 251, The gate-prompt wait helper is bypassing the shared SSE path by
opening a raw httpx stream and manually parsing frames. Update
_wait_for_gate_prompt_after_send to use tests/e2e/helpers.py:sse_stream()
instead, and keep the gate-event extraction logic there so SSE handling stays
centralized and consistent with the rest of the e2e tests.

Sources: Coding guidelines, Path instructions

🤖 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 `@tests/e2e/scenarios/test_reborn_webui_v2_legacy_settings_search.py`:
- Around line 235-258: The mocked write handlers for LLM config changes return
simplified bodies that do not match the real v2 API response shape. Update the
mocks in the test scenario around the provider upsert and active-provider
handlers to return the same LlmConfigSnapshot-style payloads produced by the
upstream endpoints, and apply the same fix to the delete flow referenced in the
review. Keep the existing llm_state updates, but make the responses mirror
production so assertions in the provider flow are exercised against realistic
data.

In `@tests/e2e/scenarios/test_reborn_webui_v2_legacy_tool_permissions.py`:
- Around line 219-251: The gate-prompt wait helper is bypassing the shared SSE
path by opening a raw httpx stream and manually parsing frames. Update
_wait_for_gate_prompt_after_send to use tests/e2e/helpers.py:sse_stream()
instead, and keep the gate-event extraction logic there so SSE handling stays
centralized and consistent with the rest of the e2e tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9e6ee845-12cd-415f-91a3-4dc54040585d

📥 Commits

Reviewing files that changed from the base of the PR and between 7250adb and dbf31b1.

📒 Files selected for processing (8)
  • crates/ironclaw_webui_v2_static/static/js/pages/projects/projects-page.js
  • crates/ironclaw_webui_v2_static/static/js/pages/settings/hooks/useSettings.js
  • crates/ironclaw_webui_v2_static/static/js/pages/settings/settings-page.js
  • tests/e2e/helpers.py
  • tests/e2e/scenarios/test_reborn_webui_v2_legacy_projects.py
  • tests/e2e/scenarios/test_reborn_webui_v2_legacy_settings_search.py
  • tests/e2e/scenarios/test_reborn_webui_v2_legacy_skills.py
  • tests/e2e/scenarios/test_reborn_webui_v2_legacy_tool_permissions.py

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

Labels

contributor: core 20+ merged PRs risk: low Changes to docs, tests, or low-risk modules size: M 50-199 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants