Skip to content

feat(wework): fix test - #1377

Merged
qdaxb merged 4 commits into
wecode-ai:mainfrom
Micro66:human/lion-20260612-021746
Jun 12, 2026
Merged

qdaxb merged 4 commits into
wecode-ai:mainfrom
Micro66:human/lion-20260612-021746

Conversation

@Micro66

@Micro66 Micro66 commented Jun 12, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Commit-message validation to enforce conventional commits.
    • Test watch script and coverage reporting for the embedded frontend.
  • Chores

    • CI expanded with dedicated lint and test jobs for the embedded frontend.
    • Husky hooks, lint-staged and pre-push gates added for local checks.
    • Dev tooling added for formatting, linting and coverage.
  • Style

    • UI stacking, layout and control-panel tweaks for more consistent overlays and sidebar behavior.
  • Tests

    • Improved deterministic timing in hover/action tests.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 86440699-0864-494a-9668-d58ea05e83a4

📥 Commits

Reviewing files that changed from the base of the PR and between 254f8b8 and f387287.

📒 Files selected for processing (1)
  • wework/src/features/workbench/WorkbenchProvider.test.tsx

📝 Walkthrough

Walkthrough

Adds CI jobs to lint and test the wework package, configures Husky hooks and lint-staged for wework, updates package tooling, converts hardcoded z-index utilities to semantic tokens, refactors ProjectWorkBar device/submenu UI, and adjusts related tests and small layout changes.

Changes

wework Module Tooling, Hooks, and Component Updates

Layer / File(s) Summary
CI/CD workflow integration for wework
.github/workflows/lint.yml, .github/workflows/test.yml, wework/vite.config.ts
Adds lint-wework and test-wework jobs that install ./wework dependencies, run ESLint/tsc and tests with coverage, upload Codecov artifacts, wires summary jobs to depend on them, and enables Istanbul coverage output in Vite test config.
Husky Git hooks configuration
wework/.husky/_/*, wework/.husky/commit-msg, wework/.husky/pre-commit, wework/.husky/pre-push
Adds shared helper h, replaces/marks deprecated the old shim, adds hook entrypoints that source h, enforces conventional commits in commit-msg, runs lint-staged for staged wework files in pre-commit, and conditionally runs an AI push gate in pre-push.
Package.json dependencies and npm scripts
wework/package.json
Adds test:watch and prepare scripts, expands devDependencies with @vitest/coverage-istanbul, husky, lint-staged, prettier, and configures lint-staged for staged formatting and linting.
ProjectWorkBar submenu and standalone device refactoring
wework/src/components/chat/composer/ProjectWorkBar.tsx
Removes standalone submenu sizing/state, narrows submenu state to create only, rewrites placement logic for the create submenu, replaces hover submenu with an inline standalone device list including a "no project" option, and updates handlers/types.
UI z-index/token updates, ModelSelector changes, and tests
wework/src/components/..., wework/src/components/chat/composer/ModelSelector.tsx, wework/src/components/chat/MessageList.test.tsx, wework/src/components/chat/ChatInput.test.tsx, wework/src/components/layout/*
Replaces hardcoded z-index utilities with semantic classes (z-chrome, z-popover, z-modal), inlines control sections in ModelSelector desktop panel (removing compact trigger submenu path), stabilizes hover-time tests with fake timers, updates test ids/expectations, and memoizes timestamp in DeviceStatusPrompt.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • wecode-ai/Wegent#1177: The main PR makes UI/test adjustments in the project chat composer components (e.g., ProjectWorkBar, ModelSelector, ChatInput*), which directly builds on and overlaps the retrieved PR’s implementation of the project chat composer feature.
  • wecode-ai/Wegent#1328: Both PRs modify wework/src/components/chat/MessageList.test.tsx around the hover-action/hover time assertions, so they’re directly related at the test coverage level.
  • wecode-ai/Wegent#1323: Both PRs touch the mobile UI layer by modifying wework/src/components/layout/MobileDrawer.tsx, so the changes are code-level related in the same component.

Suggested reviewers

  • qdaxb

Poem

🐰 I tuned the hooks and warmed the CI,
z-index tokens now stack just right,
standalone lists hop into sight,
tests and lint march in tidy line,
a happy rabbit cheers: "All green light!"

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'feat(wework): fix test' is vague and does not accurately represent the broad scope of changes, which include Husky setup, lint/test workflow additions, UI z-index updates, and component refactoring. Use a more descriptive title that reflects the primary changes, such as 'feat(wework): add Husky hooks and CI/CD workflows' or 'feat(wework): setup linting, testing, and git hooks'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 7

🧹 Nitpick comments (1)
.github/workflows/lint.yml (1)

127-144: ⚡ Quick win

Align this job’s Node version with the supported wework toolchain.

The wework toolchain in this PR is on Vite 8 / Vitest 4, and their docs require Node 20.19+ / 22.12+ for Vite 8 and Node 20+ for Vitest 4. Even if the current lint commands happen to pass on 18.x, this job is now validating the module under an unsupported runtime, which can drift from local and test behavior. (vite.dev)

🤖 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 @.github/workflows/lint.yml around lines 127 - 144, Update the Node runtime
used by the workflow's "Set up Node.js" step (uses: actions/setup-node@v4) to a
supported version for Vite 8/Vitest 4 by changing the node-version field from
'18.x' to a Node 20+ release (e.g., '20.x' or '22.x'); leave the cache and other
step names ("Install dependencies", "Run ESLint", "Run TypeScript check") intact
so the job runs under the correct toolchain-compatible Node version.
🤖 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 @.github/workflows/lint.yml:
- Around line 123-132: The new wework lint job uses floating action tags and
leaves persisted checkout credentials and broad default permissions; update the
"Checkout code" step (actions/checkout@v4) to include persist-credentials: false
(and ideally fetch-depth: 0 if needed), pin the actions to a fixed commit SHA
(or use a trusted release digest) instead of a floating tag for both
actions/checkout and actions/setup-node, and add explicit minimal permissions
for the workflow or job (e.g., permissions: contents: read) to replace the
default token scope so the lint job conforms to the repo Actions hardening
policy.

In @.github/workflows/test.yml:
- Around line 302-323: The new wework test job uses floating action tags, keeps
persisted checkout credentials, and lacks tightened token permissions; update
the "Checkout code" step (uses: actions/checkout@...) to set
persist-credentials: false and fetch-depth: 0, replace floating tags for
actions/checkout, actions/setup-node and codecov/codecov-action with pinned
immutable refs (commit SHAs or exact fixed releases), and add a job-level
permissions block (or minimal permissions field) to restrict the default
GITHUB_TOKEN scope to only what's needed for the job; ensure "Set up Node.js",
"Install dependencies", and "Upload coverage to Codecov" steps keep their
working-directory but use the pinned action refs and the tightened permissions.
- Around line 306-319: The CI step "Set up Node.js" currently pins node-version:
'18.x' which is incompatible with wework's Vitest 4 / Vite 8 requirements;
update the node-version in that step (and any related cache settings like
cache-dependency-path if needed) to a supported runtime such as '20.x' (or a
minimum of 20.19 / 22.12+) so the "Install dependencies" and "Run wework tests"
steps execute on a supported Node runtime; ensure the node-version change
applies to the step named "Set up Node.js" that precedes the "Install
dependencies" and "Run wework tests" steps.
- Around line 321-326: Replace all occurrences of the action reference
codecov/codecov-action@v3 in .github/workflows/test.yml (e.g., the "Upload
coverage to Codecov" step that contains uses: codecov/codecov-action@v3) with a
newer major (v4 or v5); update every job/step that references
codecov/codecov-action@v3 to codecov/codecov-action@v4 (or `@v5`) and verify the
action inputs (files, flags, name) still match the new action's expected
parameters per the release notes, adjusting parameter names if any changed.

In `@wework/.husky/commit-msg`:
- Line 14: The commit-msg regex rejects Conventional Commit breaking-change
markers; update the pattern string in the commit hook to allow an optional "!"
after the type or after the scope by changing the pattern to use an optional
"(!)?" after the (\(.+\))? group, e.g. replace the current pattern with:
^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?(!)?:
.{1,} so the hook (file: .husky/commit-msg) accepts headers like "feat!: ..."
and "feat(api)!: ...".

In `@wework/package.json`:
- Around line 67-70: The lint-staged config in wework/package.json runs "eslint
--fix" against "*.{js,jsx,ts,tsx,json,css,md}" which mismatches
wework/eslint.config.js (which targets only **/*.{ts,tsx}); update the
lint-staged entry so the eslint command only runs on TypeScript files (e.g.,
change the eslint target to "*.{ts,tsx}" or "**/*.{ts,tsx}") and keep Prettier
handling json/css/md; locate the lint-staged config in package.json and modify
the pattern used for the "eslint --fix" task (refer to the existing lint-staged
block and the "eslint --fix" invocation) so ESLint only receives files that
match the ESLint flat-config.

In `@wework/src/components/chat/composer/ProjectWorkBar.tsx`:
- Around line 382-384: The call to updateSideSubmenuPlacement in
handleActivateCreateSubmenu passes an unexpected argument ('create') causing a
TS arity error; fix by removing the argument and calling
updateSideSubmenuPlacement() with no parameters, leaving
setActiveSubmenu('create') to drive state (or alternatively change
updateSideSubmenuPlacement's signature to accept an optional placement parameter
if placement must be passed directly). Update handleActivateCreateSubmenu to
call updateSideSubmenuPlacement() and keep setActiveSubmenu('create') as-is (or
update the updateSideSubmenuPlacement callback signature and its callers to
accept a placement string consistently).

---

Nitpick comments:
In @.github/workflows/lint.yml:
- Around line 127-144: Update the Node runtime used by the workflow's "Set up
Node.js" step (uses: actions/setup-node@v4) to a supported version for Vite
8/Vitest 4 by changing the node-version field from '18.x' to a Node 20+ release
(e.g., '20.x' or '22.x'); leave the cache and other step names ("Install
dependencies", "Run ESLint", "Run TypeScript check") intact so the job runs
under the correct toolchain-compatible Node version.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1da5863d-e019-4aa0-b1ad-14b6945dd956

📥 Commits

Reviewing files that changed from the base of the PR and between e87df96 and d62f009.

⛔ Files ignored due to path filters (1)
  • wework/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (34)
  • .github/workflows/lint.yml
  • .github/workflows/test.yml
  • wework/.husky/_/applypatch-msg
  • wework/.husky/_/commit-msg
  • wework/.husky/_/h
  • wework/.husky/_/husky.sh
  • wework/.husky/_/post-applypatch
  • wework/.husky/_/post-checkout
  • wework/.husky/_/post-commit
  • wework/.husky/_/post-merge
  • wework/.husky/_/post-rewrite
  • wework/.husky/_/pre-applypatch
  • wework/.husky/_/pre-auto-gc
  • wework/.husky/_/pre-commit
  • wework/.husky/_/pre-merge-commit
  • wework/.husky/_/pre-push
  • wework/.husky/_/pre-rebase
  • wework/.husky/_/prepare-commit-msg
  • wework/.husky/commit-msg
  • wework/.husky/pre-commit
  • wework/.husky/pre-push
  • wework/package.json
  • wework/src/components/chat/ChatInput.test.tsx
  • wework/src/components/chat/composer/CompactChatComposer.tsx
  • wework/src/components/chat/composer/ComposerTextarea.tsx
  • wework/src/components/chat/composer/ModelSelector.tsx
  • wework/src/components/chat/composer/ProjectWorkBar.tsx
  • wework/src/components/layout/DesktopSidebar.tsx
  • wework/src/components/layout/DesktopWorkbenchMain.tsx
  • wework/src/components/layout/MobileDrawer.tsx
  • wework/src/components/layout/MobileWorkbenchLayout.test.tsx
  • wework/src/features/workbench/WorkbenchProvider.test.tsx
  • wework/src/test/theme-token-guard.test.ts
  • wework/vite.config.ts
💤 Files with no reviewable changes (1)
  • wework/src/test/theme-token-guard.test.ts

Comment on lines +123 to +132
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
cache: 'npm'
cache-dependency-path: ./wework/package-lock.json

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden the new wework lint job before merging.

This job carries the exact GitHub Actions issues already flagged by zizmor on the changed lines: floating action tags, persisted checkout credentials, and default token permissions. That leaves the new CI path outside the repository’s Actions hardening policy.

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 124-125: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 125-125: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 128-128: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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 @.github/workflows/lint.yml around lines 123 - 132, The new wework lint job
uses floating action tags and leaves persisted checkout credentials and broad
default permissions; update the "Checkout code" step (actions/checkout@v4) to
include persist-credentials: false (and ideally fetch-depth: 0 if needed), pin
the actions to a fixed commit SHA (or use a trusted release digest) instead of a
floating tag for both actions/checkout and actions/setup-node, and add explicit
minimal permissions for the workflow or job (e.g., permissions: contents: read)
to replace the default token scope so the lint job conforms to the repo Actions
hardening policy.

Source: Linters/SAST tools

Comment on lines +302 to +323
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
cache: 'npm'
cache-dependency-path: ./wework/package-lock.json

- name: Install dependencies
working-directory: ./wework
run: npm ci

- name: Run wework tests
working-directory: ./wework
run: npm test -- --coverage --passWithNoTests

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden the new wework test job before merging.

This new job repeats the same GitHub Actions hardening problems flagged by zizmor on the changed lines: floating action tags, persisted checkout credentials, and default token permissions. That is a supply-chain regression on a newly added CI path.

🧰 Tools
🪛 actionlint (1.7.12)

[error] 322-322: the runner of "codecov/codecov-action@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 zizmor (1.25.2)

[warning] 303-304: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 304-304: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 307-307: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 322-322: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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 @.github/workflows/test.yml around lines 302 - 323, The new wework test job
uses floating action tags, keeps persisted checkout credentials, and lacks
tightened token permissions; update the "Checkout code" step (uses:
actions/checkout@...) to set persist-credentials: false and fetch-depth: 0,
replace floating tags for actions/checkout, actions/setup-node and
codecov/codecov-action with pinned immutable refs (commit SHAs or exact fixed
releases), and add a job-level permissions block (or minimal permissions field)
to restrict the default GITHUB_TOKEN scope to only what's needed for the job;
ensure "Set up Node.js", "Install dependencies", and "Upload coverage to
Codecov" steps keep their working-directory but use the pinned action refs and
the tightened permissions.

Source: Linters/SAST tools

Comment thread .github/workflows/test.yml
Comment on lines +321 to +326
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: ./wework/coverage/coverage-final.json
flags: wework
name: wework-coverage

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the referenced lines in the workflow
sed -n '300,360p' .github/workflows/test.yml

# Find all usages of the codecov action (v3 and any other versions)
rg -n "codecov/codecov-action@v[0-9]+" .github/workflows/test.yml .github/workflows || true
rg -n "codecov-action@v" .github/workflows || true

Repository: wecode-ai/Wegent

Length of output: 3466


🌐 Web query:

codecov/codecov-action v3 deprecated actionlint too old for GitHub Actions runner node version

💡 Result:

The deprecation warnings you are seeing related to codecov/codecov-action@v3 and Node.js versions are due to GitHub's platform-wide transition from older Node.js runtimes to newer ones [1][2][3]. Here is the situation regarding the warning and compatibility: 1. Deprecation Status: GitHub Actions has been systematically deprecating older Node.js runtimes (e.g., Node.js 16) in favor of newer versions like Node.js 20 and beyond [1][3]. When you use codecov/codecov-action@v3, you may encounter warnings because the action's underlying runtime version is considered outdated by the current GitHub runner environment [1][2]. 2. Patch Version Conflict: A notable issue occurred in early 2024 when version 3.1.5 of the action was released, which attempted to update the action to Node.js 20 [4]. Because this was a breaking change for users on self-hosted runners or GitHub Enterprise Server instances that did not yet support Node.js 20, it caused failures [4]. This change was subsequently reverted in version 3.1.6 to maintain compatibility for those environments [4]. Consequently, v3 remains on an older runtime configuration to avoid breaking those specific setups [1][4]. 3. Recommendation: To resolve the deprecation warnings and ensure long-term stability, the recommended path is to migrate to a newer major version of the action, such as v4 or v5 [1][5]. These newer versions are built with modern runtimes and the updated Codecov CLI/Wrapper [5]. Regarding actionlint: If you are encountering issues where actionlint flags the action as old or incorrect, ensure you are using the latest version of actionlint [6]. The actionlint project periodically updates its database of "popular actions" to recognize newer versions and their specific input schemas [6][7]. If you recently upgraded your codecov-action version and actionlint is failing, it is likely because your local version of actionlint is outdated and does not yet recognize the inputs of the newer action version [6]. Update actionlint to the latest release to fix this [6]. In summary, do not expect v3 to be updated to support the latest Node.js runtime requirements, as it is in maintenance mode [1]. Migrate to the latest major version (v5) for full support and update actionlint to resolve any linting discrepancies [5][6].

Citations:


Replace codecov/codecov-action@v3 with a newer major (v4/v5) across this workflow.

This repo’s .github/workflows/test.yml uses codecov/codecov-action@v3 in multiple jobs (including the test-wework upload at lines 321-326). Actionlint and GitHub’s runner runtime deprecations make v3 a moving target; bumping avoids post-test upload breakage as older runtimes get dropped. https://github.com/codecov/codecov-action/releases

🧰 Tools
🪛 actionlint (1.7.12)

[error] 322-322: the runner of "codecov/codecov-action@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 zizmor (1.25.2)

[warning] 298-326: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)


[error] 322-322: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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 @.github/workflows/test.yml around lines 321 - 326, Replace all occurrences
of the action reference codecov/codecov-action@v3 in .github/workflows/test.yml
(e.g., the "Upload coverage to Codecov" step that contains uses:
codecov/codecov-action@v3) with a newer major (v4 or v5); update every job/step
that references codecov/codecov-action@v3 to codecov/codecov-action@v4 (or `@v5`)
and verify the action inputs (files, flags, name) still match the new action's
expected parameters per the release notes, adjusting parameter names if any
changed.

Source: Linters/SAST tools

Comment thread wework/.husky/commit-msg

# Commit message pattern: should start with a type (feat, fix, docs, style, refactor, test, chore)
# Example: "feat: add new feature" or "fix: resolve bug"
pattern="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,}"

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Allow Conventional Commit breaking-change marker (!) in the header pattern.

Line 14 currently rejects valid headers like feat!: ... and feat(api)!: ..., so legitimate commits can be blocked.

Suggested patch
-pattern="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,}"
+pattern="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\([^)]+\))?!?: .+"
📝 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
pattern="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,}"
pattern="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\([^)]+\))?!?: .+"
🤖 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 `@wework/.husky/commit-msg` at line 14, The commit-msg regex rejects
Conventional Commit breaking-change markers; update the pattern string in the
commit hook to allow an optional "!" after the type or after the scope by
changing the pattern to use an optional "(!)?" after the (\(.+\))? group, e.g.
replace the current pattern with:
^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?(!)?:
.{1,} so the hook (file: .husky/commit-msg) accepts headers like "feat!: ..."
and "feat(api)!: ...".

Comment thread wework/package.json
Comment on lines +67 to +70
"*.{js,jsx,ts,tsx,json,css,md}": [
"prettier --write",
"eslint --fix"
]

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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== lint-staged config =="
jq '.["lint-staged"]' wework/package.json

echo
echo "== ESLint config files =="
fd -HI 'eslint\.config\.(js|mjs|cjs|ts)|\.eslintrc(\.(js|cjs|json|yaml|yml))?$'

echo
echo "== Rules/plugins/processors hinting non-JS support (json/css/md) =="
rg -n -C2 'markdown|mdx|jsonc|json|css|processor|overrides|files' \
  eslint.config.js eslint.config.mjs eslint.config.cjs \
  .eslintrc .eslintrc.js .eslintrc.cjs .eslintrc.json .eslintrc.yaml .eslintrc.yml 2>/dev/null || true

Repository: wecode-ai/Wegent

Length of output: 320


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== wework/package.json scripts/devDependencies for lint-staged =="
jq '.scripts // {} | {prepare: .prepare, "test:watch": ."test:watch", "lint-staged": .["lint-staged"]} , .devDependencies // {} | with_entries(select(.key|test("eslint|prettier|lint-staged|markdown|json|css"; "i"))) ' wework/package.json 2>/dev/null || true

echo
echo "== wework/package.json lint-staged config =="
jq '.["lint-staged"]' wework/package.json

echo
echo "== ESLint config: wework/eslint.config.js (type support hints) =="
rg -n -C2 '(markdown|mdx|jsonc?|css|processor|overrides|files|languageOptions)' wework/eslint.config.js || true

echo
echo "== ESLint config: wework/eslint.config.js (plugins list) =="
rg -n 'plugins' wework/eslint.config.js || true

echo
echo "== Show top portion of wework/eslint.config.js =="
sed -n '1,200p' wework/eslint.config.js

Repository: wecode-ai/Wegent

Length of output: 1339


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== wework/package.json scripts/devDependencies for lint-staged =="
jq '.scripts // {} | {prepare: .prepare, "test:watch": ."test:watch"} , .devDependencies // {} | with_entries(select(.key|test("eslint|prettier|lint-staged|markdown|json|css"; "i")))' wework/package.json

echo
echo "== wework/package.json lint-staged config =="
jq '.["lint-staged"]' wework/package.json

echo
echo "== ESLint config: wework/eslint.config.js (type support hints) =="
rg -n -C2 '(markdown|mdx|jsonc?|css|processor|overrides|files|languageOptions)' wework/eslint.config.js || true

echo
echo "== ESLint config: wework/eslint.config.js (plugins list) =="
rg -n 'plugins' wework/eslint.config.js || true

echo
echo "== Show top portion of wework/eslint.config.js =="
sed -n '1,200p' wework/eslint.config.js

Repository: wecode-ai/Wegent

Length of output: 1316


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== wework/package.json eslint*/prettier deps =="
jq '.devDependencies // {} | with_entries(select(.key|test("^eslint|^`@eslint`|^prettier";"i"))) | sort_by(.key)' wework/package.json

echo
echo "== wework package-lock? (if exists) eslint plugins present (fast grep) =="
ls -1 wework/package-lock.json 2>/dev/null || true
rg -n --hidden --no-ignore-vcs '"eslint-plugin-' wework/package-lock.json 2>/dev/null || true

echo
echo "== Search wework for extra ESLint configs =="
fd -a -HI 'eslint\.config\.(js|mjs|cjs|ts)|\.eslintrc(\.(js|cjs|json|yaml|yml))?$' wework -t f || true

echo
echo "== Search wework for markdown/json/css ESLint plugins/config usage =="
rg -n 'eslint-plugin-(markdown|mdx|json|jsonc|css)|markdown|mdx|jsonc?|css' wework/eslint.config.js wework -g'*.js' -g'*.ts' -g'*.mjs' -g'*.cjs' || true

Repository: wecode-ai/Wegent

Length of output: 203


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== wework/package.json devDependencies type/value =="
jq '.devDependencies | type' wework/package.json
jq '.devDependencies // {}' wework/package.json | head -n 80

echo
echo "== wework/package.json eslint*/prettier deps (devDependencies only) =="
jq '
  ((.devDependencies // {}) | objects) as $o
  | ($o | with_entries(select(.value != null and (.key|test("^(eslint|`@eslint`|prettier)";"i")) ))) 
' wework/package.json

echo
echo "== wework lockfiles presence =="
ls -1 wework | rg -i 'package-lock\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lockb' || true

echo
echo "== wework lockfiles (eslint-plugin- occurrences) =="
rg -n --hidden --no-ignore-vcs 'eslint-plugin-' wework/{package-lock.json,yarn.lock,pnpm-lock.yaml} 2>/dev/null || true

echo
echo "== Search wework for extra ESLint configs =="
fd -a -HI 'eslint\.config\.(js|mjs|cjs|ts)|\.eslintrc(\.(js|cjs|json|yaml|yml))?$' wework -t f || true

echo
echo "== Search wework for markdown/json/css ESLint plugins/config usage =="
rg -n 'eslint-plugin-(markdown|mdx|json|jsonc|css)|markdown|mdx|jsonc?|css' wework/eslint.config.js || true

Repository: wecode-ai/Wegent

Length of output: 2132


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== wework/package.json scripts/prepare/husky (first 120 lines with numbers) =="
sed -n '1,160p' wework/package.json | cat -n

echo
echo "== wework/package.json lint-staged block (lines containing lint-staged) =="
rg -n '"lint-staged"' wework/package.json

echo
echo "== show around lint-staged block =="
python3 - <<'PY'
import re
p="wework/package.json"
lines=open(p,'r',encoding='utf-8').read().splitlines()
for i,l in enumerate(lines,1):
    if '"lint-staged"' in l:
        start=max(1,i-25); end=min(len(lines), i+40)
        for j in range(start,end+1):
            print(f"{j:5d}: {lines[j-1]}")
        break
else:
    print("not found")
PY

echo
echo "== wework/eslint.config.js any mention of json/css/md/overrides/processors/etc =="
rg -n '(json|css|md|mdx|jsonc|processor|overrides|files:|extends|languageOptions|parser)' wework/eslint.config.js || true

echo
echo "== wework/eslint.config.js full (small file) =="
wc -l wework/eslint.config.js
sed -n '1,220p' wework/eslint.config.js

Repository: wecode-ai/Wegent

Length of output: 5176


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== wework/.husky files =="
ls -la wework/.husky

echo
echo "== Husky hook scripts (show all) =="
for f in wework/.husky/*; do
  echo "--- $f ---"
  sed -n '1,200p' "$f"
done

echo
echo "== Search for lint-staged usage in husky/scripts =="
rg -n 'lint-staged' wework/.husky .. || true

echo
echo "== frontend/eslint.config.mjs file patterns (head) =="
wc -l frontend/eslint.config.mjs 2>/dev/null || true
sed -n '1,260p' frontend/eslint.config.mjs 2>/dev/null || true

Repository: wecode-ai/Wegent

Length of output: 551


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== wework/.husky directory listing =="
find wework/.husky -maxdepth 2 -type f -print

echo
echo "== wework/.husky/pre-commit (full) =="
sed -n '1,200p' wework/.husky/pre-commit

echo
echo "== wework/.husky/_ contents (top) =="
find wework/.husky/_ -maxdepth 1 -type f -print
for f in wework/.husky/_/*; do
  [ -f "$f" ] && { echo "--- $f ---"; sed -n '1,200p' "$f"; }
done

echo
echo "== Search for lint-staged in repo =="
rg -n 'lint-staged' wework/.husky .. || true

echo
echo "== frontend/eslint.config.mjs (patterns, head) =="
sed -n '1,220p' frontend/eslint.config.mjs 2>/dev/null || true

Repository: wecode-ai/Wegent

Length of output: 7312


🌐 Web query:

ESLint flat config behavior when linting a file that matches no filespatterns (e.g., config only hasfiles: ['**/*.{ts,tsx}']); does ESLint throw an error or skip file?

💡 Result:

In ESLint's flat config system, the behavior depends on whether the file is being processed because it was explicitly passed to the ESLint CLI or because it was discovered during directory traversal [1][2]. If you pass a file path to the ESLint CLI that does not match any files pattern in your configuration, ESLint will typically skip that file without throwing an error, provided the file is not being explicitly forced into the linting process [1]. However, ESLint does handle file matching and errors in the following ways: 1. CLI Argument Matching: If you pass a specific file pattern or path to the ESLint CLI (e.g., eslint "src//*.js"), and that pattern results in no files being found, ESLint may throw an error indicating that no files matched the pattern [3][4]. You can control this behavior using the --no-error-on-unmatched-pattern CLI flag [4]. 2. Configuration File Scope: A configuration object in eslint.config.js only applies to files that match its files pattern (or all files if files is omitted) [5][2]. If a file does not match any configuration object's files pattern, it is simply not linted by that object [1][5]. 3. "All Files Ignored" Warning: If you run ESLint and the configuration effectively ignores all files (or they do not match any patterns), ESLint may output a warning message stating that all files matching the pattern are ignored or not configured [6][7]. This is designed to help users identify misconfigurations [7]. In summary, ESLint does not generally throw an error simply because a file is not covered by a specific files pattern [1]. Instead, it will silently skip that file or, if you explicitly targeted that file via the CLI, it may warn you that it has been ignored or that no configuration applies to it [1][7].

Citations:


Narrow lint-staged’s ESLint targets to match wework/eslint.config.js

  • wework/package.json runs eslint --fix on *.{js,jsx,ts,tsx,json,css,md}.
  • wework/eslint.config.js only applies to **/*.{ts,tsx} (no config for json/css/md, and js/jsx are also not covered).
  • ESLint flat-config typically skips files that don’t match any files pattern, but this setup is still mismatched and can produce “no matching configuration” warnings/inconsistent lint behavior.

Update the lint-staged eslint entry to target only what ESLint is configured for (e.g., *.{ts,tsx}), and keep prettier for json/css/md.

🤖 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 `@wework/package.json` around lines 67 - 70, The lint-staged config in
wework/package.json runs "eslint --fix" against "*.{js,jsx,ts,tsx,json,css,md}"
which mismatches wework/eslint.config.js (which targets only **/*.{ts,tsx});
update the lint-staged entry so the eslint command only runs on TypeScript files
(e.g., change the eslint target to "*.{ts,tsx}" or "**/*.{ts,tsx}") and keep
Prettier handling json/css/md; locate the lint-staged config in package.json and
modify the pattern used for the "eslint --fix" task (refer to the existing
lint-staged block and the "eslint --fix" invocation) so ESLint only receives
files that match the ESLint flat-config.

Comment on lines +382 to +384
const handleActivateCreateSubmenu = () => {
updateSideSubmenuPlacement('create')
setActiveSubmenu('create')

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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify signature/call arity mismatch in ProjectWorkBar.tsx
rg -n "const updateSideSubmenuPlacement = useCallback\\(\\(\\) =>" wework/src/components/chat/composer/ProjectWorkBar.tsx
rg -n "updateSideSubmenuPlacement\\(" wework/src/components/chat/composer/ProjectWorkBar.tsx

Repository: wecode-ai/Wegent

Length of output: 244


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="wework/src/components/chat/composer/ProjectWorkBar.tsx"

echo "---- Around updateSideSubmenuPlacement definition (lines 240-280) ----"
sed -n '240,280p' "$FILE" | nl -ba | sed 's/^/ /'

echo "---- Around handleActivateCreateSubmenu (lines 360-410) ----"
sed -n '360,410p' "$FILE" | nl -ba | sed 's/^/ /'

Repository: wecode-ai/Wegent

Length of output: 192


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="wework/src/components/chat/composer/ProjectWorkBar.tsx"

echo "---- Around updateSideSubmenuPlacement definition (approx lines 245-275) ----"
sed -n '245,275p' "$FILE" | cat -n

echo "---- Around handleActivateCreateSubmenu call site (approx lines 370-395) ----"
sed -n '370,395p' "$FILE" | cat -n

Repository: wecode-ai/Wegent

Length of output: 2321


Fix TypeScript arity error in handleActivateCreateSubmenu
updateSideSubmenuPlacement is defined as useCallback(() => { ... }) (no parameters), but handleActivateCreateSubmenu calls updateSideSubmenuPlacement('create')—this causes a TS “expected 0 arguments” type-check/build failure.

const handleActivateCreateSubmenu = () => {
  updateSideSubmenuPlacement('create')
  setActiveSubmenu('create')
}
Suggested fix
  const handleActivateCreateSubmenu = () => {
-    updateSideSubmenuPlacement('create')
+    updateSideSubmenuPlacement()
     setActiveSubmenu('create')
  }
🤖 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 `@wework/src/components/chat/composer/ProjectWorkBar.tsx` around lines 382 -
384, The call to updateSideSubmenuPlacement in handleActivateCreateSubmenu
passes an unexpected argument ('create') causing a TS arity error; fix by
removing the argument and calling updateSideSubmenuPlacement() with no
parameters, leaving setActiveSubmenu('create') to drive state (or alternatively
change updateSideSubmenuPlacement's signature to accept an optional placement
parameter if placement must be passed directly). Update
handleActivateCreateSubmenu to call updateSideSubmenuPlacement() and keep
setActiveSubmenu('create') as-is (or update the updateSideSubmenuPlacement
callback signature and its callers to accept a placement string consistently).

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
wework/src/components/chat/MessageList.test.tsx (1)

217-241: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wrap fake-timer scopes with try/finally to prevent timer leakage between tests.

In both tests, fake timers are enabled but reset only on the happy path (Line 236 and Line 357). If an assertion fails earlier, vi.useRealTimers() won’t run and later tests can become flaky.

Suggested fix
   test('shows user message hover actions with time and copy', async () => {
-    vi.useFakeTimers()
-    vi.setSystemTime(new Date('2026-05-25T16:00:00.000+08:00'))
-
-    render(
-      <MessageList
-        messages={[
-          {
-            id: '1',
-            role: 'user',
-            content: '对 bind_shell=openclaw 直接跳过',
-            status: 'done',
-            createdAt: '2026-05-25T15:08:00.000+08:00',
-          },
-        ]}
-      />,
-    )
-
-    expect(screen.getByTestId('message-hover-time')).toHaveTextContent('15:08')
-
-    vi.useRealTimers()
+    vi.useFakeTimers()
+    try {
+      vi.setSystemTime(new Date('2026-05-25T16:00:00.000+08:00'))
+      render(
+        <MessageList
+          messages={[
+            {
+              id: '1',
+              role: 'user',
+              content: '对 bind_shell=openclaw 直接跳过',
+              status: 'done',
+              createdAt: '2026-05-25T15:08:00.000+08:00',
+            },
+          ]}
+        />,
+      )
+      expect(screen.getByTestId('message-hover-time')).toHaveTextContent('15:08')
+    } finally {
+      vi.useRealTimers()
+    }

     const writeText = vi.fn().mockResolvedValue(undefined)
     Object.assign(navigator, {
       clipboard: { writeText },
     })
@@
   test('shows assistant message hover actions with time and copy', async () => {
-    vi.useFakeTimers()
-    vi.setSystemTime(new Date('2026-05-25T19:00:00.000+08:00'))
-
-    render(
-      <MessageList
-        messages={[
-          {
-            id: '2',
-            role: 'assistant',
-            content: '好的,以下是作文内容。',
-            status: 'done',
-            createdAt: '2026-05-25T18:38:00.000+08:00',
-          },
-        ]}
-      />,
-    )
-
-    expect(screen.getByTestId('message-hover-time')).toHaveTextContent('18:38')
-
-    vi.useRealTimers()
+    vi.useFakeTimers()
+    try {
+      vi.setSystemTime(new Date('2026-05-25T19:00:00.000+08:00'))
+      render(
+        <MessageList
+          messages={[
+            {
+              id: '2',
+              role: 'assistant',
+              content: '好的,以下是作文内容。',
+              status: 'done',
+              createdAt: '2026-05-25T18:38:00.000+08:00',
+            },
+          ]}
+        />,
+      )
+      expect(screen.getByTestId('message-hover-time')).toHaveTextContent('18:38')
+    } finally {
+      vi.useRealTimers()
+    }

     const writeText = vi.fn().mockResolvedValue(undefined)
     Object.assign(navigator, {
       clipboard: { writeText },
     })

Also applies to: 338-362

🤖 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 `@wework/src/components/chat/MessageList.test.tsx` around lines 217 - 241, The
test enables fake timers with vi.useFakeTimers() but calls vi.useRealTimers()
only on the happy path; wrap the fake-timer usage in a try/finally so
vi.useRealTimers() always runs to avoid leaking fake timers between tests—modify
the MessageList.test.tsx tests that call vi.useFakeTimers() (the blocks
rendering <MessageList ... /> and subsequent assertions) to put the
render/assert logic inside a try and call vi.useRealTimers() in the finally; do
the same for the other test that uses vi.useFakeTimers()/vi.setSystemTime() to
ensure cleanup even on assertion failures.
🤖 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 `@wework/src/components/layout/DeviceStatusPrompt.tsx`:
- Around line 117-118: The component DeviceStatusPrompt currently memoizes now
via useMemo (const now = useMemo(() => Date.now(), [devices, presentation])),
which freezes TTL checks and timestamp writes; replace this with a per-render
timestamp (const now = Date.now()), remove the corresponding eslint-disable
comment, and ensure writeSidebarDeviceCache(devices, now) and the TTL check use
the fresh per-render now. Add a vitest fake-timers test that imports
SIDEBAR_EMPTY_DEVICE_FALLBACK_MS, renders <DeviceStatusPrompt
devices={emptyArray} presentation="sidebar-action" /> with sessionStorage seeded
with a cached updatedAt so the sidebar action initially shows, advance timers
beyond SIDEBAR_EMPTY_DEVICE_FALLBACK_MS, rerender with the same devices array
reference and presentation prop, and assert the sidebar action is no longer
rendered.

---

Outside diff comments:
In `@wework/src/components/chat/MessageList.test.tsx`:
- Around line 217-241: The test enables fake timers with vi.useFakeTimers() but
calls vi.useRealTimers() only on the happy path; wrap the fake-timer usage in a
try/finally so vi.useRealTimers() always runs to avoid leaking fake timers
between tests—modify the MessageList.test.tsx tests that call vi.useFakeTimers()
(the blocks rendering <MessageList ... /> and subsequent assertions) to put the
render/assert logic inside a try and call vi.useRealTimers() in the finally; do
the same for the other test that uses vi.useFakeTimers()/vi.setSystemTime() to
ensure cleanup even on assertion failures.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5a855936-4d21-4b66-a72b-d500c9717228

📥 Commits

Reviewing files that changed from the base of the PR and between 9c83fd5 and 254f8b8.

📒 Files selected for processing (5)
  • .github/workflows/test.yml
  • wework/src/components/chat/ChatInput.test.tsx
  • wework/src/components/chat/MessageList.test.tsx
  • wework/src/components/chat/composer/ModelSelector.tsx
  • wework/src/components/layout/DeviceStatusPrompt.tsx
💤 Files with no reviewable changes (1)
  • wework/src/components/chat/composer/ModelSelector.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/test.yml
  • wework/src/components/chat/ChatInput.test.tsx

Comment on lines +117 to +118
// eslint-disable-next-line react-hooks/purity, react-hooks/exhaustive-deps
const now = useMemo(() => Date.now(), [devices, presentation])

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="wework/src/components/layout/DeviceStatusPrompt.tsx"

# Show the relevant region around the reported lines
nl -ba "$FILE" | sed -n '80,170p'

# Find all uses of `now` in the file
rg -n "const now|now\b" "$FILE"

# Locate the TTL constant and its usage
rg -n "SIDEBAR_EMPTY_DEVICE_FALLBACK_MS|fallback" "$FILE"

Repository: wecode-ai/Wegent

Length of output: 103


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="wework/src/components/layout/DeviceStatusPrompt.tsx"

# Show relevant region with line numbers (cat -n works without nl)
cat -n "$FILE" | sed -n '90,150p'

# Find `now` declaration and its usage sites
rg -n "const now\b|now\s*-" "$FILE"
rg -n "\bnow\b" "$FILE"

# Find TTL constant and any fallback/empty-device conditions
rg -n "SIDEBAR_EMPTY_DEVICE_FALLBACK_MS|fallback|EMPTY_DEVICE|cache|updatedAt" "$FILE"

Repository: wecode-ai/Wegent

Length of output: 3629


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="wework/src/components/layout/DeviceStatusPrompt.tsx"

# Show the cache/TLL helpers near their definitions
rg -n "SIDEBAR_EMPTY_DEVICE_FALLBACK_MS|readSidebarDeviceCache|writeSidebarDeviceCache" "$FILE"
echo "----"
sed -n '1,220p' "$FILE"

Repository: wecode-ai/Wegent

Length of output: 7536


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "DeviceStatusPrompt\s*\(" wework/src || true
rg -n "DeviceStatusPrompt" wework/src || true

Repository: wecode-ai/Wegent

Length of output: 2387


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show call sites with some context
for f in \
  wework/src/components/layout/MobileWorkbenchLayout.tsx \
  wework/src/components/layout/DesktopWorkbenchMain.tsx \
  wework/src/components/layout/DesktopSidebar.tsx \
  wework/src/components/layout/DeviceStatusPrompt.test.tsx
do
  echo "==== $f ===="
  rg -n "DeviceStatusPrompt" "$f" | head
done

echo "==== call-site context (MobileWorkbenchLayout) ===="
rg -n "DeviceStatusPrompt" wework/src/components/layout/MobileWorkbenchLayout.tsx -n -C 3

echo "==== call-site context (DesktopWorkbenchMain) ===="
rg -n "DeviceStatusPrompt" wework/src/components/layout/DesktopWorkbenchMain.tsx -n -C 3

echo "==== call-site context (DesktopSidebar) ===="
rg -n "DeviceStatusPrompt" wework/src/components/layout/DesktopSidebar.tsx -n -C 3

echo "==== test file: looking for sidebar fallback / cache TTL ===="
rg -n "SIDEBAR_EMPTY_DEVICE_FALLBACK_MS|fallback|cache|updatedAt|sessionStorage|now|Date\.now|empty devices" wework/src/components/layout/DeviceStatusPrompt.test.tsx || true

Repository: wecode-ai/Wegent

Length of output: 3876


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="wework/src/components/layout/DeviceStatusPrompt.test.tsx"

# Check file size and then print the relevant sections
wc -l "$FILE" || true
echo "---- top ----"
sed -n '1,120p' "$FILE"
echo "---- mid (around sidebar TTL test) ----"
sed -n '260,380p' "$FILE"
echo "---- later ----"
sed -n '380,520p' "$FILE"

echo "---- search for fake timers / Date.now mocking ----"
rg -n "jest\.useFakeTimers|useFakeTimers|advanceTimers|setSystemTime|Date\.now|now\s*\(|SIDEBAR_EMPTY_DEVICE_FALLBACK_MS|updatedAt|sessionStorage" "$FILE" || true

Repository: wecode-ai/Wegent

Length of output: 6453


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="wework/src/components/layout/DeviceStatusPrompt.tsx"

# Find where sidebar-action button/testid is rendered
rg -n "device-status-sidebar-action-button|sidebar-action-button" "$FILE"

# Find sidebar-action rendering branches
rg -n "presentation === 'sidebar-action'|presentation\s*===\s*'sidebar-action'" "$FILE"

# Show render section around sidebar action (limit output)
rg -n "renderSidebarAction|effectiveDevices|canUseSidebarDeviceFallback" "$FILE"

Repository: wecode-ai/Wegent

Length of output: 994


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="wework/src/components/layout/DeviceStatusPrompt.tsx"

# Inspect rendering logic and sidebar-action branches
sed -n '160,280p' "$FILE"
echo "----"
sed -n '280,430p' "$FILE"
echo "----"
sed -n '430,620p' "$FILE"

Repository: wecode-ai/Wegent

Length of output: 14211


Fix sidebar fallback TTL: don’t memoize Date.now()

DeviceStatusPrompt.tsx memoizes now with [devices, presentation] and uses it for both the TTL check (now - sidebarDeviceCache.updatedAt < SIDEBAR_EMPTY_DEVICE_FALLBACK_MS) and for writing updatedAt (writeSidebarDeviceCache(devices, now)). This freezes the “30s” window/timestamping until devices/presentation change, even when the component re-renders due to local state (e.g., tooltip/manualPending).

  • Replace with a per-render timestamp (and remove the now-unneeded eslint-disable).
  • Add a vitest fake-timers test: keep devices={[]} with the same array reference and presentation="sidebar-action", seed sessionStorage with updatedAt so fallback initially shows the sidebar action, advance time past SIDEBAR_EMPTY_DEVICE_FALLBACK_MS, rerender with unchanged props, and assert the sidebar action is no longer rendered.
Suggested fix
-  // eslint-disable-next-line react-hooks/purity, react-hooks/exhaustive-deps
-  const now = useMemo(() => Date.now(), [devices, presentation])
+  const now = Date.now()
📝 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
// eslint-disable-next-line react-hooks/purity, react-hooks/exhaustive-deps
const now = useMemo(() => Date.now(), [devices, presentation])
const now = Date.now()
🤖 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 `@wework/src/components/layout/DeviceStatusPrompt.tsx` around lines 117 - 118,
The component DeviceStatusPrompt currently memoizes now via useMemo (const now =
useMemo(() => Date.now(), [devices, presentation])), which freezes TTL checks
and timestamp writes; replace this with a per-render timestamp (const now =
Date.now()), remove the corresponding eslint-disable comment, and ensure
writeSidebarDeviceCache(devices, now) and the TTL check use the fresh per-render
now. Add a vitest fake-timers test that imports
SIDEBAR_EMPTY_DEVICE_FALLBACK_MS, renders <DeviceStatusPrompt
devices={emptyArray} presentation="sidebar-action" /> with sessionStorage seeded
with a cached updatedAt so the sidebar action initially shows, advance timers
beyond SIDEBAR_EMPTY_DEVICE_FALLBACK_MS, rerender with the same devices array
reference and presentation prop, and assert the sidebar action is no longer
rendered.

@qdaxb
qdaxb merged commit 7fd4965 into wecode-ai:main Jun 12, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants