Skip to content

[5528] test(frontend): run web/oss unit tests in CI - #5567

Open
ardaerzin wants to merge 2 commits into
release/v0.106.2from
claude/github-issue-5528-3377d2
Open

[5528] test(frontend): run web/oss unit tests in CI#5567
ardaerzin wants to merge 2 commits into
release/v0.106.2from
claude/github-issue-5528-3377d2

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

Fixes #5528.

Problem

The unit test files colocated under web/oss/src imported from vitest but never executed — not in CI, not locally. web/oss had no vitest config, vitest was not a dependency, and package.json declared no test:unit script. The unit-layer runner selects packages with pnpm -r --if-present run test:unit (web/tests/playwright/scripts/run-tests.ts), so a package without that script is skipped silently and the "Run web unit tests" job still passes. The suites passed review while asserting nothing.

Fix

  • web/oss/vitest.config.ts (new): colocated glob src/**/*.test.{ts,tsx} (oss keeps tests next to source, unlike the @agenta/* packages under tests/unit/), the two tsconfig.json path aliases (@/oss/*, @/agenta-oss-common/*), jsdom environment (some tests touch window.localStorage), and the junit reporter writing to test-results/junit.xml.
  • @vitejs/plugin-react-swc for the JSX transform. tsconfig.json sets jsx: "preserve" for the Next build, which leaves JSX in the source and breaks vite's parser the moment a test transitively imports a .tsx (the state-selector tests reach AlertPopup.tsx through the app graph). The SWC React plugin transforms JSX itself and ignores that tsconfig setting.
  • web/oss/package.json: test / test:unit / test:watch / test:coverage scripts and dev deps (vitest, @vitest/coverage-v8, jsdom, @vitejs/plugin-react-swc). test:unit is what makes the recursive runner pick oss up.
  • .github/workflows/12-check-unit-tests.yml: publish web/oss/test-results/junit.xml alongside the per-package reports (oss is not under web/packages/*).
  • web/oss/.gitignore: ignore /test-results.

Result

cd web/oss && pnpm run test:unit runs 14 files / 98 tests, all passing (the issue listed 13; a 14th, transcriptToMessages.test.ts, was added since). A failing suite exits non-zero — during setup the pre-fix parse failures produced exit code 1 while other tests passed — so a failing assertion now fails the build.

Notes

  • The oss suite currently takes ~6.5 min, dominated by three state-selector tests that import the entire app graph (queryClient, services, appState, antd). Trimming their imports is a reasonable follow-up but out of scope here.
  • All 98 tests passed — the issue anticipated some might fail once enforced, but none did.
  • web/ee/src has no test files today, so it is unaffected; the same wiring could be added there if/when it gains tests.

The 13+ unit test files colocated under web/oss/src imported from vitest
but never executed: web/oss had no vitest config, no vitest dependency,
and no test:unit script, so the recursive runner
(pnpm -r --if-present run test:unit) skipped the package silently and the
CI job still passed.

Add a vitest config (colocated src/**/*.test glob, tsconfig path aliases,
jsdom env, junit reporter), the vitest/coverage/jsdom dev deps, and the
test scripts so the suite runs in the existing unit layer and a failing
assertion fails the build. Use @vitejs/plugin-react-swc to transform JSX,
since tsconfig's jsx: preserve otherwise breaks vite's parser when a test
transitively imports a .tsx. Publish web/oss/test-results/junit.xml from
the workflow alongside the per-package reports.

All 14 files (98 tests) run and pass.
@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Jul 30, 2026
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview Jul 30, 2026 4:12pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 970214de-caa1-4022-9393-0cb27d6b711f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added optional voice dictation and audio messages in Agent Chat.
    • Added drag-and-drop file staging, upload progress, retry controls, previews, and inline audio playback.
    • Added simplified navigation for new signups, with a Classic mode toggle.
    • Added Feature Flags and Organization General settings pages.
    • Added clearer waiting and connection controls for paused agent interactions.
    • Prompts and agents now open directly in Playground.
  • Bug Fixes

    • Improved file downloads, archive handling, routing, and timeout behavior.
    • Improved schedule editing and validation experiences.
  • Documentation

    • Added guidance for announcements, changelogs, feature planning, API documentation, and issue writing.

Walkthrough

This release updates agent skills, API and mount handling, runner reliability, Agent Chat uploads and voice interactions, onboarding navigation, settings, shared entity contracts, table imports, gateway-trigger flows, tests, and package metadata.

Changes

Agent skill documentation

Layer / File(s) Summary
Skill workflows and Claude wiring
.agents/skills/*, .claude/skills/*, .gitignore
Adds reusable skill workflows for announcements, planning, implementation, API docs, model catalogs, issues, and social posts; Claude entries now link to the corresponding agent skills.

Backend and runner behavior

Layer / File(s) Summary
API and mount handling
api/entrypoints/*, api/oss/src/apis/fastapi/mounts/*, api/oss/src/core/mounts/*, api/oss/src/middlewares/*
Adds route-aware /api normalization, explicit binary response schemas, and zip namespace collision filtering including degraded agent-files handling.
Runner and deployment configuration
services/runner/*, hosting/railway/*, hosting/docker-compose/*
Bounds environment-driven timers and retries, adds record-query timeouts and persistence-drop warnings, enables Docker init handling, and configures the runner host and Railway credential reuse.
Validation and test execution
api/oss/tests/*, services/runner/tests/*, .github/workflows/*, web/oss/vitest.config.ts
Centralizes mount-storage skips, adds archive and middleware regression coverage, adds runner timeout/persistence tests, and wires OSS Vitest/JUnit reporting.

Agent Chat and drives

Layer / File(s) Summary
Composer attachments and drive uploads
web/oss/src/components/AgentChatSlice/*, web/oss/src/components/Drives/*
Adds feature-gated file uploads, staged drag-and-drop, progress/retry states, local file previews, mount uploads, audio playback, and attachment viewer integration.
Voice and parked interactions
web/oss/src/components/AgentChatSlice/components/*, web/oss/src/components/AgentChatSlice/hooks/*, web/packages/agenta-ui/src/RichChatInput/*
Adds dictation, microphone recording and waveform UI, parked OAuth connection handling through InteractionDock, held queue indicators, and shared dictation/send-button APIs.

Navigation and settings

Layer / File(s) Summary
Simplified navigation and settings scopes
web/oss/src/lib/onboarding/*, web/oss/src/state/onboarding/*, web/oss/src/components/Sidebar/*, web/oss/src/components/pages/settings/*
Adds persisted simplified-navigation defaults and overrides, hides advanced sidebar entries, introduces scoped settings sections, and adds Classic mode, inspector, and organization-general settings pages.
Playground routing and first-run state
web/oss/src/pages/*, web/oss/src/components/pages/agent-home/*, web/oss/src/components/pages/agents/*
Routes app, prompt, and agent entry points to Playground and adds query-backed first-run detection and created-agent cache registration.

Shared contracts and UI migration

Layer / File(s) Summary
Table and tracing migration
web/oss/src/components/InfiniteVirtualTable/*, web/oss/src/components/EvalRunDetails/*, web/oss/src/components/EvaluationRunsTablePOC/*, web/packages/agenta-ui/src/InfiniteVirtualTable/*
Removes the OSS-local table implementation from consumers, moves imports to @agenta/ui/table, gates table export by project permissions, and extends shared column metadata types.
Entity and gateway contracts
web/packages/agenta-entities/*, web/packages/agenta-entity-ui/*, web/oss/src/services/tracing/*
Consolidates evaluation and trace types, prevents deduplication of unidentified trace rows, detects message-shaped schedule payloads, adds model modality lookup, and centralizes run-version binding helpers.
Package and compiler configuration
web/package.json, web/oss/package.json, web/ee/*, web/packages/*/package.json, */pyproject.toml, hosting/kubernetes/helm/Chart.yaml
Bumps release and dependency versions, adds type-check/test scripts, adds CSS module declarations, and updates package peer constraints.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also contains large unrelated refactors and removals beyond test-run wiring, including the InfiniteVirtualTable teardown and many UI/state changes. Split the unrelated refactors into separate PRs and keep this one focused on web/oss unit-test execution and CI wiring.
Docstring Coverage ⚠️ Warning Docstring coverage is 27.54% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR adds Vitest config, scripts, deps, and JUnit reporting so web/oss tests run in the unit-test layer and fail the build.
Title check ✅ Passed The title matches the PR’s main goal: enabling web/oss unit tests in CI.
Description check ✅ Passed The description is on-topic and accurately describes the testing, Vitest, and CI changes.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/github-issue-5528-3377d2

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.

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

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (2)
web/oss/src/hooks/usePostAuthRedirect.ts (1)

142-154: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Initialize simplified navigation before the invitation return.

Invited first-time users return at lines 133–136 before either assignment here, leaving their per-user default false. Set the default immediately inside if (isNewUser) so every new signup receives the intended navigation baseline.

Proposed fix
             if (isNewUser) {
+                setNavSimplifiedDefault(true)
                 if (isInvitedUser) {
...
-                    setNavSimplifiedDefault(true)
                     await router.push("/post-signup")
...
-                    setNavSimplifiedDefault(true)
                 }
             }
web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/shared/RunVersionField.tsx (1)

171-210: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Existing environment-bound schedules/subscriptions lose their version picker UI once "Deployed" is hidden. The shared root cause is in RunVersionField.tsx: hideEnvironment forces the revision-picker branch and removes the "Deployed" rail option unconditionally, but both drawers can still prefill bindMode = "environment" for pre-existing environment-bound entities, whose header summary continues to display "env: X" while the expanded section now shows an unrelated, unbound "Pinned" picker.

  • web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/shared/RunVersionField.tsx#L171-L210: branch the rendered content on the actual bindMode (e.g. still show a read-only/environment summary when bindMode === "environment") instead of letting hideEnvironment unconditionally force the revision branch.
  • web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerScheduleDrawer.tsx#L848-L863: once the component supports it, pass through enough context (or a read-only environment view) so an environment-bound schedule remains visible/explainable here.
  • web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerSubscriptionDrawer.tsx#L1047-L1061: same fix needed for the subscription drawer's identical call site.
🟠 Major comments (20)
docs/design/simplify-nav-new-users/plan.md-148-152 (1)

148-152: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Use a genuinely user-scoped override key.

Line 149 uses the literal "<userId>"; if implemented as written, every account in the same browser shares the Classic-mode preference. Reuse the Phase 1 scoped storage family and createScopedStorageKey pattern.

.agents/skills/add-announcement/SKILL.md-1-5 (1)

1-5: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Align the tool allowlist with the documented workflow.

The skill allows only Read, Edit, Grep, and Glob, but later instructs the agent to run cat, code, git add, and git commit. Add the required tools, or explicitly mark those commands as user-run instructions; otherwise the skill cannot perform its own documented workflow.

.agents/skills/add-announcement/SKILL.md-200-204 (1)

200-204: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use the canonical changelog entry path.

These instructions create entries under docs/docs/changelog, but .agents/skills/create-changelog-announcement/SKILL.md defines docs/blog/entries/[feature-slug].mdx as the source consumed by the changelog index. Following this skill can create a page the changelog build does not discover.

.agents/skills/add-announcement/SKILL.md-219-223 (1)

219-223: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use GitButler commands instead of raw Git commands.

This workflow teaches git add and git commit, which bypasses the repository’s lane assignment and stack safeguards. Replace this with the documented but rub and but commit <lane> --only flow, or clearly scope the example to non-GitButler repositories.

Per coding guidelines, when GitButler workspace mode is active, use but and verify lane scope before committing.

Source: Coding guidelines

.agents/skills/create-changelog-announcement/SKILL.md-321-321 (1)

321-321: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Avoid direct peer-skill instruction access.

This line tells the skill to read another skill’s SKILL.md, exposing prompt instructions and capabilities across skills. Inline the needed social-announcement requirements or pass sanitized guidance through an orchestrator instead.

Source: Linters/SAST tools

.agents/skills/create-changelog-announcement/SKILL.md-372-384 (1)

372-384: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make the example follow the required Summary/truncate contract.

The example entry starts with front matter and the full write-up but omits <Summary> and {/* truncate */}, even though the required structure is documented earlier. Users copying this example can produce entries without the intended index preview or page split.

.agents/skills/implement-feature/SKILL.md-190-191 (1)

190-191: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Resolve the contradictory push policy.

Phase 6 says to stop unless the user asks to push, but the parallel-agent section later tells agents to push each lane when a slice is done. Choose one policy, preferably explicit user approval, and apply it consistently to avoid publishing shared-workspace changes unexpectedly.

.agents/skills/plan-feature/SKILL.md-1-3 (1)

1-3: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use plan-feature as the skill name

This skill is referenced by directory and documentation as plan-feature, but .agents/skills/plan-feature/SKILL.md declares name: planner-feature. Keep the frontmatter name consistent with the directory to avoid ambiguity and inconsistent tooling resolution.

.agents/skills/update-llm-model-list/SKILL.md-37-70 (1)

37-70: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin LiteLLM for every audit command.

Both PEP 723 scripts declare unpinned dependencies = ["litellm"], and the uvx --with litellm invocations resolve the latest release at runtime. Pin LiteLLM in the script metadata and in each uvx command, updating it deliberately when regenerating the model list.

Also applies to: 86-138, 174-180

Source: Linters/SAST tools

.agents/skills/update-llm-model-list/SKILL.md-50-53 (1)

50-53: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Scope the slash-fallback to Anthropic and Cohere.

exists() currently strips the first path component for any provider/model entry, so unsupported/model can pass whenever model exists in LiteLLM. Restrict fallback matching to the documented anthropic/ and cohere/ prefixes (lines 23-26).

.agents/skills/update-llm-model-list/SKILL.md-45-46 (1)

45-46: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the “no local install” command import the local SDK.

uvx --with litellm only installs litellm in the isolated tool environment; it does not make the repository’s agenta package importable. The script at .agents/skills/update-llm-model-list/SKILL.md:46 imports agenta.sdk.utils.assets, so the Step 1 and Step 4 commands fail in a fresh clone unless the SDK path is passed with uvx --with . or documented/installed via PYTHONPATH.

web/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsx-124-140 (1)

124-140: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Clickable tiles aren't keyboard-reachable.

Chip and the image tile both use role="button" + onClick on a div with no tabIndex or key handler, so opening an attachment in the viewer is mouse-only. Add tabIndex={0} and an Enter/Space handler when onClick/onView is present (or render a real <button>).

As per coding guidelines, "Implement light and dark appearance and interaction states for every added or changed UI element" and accessibility blockers that prevent task completion must be fixed.

♿ Proposed fix for `Chip`
     <div
         role={onClick ? "button" : undefined}
+        tabIndex={onClick ? 0 : undefined}
         onClick={onClick}
+        onKeyDown={
+            onClick
+                ? (e) => {
+                      if (e.key === "Enter" || e.key === " ") {
+                          e.preventDefault()
+                          onClick()
+                      }
+                  }
+                : undefined
+        }
         className={...}
     >

Also applies to: 339-346

Source: Coding guidelines

web/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsx-185-196 (1)

185-196: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Object-URL lifecycle keyed on array identity revokes URLs that are still in use. Both effects depend on the whole upload array, so any new array reference — including an upload status/percent tick from the (planned) upload flow — revokes and re-creates every URL. In the composer this swaps AudioPlayer's src and restarts playback mid-clip; in the drawer it revokes the blob the viewer is currently rendering. Key the effect on the file identities instead.

  • web/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsx#L185-L196: derive a stable key from the uids (or cache URLs per originFileObj and revoke only removed entries) instead of depending on files.
  • web/oss/src/components/AgentChatSlice/components/AttachmentViewerDrawer.tsx#L97-L105: rebuild nodes on the uid set rather than the uploads reference.
hosting/railway/oss/scripts/configure.sh-79-82 (1)

79-82: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Silently accepts an empty RSA key if openssl genpkey fails.

Line 82 discards stderr inside a ${VAR:-$(...)} substitution, so a genpkey failure (missing/old openssl, no entropy) leaves AGENTA_STORE_JWT_PRIVATE_KEY empty. That empty value is then pushed to the api service at line 370 and the deploy looks successful, but store web-identity signing fails at runtime with no hint from this script. Worth failing fast here.

🛡️ Proposed fix to validate the generated key
     AGENTA_STORE_ACCESS_KEY="${AGENTA_STORE_ACCESS_KEY:-$(openssl rand -hex 10)}"
     AGENTA_STORE_SECRET_KEY="${AGENTA_STORE_SECRET_KEY:-$(openssl rand -hex 32)}"
     AGENTA_STORE_SIGNING_KEY="${AGENTA_STORE_SIGNING_KEY:-$(openssl rand -base64 32)}"
-    AGENTA_STORE_JWT_PRIVATE_KEY="${AGENTA_STORE_JWT_PRIVATE_KEY:-$(openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 2>/dev/null)}"
+    if [ -z "$AGENTA_STORE_JWT_PRIVATE_KEY" ]; then
+        AGENTA_STORE_JWT_PRIVATE_KEY="$(openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048)"
+    fi
+    if [ -z "$AGENTA_STORE_ACCESS_KEY" ] || [ -z "$AGENTA_STORE_SECRET_KEY" ] ||
+        [ -z "$AGENTA_STORE_SIGNING_KEY" ] || [ -z "$AGENTA_STORE_JWT_PRIVATE_KEY" ]; then
+        printf "Failed to resolve or generate the mount store credentials.\n" >&2
+        return 1
+    fi
web/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.ts-113-129 (1)

113-129: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard finish against meta.settled before calling settle().

finish is the shared terminal-settle path for success, poll-abandon, timeout, and error failures. cancel/decline already check !settledRef.current && !meta.settled, but finish only checks its own settledRef, so two mounted instances for the same parked call can race: another instance’s settle can flip meta.settled before this instance’s async terminal callback returns, then this callback still calls settle() again.

🔒️ Proposed fix
     const finish = useCallback(
         (result: ConnectOutput | {errorText: string}) => {
-            if (settledRef.current) return
+            if (settledRef.current || meta.settled) return
             settledRef.current = true
             teardown()
             setPhase("idle")
             if ("errorText" in result) {
                 setOutcome({connected: false})
                 settle({errorText: result.errorText})
             } else {
                 setOutcome({connected: result.connected === true})
                 settle({output: result as Record<string, unknown>})
             }
         },
-        [settle, teardown],
+        [settle, teardown, meta.settled],
     )
web/oss/src/components/AgentChatSlice/hooks/useAttachmentUploads.ts-27-76 (1)

27-76: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

enqueue silently no-ops for files added in the same tick, once a transport is wired.

filesRef.current (Line 33-34) only reflects files as of the hook's last render. The documented/expected caller pattern (see AgentConversation.tsx lines ~1738-1745) calls setFiles(...) and then uploads.enqueue(...) synchronously in the same event handler — filesRef.current hasn't caught up yet, so run (Line 47-50) can't find the newly-added originFileObj and returns early (Line 50) without starting the upload. This is currently masked by if (!upload) return (Line 46), but will silently break the very first attachment upload once a real upload transport is wired — the exact scenario the docstring (Line 10-13) promises "just works."

Consider having enqueue accept the file payload directly instead of re-deriving it from a possibly-stale ref, e.g.:

🐛 Proposed fix direction
 export interface AttachmentUploads {
-    enqueue: (uids: string[]) => void
+    enqueue: (files: {uid: string; file: File}[]) => void
     retry: (uid: string) => void
 }
@@
     const run = useCallback(
-        (uid: string) => {
+        (uid: string, fileArg?: File) => {
             if (!upload) return
-            const file = filesRef.current.find((f) => f.uid === uid)?.originFileObj as
-                | File
-                | undefined
+            const file =
+                fileArg ??
+                (filesRef.current.find((f) => f.uid === uid)?.originFileObj as File | undefined)
             if (!file) return
             ...
         },
         [upload, patch],
     )
-    const enqueue = useCallback((uids: string[]) => uids.forEach(run), [run])
+    const enqueue = useCallback(
+        (files: {uid: string; file: File}[]) => files.forEach(({uid, file}) => run(uid, file)),
+        [run],
+    )

The caller in AgentConversation.tsx would then pass accepted.map((f) => ({uid: ..., file: f})) instead of just uids.

web/packages/agenta-ui/src/RichChatInput/ComposerSendButton.tsx-37-37 (1)

37-37: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Replace raw hex colors with theme tokens.

Line 37 hard-codes #191a0d and #b8cb3f, which can render incorrectly across themes and violates the repository’s theme-color rule.

Suggested direction
- : "!border-[var(--ag-surface-accent)] !bg-[var(--ag-surface-accent)] !text-[`#191a0d`] hover:!border-[`#b8cb3f`] hover:!bg-[`#b8cb3f`]"
+ : "use semantic Ant/Tailwind theme tokens for text, border, background, and hover states"

As per coding guidelines, consume theme colors through semantic tokens, Tailwind utilities, or supported var(--ag-color*) variables; raw hex colors are not allowed.

Source: Coding guidelines

web/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx-297-297 (1)

297-297: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Disable submission while dictation is active.

Line 297 locks the editor, but the SendButton remains enabled unless disabled is set. A user can submit during an active transcript, sending incomplete text while the dictation session continues updating the editor.

Suggested fix
<SendButton
    onSubmit={onSubmit}
    forceEnabled={sendForceEnabled}
-   disabled={disabled}
+   disabled={disabled || dictating}
    streaming={streaming}
    onStop={onStop}
/>
web/oss/src/components/Drives/driveMedia.ts-167-191 (1)

167-191: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Move this upload behind the Fern resource accessor.

This direct Axios call violates the frontend API rule requiring per-resource accessors from @agenta/sdk/resources. Add or extend the mount-file accessor in web/packages/agenta-sdk/src/resources.ts, including any needed progress-capable transport support, rather than exposing a raw API call here.

Source: Coding guidelines

web/oss/src/components/Drives/useMountUpload.ts-124-126 (1)

124-126: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Non-unique item id can collide across rapid upload batches.

`${Date.now()}-${i}-${file.name}` can collide when upload() is invoked twice in the same millisecond (e.g., two drop targets firing synchronously) with overlapping index i and identical file name. A collision overwrites the earlier item's entry in sources.current/controllers.current, causing retry/dismiss/abort to target the wrong file and duplicate React keys in items.

🐛 Proposed fix using a guaranteed-unique id
-                const id = `${Date.now()}-${i}-${file.name}`
+                const id = crypto.randomUUID()
🟡 Minor comments (14)
docs/design/simplify-nav-new-users/plan.md-168-175 (1)

168-175: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Resolve the OSS/EE scope contradiction.

Phase 2 requires availability in both OSS and EE, but the file list and Line 198 explicitly prohibit EE changes. Either add the EE registration/files or revise the requirement to OSS-only.

Also applies to: 191-198

docs/design/simplify-nav-new-users/status.md-7-10 (1)

7-10: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Refresh this status note after enabling the OSS Vitest runner.

The file says both phases are implemented, then calls the work “Ready to build” and claims there is no CI-wired OSS Vitest runner. The PR objectives state that web/oss now has Vitest/test:unit scripts and CI JUnit collection. Update Lines 53-65 to say implementation is complete with manual QA pending, and remove or rewrite the stale Slice 0 follow-up.

Also applies to: 53-65

docs/design/simplify-nav-new-users/context.md-89-90 (1)

89-90: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Format the Phase 2 criteria as a real list.

**Phase 2** 6. keeps the heading and all criteria in one paragraph, so Markdown will not render the numbered list correctly. Also use the compound adjective full-page.

Proposed fix
-**Phase 2** 6. The Settings → Feature flags switch flips the effective mode and survives a
-reload. 7. Toggling the switch updates the sidebar without a full page reload. 8. Phase 1's
-sidebar behavior is unchanged when no override is set.
+**Phase 2**
+
+6. The Settings → Feature flags switch flips the effective mode and survives a reload.
+7. Toggling the switch updates the sidebar without a full-page reload.
+8. Phase 1's sidebar behavior is unchanged when no override is set.

Source: Linters/SAST tools

.agents/skills/create-changelog-announcement/SKILL.md-275-285 (1)

275-285: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the sidebar example valid JSON.

The // ... existing entries line makes this block invalid JSON, despite the workflow requiring valid JSON. Put the ellipsis outside the fenced block or label the example as JSONC.

.agents/skills/update-api-docs/SKILL.md-68-68 (1)

68-68: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the verification step number.

This is the third documented step, not “Step 5,” which makes the workflow appear incomplete.

.agents/skills/update-api-docs/SKILL.md-83-86 (1)

83-86: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language tags to prose example fences.

  • .agents/skills/update-api-docs/SKILL.md#L83-L86: mark the commit-message block as text.
  • .agents/skills/write-issue/SKILL.md#L67-L70: mark the checklist example as text.
  • .agents/skills/write-social-announcement/SKILL.md#L163-L218: mark each social-post example as text.

Source: Linters/SAST tools

web/oss/src/components/Drives/useDriveDrop.ts-81-87 (1)

81-87: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear the spring-load timer during effect cleanup.

Unmounting or disabling the hook while a folder timer is pending still calls onNavigate() after 700 ms. Call clearSpring() in this cleanup before removing listeners.

web/oss/src/components/Drives/useDriveDrop.ts-103-152 (1)

103-152: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make disabled drop handlers inert.

enabled={false} removes window listeners, but these handlers still highlight folders, suppress browser defaults, spring-navigate, and call the no-op upload callback. Return early from each handler when disabled.

web/oss/src/components/AgentChatSlice/assets/attachments.ts-83-91 (1)

83-91: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Two-item list renders as "Images, and audio".

nouns.slice(0, -1).join(", ") leaves an empty separator for a single leading item, so the Oxford comma is emitted with only two kinds — reachable as soon as kinds is narrowed by capability gating (the documented seam).

🐛 Proposed fix
     const sentence =
         nouns.length === 1
             ? nouns[0]
-            : `${nouns.slice(0, -1).join(", ")}, and ${nouns[nouns.length - 1]}`
+            : nouns.length === 2
+              ? `${nouns[0]} and ${nouns[1]}`
+              : `${nouns.slice(0, -1).join(", ")}, and ${nouns[nouns.length - 1]}`
     return sentence.charAt(0).toUpperCase() + sentence.slice(1)
web/oss/src/components/Drives/DriveExplorer.tsx-1834-1852 (1)

1834-1852: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

A drop into an unresolvable folder is silently discarded.

When drive.resolveMount(folder) returns nothing, uploadIntoFolder returns without starting an upload, staging the file, or telling the user — the tile just never appears and the picked/dropped files are lost. message from App.useApp() is already in scope (line 1545); surface a failure there so the intent isn't dropped on the floor.

🛡️ Proposed fix
     const uploadIntoFolder = useCallback(
         (picked: File[], folder: string) => {
             const resolved = drive.resolveMount(folder)
-            if (!resolved) return
+            if (!resolved) {
+                message.error("Couldn't resolve a destination for these files.")
+                return
+            }
             mountUpload.upload(picked, {

and add message to the dependency array.

web/oss/src/components/AgentChatSlice/components/MicPermissionNotice.tsx-23-25 (1)

23-25: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not update refs during render.

MicPermissionNotice writes messageRef.current inside the render function and then reads it at line 32, which React discourages/forbids because pending renders can overwrite the ref without committing. Keep the fallback text in state and render message ?? latchedMessage instead.

Proposed fix
-import {useRef} from "react"
+import {useEffect, useState} from "react"
...
-    const messageRef = useRef("")
-    if (message) messageRef.current = message
+    const [latchedMessage, setLatchedMessage] = useState(message ?? "")
+
+    useEffect(() => {
+        if (message) setLatchedMessage(message)
+    }, [message])
...
-                    <span className="truncate">{messageRef.current}</span>
+                    <span className="truncate">{message ?? latchedMessage}</span>
web/oss/src/components/pages/settings/Organization/General.tsx-232-233 (1)

232-233: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Hardcoded gray palette utilities won't adapt to dark mode.

text-gray-400, bg-gray-100, and border-gray-100 are fixed light-theme values; use Ant Design semantic tokens or var(--ag-color*)-backed utilities so the option rows render correctly in both themes.

As per coding guidelines: "Consume theme colors through Ant Design semantic tokens, Tailwind color utilities, or supported var(--ag-color*) variables" and "Implement light and dark appearance and interaction states for every added or changed UI element, and verify both themes."

Also applies to: 247-247

Source: Coding guidelines

web/oss/src/components/AgentChatSlice/hooks/useAudioRecorder.ts-197-215 (1)

197-215: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Stale micPermissionState() callback can clobber a newer session's state.

The fire-and-forget micPermissionState().then(...) (Line 212-214) isn't guarded against a newer start()/dismissError() happening in between. If the user retries quickly after a denial, this stale callback can still fire setError(DISMISSED_MESSAGE) after recording has already started (or after the error was dismissed), showing a stale mic-error message over an unrelated/successful state.

🐛 Proposed fix
                 setStatus("denied")
                 setError(BLOCKED_MESSAGE)
-                micPermissionState().then((state) => {
-                    if (state === "prompt") setError(DISMISSED_MESSAGE)
-                })
+                micPermissionState().then((state) => {
+                    // Only apply if nothing superseded this attempt in the meantime.
+                    if (state === "prompt" && recRef.current === null && !cancelledRef.current) {
+                        setError((prev) => (prev === BLOCKED_MESSAGE ? DISMISSED_MESSAGE : prev))
+                    }
+                })
web/oss/src/components/Drives/driveFileSource.tsx-72-85 (1)

72-85: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Key text state to the current local file.

Switching local files can render the previous file’s text with isPending: false until the new read resolves. Store the source File alongside the text, and return undefined/pending when it differs.

Proposed fix
-    const [text, setText] = useState<string | undefined>(undefined)
+    const [text, setText] = useState<{file: File | null; value: string | undefined}>({
+        file: null,
+        value: undefined,
+    })
     useEffect(() => {
         if (!local) return
         let cancelled = false
         local.file
             .text()
-            .then((t) => !cancelled && setText(t))
-            .catch(() => !cancelled && setText(""))
+            .then((value) => !cancelled && setText({file: local.file, value}))
+            .catch(() => !cancelled && setText({file: local.file, value: ""}))
@@
-    if (local) return {data: text, isPending: text === undefined}
+    if (local) {
+        const data = text.file === local.file ? text.value : undefined
+        return {data, isPending: data === undefined}
+    }
🧹 Nitpick comments (16)
web/oss/src/components/TestsetsTable/TestsetsTable.tsx (1)

367-368: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Do not bypass the table-store contract with as never.

The cast only silences the generic mismatch at the @agenta/ui/table boundary. Use a correctly typed shared store or an explicit adapter so incompatible pagination/row contracts remain compile-time errors.

web/oss/src/components/Drives/useDriveDrop.ts (1)

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

Shorten the new behavioral comments.

These multi-line comments describe normal hook behavior rather than a surprising constraint. Keep them to short one-line summaries.

As per coding guidelines, “Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements.”

Also applies to: 163-169

Source: Coding guidelines

web/oss/src/components/AgentChatSlice/AgentConversation.tsx (2)

1738-1749: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the upload uid from toUploadFile instead of re-implementing it.

addFiles recomputes ${f.name}-${f.lastModified}-${f.size} independently of toUploadFile (line 1723); any change to the uid scheme silently desyncs the enqueue keys from the tray entries. Two identical drops also collide on the same uid, so removeFile would drop both rows.

♻️ Reuse the mapped entries
         if (accepted.length) {
-            setFiles((prev) => [...prev, ...accepted.map(toUploadFile)])
-            uploads.enqueue(accepted.map((f) => `${f.name}-${f.lastModified}-${f.size}`))
+            const mapped = accepted.map(toUploadFile)
+            setFiles((prev) => [...prev, ...mapped])
+            uploads.enqueue(mapped.map((f) => f.uid))
         }

1855-1864: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

!attachmentsSettled returns silently — the send is swallowed with no feedback.

Inert today (no uploader is wired, so nothing ever reaches uploading/error), but once the transport lands, Enter with text plus an in-flight upload just no-ops while the composer clears. Surface the reason (or block the send affordance) rather than dropping it.

api/oss/src/middlewares/prefix.py (1)

49-52: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Every request now pays an extra full route-table scan.

_known(scope, path) runs unconditionally on each HTTP request, and for the common case (path already known) that's one complete linear pass over self._routes() with regex matching per route, on top of Starlette's own routing pass. With a large route table this is a per-request hot-path cost.

A cheap mitigation is a small memo keyed by (method, path) of the "known" verdict, or skipping the probe when the path already ends in the shape the vast majority of routes declare.

web/oss/src/components/AgentChatSlice/components/AttachmentViewerDrawer.tsx (1)

97-105: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Rebuilding on uploads identity revokes live object URLs mid-view.

Any change to the uploads array while the drawer is open (a new reference from the parent, or an upload status/percent update) tears down and re-creates every object URL, so the currently previewed blob URL is revoked underneath the viewer. Keying the rebuild on the uid set (or the originFileObj identities) instead avoids the churn.

♻️ Sketch
-    useEffect(() => {
+    const uploadsKey = uploads.map((u) => u.uid).join("|")
+    useEffect(() => {
         if (!open) {
             setNodes(null)
             return
         }
         const built = buildNodes(uploads)
         setNodes(built)
         return () => built.source.forEach((v) => URL.revokeObjectURL(v.objectUrl))
-    }, [open, uploads])
+        // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [open, uploadsKey])
web/oss/src/components/AgentChatSlice/components/AudioPlayer.tsx (1)

99-116: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

No scrub affordance and the progress bar is invisible to assistive tech.

The native element is hidden, so play/pause is the only interaction: keyboard and screen-reader users can't seek, and the bar carries no role="progressbar"/aria-valuenow. Adding the ARIA attributes (and optionally a click/keyboard seek on the bar) closes that gap.

web/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsx (1)

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

Raw rgba(...) literals instead of theme tokens.

The overlay scrim, progress track and error background use hardcoded color literals (rgba(0,0,0,0.6), rgba(0,0,0,0.4), rgba(255,255,255,0.3)) plus text-white, which won't adapt across light/dark. Prefer Ant Design semantic tokens or supported var(--ag-color*) variables.

As per coding guidelines, "Consume theme colors through Ant Design semantic tokens, Tailwind color utilities, or supported var(--ag-color*) variables; do not use raw hex colors or --ag-c-* literals."

Also applies to: 86-88, 102-102

Source: Coding guidelines

web/oss/src/components/Drives/DriveExplorer.tsx (1)

1727-1733: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

as unknown as MountFile fabricates a partial entity.

The synthetic upload nodes only carry path/size, and the double cast hides that from every downstream consumer of the built tree. It works today because both render paths short-circuit these nodes via pendingUploadByPath/pending before touching other fields, but a future consumer reading touchedAt (or any other required field) will get undefined with no compile error. Consider a narrow local type instead of casting through unknown, e.g. Pick<MountFile, "path" | "size"> on the tree-builder input.

web/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.ts (2)

36-41: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Leftover reviewer note in shipped code.

"NOTE for Mahmoud: confirm the bound" reads as an unresolved open question left in the comment rather than a settled design decision.


70-304: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy lift

No unit tests for this hook.

useConnectFlow implements security-sensitive origin validation, popup lifecycle management, and multi-instance settle-race protection — exactly the kind of pure-ish, high-risk logic worth direct unit testing (mocking window.open/postMessage/timers), especially since this review found gaps in the settle guarantees the docstring promises.

web/oss/src/components/AgentChatSlice/components/InteractionDock.tsx (1)

1-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Trim the introductory implementation comment.

Move the UX narrative to PR/docs; retain only a short constraint-oriented comment here. As per coding guidelines, “Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements.”

Source: Coding guidelines

web/oss/src/lib/helpers/dynamicEnv.ts (1)

40-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Shorten the template-strip comment.

This documents ordinary feature behavior in a multi-line implementation comment; keep it to one short line and move rollout details to flag documentation. As per coding guidelines, keep in-code comments to at most one short line except for genuinely surprising constraints.

Source: Coding guidelines

web/oss/src/components/AgentChatSlice/components/RecordingBar.tsx (1)

66-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

aria-live="polite" on the container will announce the timer every second.

The clock text changes ~1x/s inside the live region, so screen readers get a continuous stream of announcements for the whole take. Consider keeping the live region for state changes only and marking the running timer as aria-hidden (or moving aria-live onto the near-limit warning).

♿ Proposed tweak
             <span
+                aria-hidden
                 className={`text-sm tabular-nums transition-colors duration-300 ${
web/oss/src/components/pages/observability/components/NodeNameCell.tsx (1)

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

Same spanTypeStyles.unknown assumption as AvatarTreeContent.tsx.

The duplicated spanTypeStyles[type ?? "unknown"] ?? spanTypeStyles.unknown lookup would be better as a single shared resolver in ../assets/constants so the fallback contract lives in one place.

web/oss/src/components/AgentChatSlice/components/VoiceInputButton.tsx (1)

138-152: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Memoize menuItems.

menuItems rebuilds a JSX-bearing array on every render and is handed straight to Dropdown. In a composer that re-renders frequently during streaming/dictation, wrap it in useMemo.

♻️ Suggested fix
-    const menuItems: MenuProps["items"] = available.map((m) => ({
-        key: m.key,
-        icon: modeIcon(m.key),
-        label:
-            m.key === "audio" && audioNotHeard ? (
-                <span className="flex flex-col">
-                    <span>{MODE_LABEL[m.key]}</span>
-                    <span className="text-[11px] text-colorTextTertiary">
-                        This model can't listen to audio
-                    </span>
-                </span>
-            ) : (
-                MODE_LABEL[m.key]
-            ),
-    }))
+    const menuItems: MenuProps["items"] = useMemo(
+        () =>
+            available.map((m) => ({
+                key: m.key,
+                icon: modeIcon(m.key),
+                label:
+                    m.key === "audio" && audioNotHeard ? (
+                        <span className="flex flex-col">
+                            <span>{MODE_LABEL[m.key]}</span>
+                            <span className="text-[11px] text-colorTextTertiary">
+                                This model can't listen to audio
+                            </span>
+                        </span>
+                    ) : (
+                        MODE_LABEL[m.key]
+                    ),
+            })),
+        [available, audioNotHeard],
+    )

Source: Coding guidelines

Comment on lines +180 to +189
const popup = window.open(
redirectUrl,
oauthWindowName,
"width=600,height=700,popup=yes",
)
if (!popup) {
setPhase("error")
setErrorText("Couldn’t open the connection window. Allow popups and retry.")
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the target file and inspect the relevant hook implementation around terminal branches.
target="web/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.ts"
if [ ! -f "$target" ]; then
  echo "target file not found"
  fd -a 'useConnectFlow\.ts$' .
  exit 1
fi

wc -l "$target"
sed -n '1,320p' "$target" | nl -ba | sed -n '1,320p'

# Identify all calls to finish() and terminal signal locations.
python3 - <<'PY'
from pathlib import Path
p=Path("web/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.ts")
text=p.read_text()
for i, line in enumerate(text.splitlines(), 1):
    if 'finish(' in line or 'settleParkedCall' in line or 'function finish' in line or 'const finish' in line or 'runConnect' in line:
        print(f"{i}: {line}")
PY

Repository: Agenta-AI/agenta

Length of output: 278


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target="web/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.ts"

if [ ! -f "$target" ]; then
  echo "target file not found"
  fd 'useConnectFlow\.ts$' .
  exit 1
fi

echo "## file line count"
wc -l "$target"

echo "## first 320 lines with line numbers"
awk '{print NR": "$0}' "$target" | sed -n '1,320p'

echo "## relevant identifiers"
python3 - <<'PY'
from pathlib import Path
p=Path("web/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.ts")
for i, line in enumerate(p.read_text().splitlines(), 1):
    if any(token in line for token in [
        'finish(', 'settleParkedCall', 'function finish', 'const finish', 'runConnect',
        'window.open', 'popup_blocked', 'connected:', 'reactive: "connected"',
    ]):
        print(f"{i}: {line}")
PY

Repository: Agenta-AI/agenta

Length of output: 17801


Settle the popup-blocked path before returning.

When window.open is blocked, this is a terminal OAuth failure, but the branch only sets phase="error" and returns. For the live parked connection flow (settleParkedCall=true), finish() is never called, so the client tool never settles and the agent run can hang.

🐛 Proposed fix
                 if (!popup) {
                     setPhase("error")
                     setErrorText("Couldn’t open the connection window. Allow popups and retry.")
+                    if (settleParkedCall) {
+                        finish({
+                            connected: false,
+                            integration,
+                            slug,
+                            reason: "popup_blocked",
+                        })
+                    }
                     return
                 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const popup = window.open(
redirectUrl,
oauthWindowName,
"width=600,height=700,popup=yes",
)
if (!popup) {
setPhase("error")
setErrorText("Couldn’t open the connection window. Allow popups and retry.")
return
}
const popup = window.open(
redirectUrl,
oauthWindowName,
"width=600,height=700,popup=yes",
)
if (!popup) {
setPhase("error")
setErrorText("Couldn’t open the connection window. Allow popups and retry.")
if (settleParkedCall) {
finish({
connected: false,
integration,
slug,
reason: "popup_blocked",
})
}
return
}

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-production-eac2.up.railway.app/w
Image tag pr-5567-e26ed6f
Status Failed
Railway logs Open logs
Logs View workflow run
Updated at 2026-07-30T16:29:05.419Z

@ardaerzin
ardaerzin changed the base branch from main to release/v0.106.2 July 30, 2026 15:59
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels Jul 30, 2026
…thub-issue-5528-3377d2

# Conflicts:
#	web/oss/package.json
#	web/pnpm-lock.yaml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size:M This PR changes 30-99 lines, ignoring generated files. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(bug) Unit tests under web/oss never run, in CI or locally

1 participant