* fix(web): implement stale asset recovery in entry client and error boundary
Added event listeners in entry.client.tsx to handle stale asset errors in production, allowing for recovery from stale assets. Updated the ErrorBoundary component in root.tsx to utilize the same recovery mechanism for stale chunk failures, enhancing error handling and user experience.
* fix(web): improve stale asset recovery logic in entry client
Refactored the recoverFromStaleAsset function to return a boolean indicating whether a reload was triggered, enhancing the handling of stale asset errors. Updated the event listener in entry.client.tsx to prevent default behavior only when a recovery is in progress, improving error management in production environments.
* fix: filter out undefined label options in issue properties components
Updated the mapping of label IDs to ensure that only defined label options are included in the defaultLabelOptions array across multiple components. This change enhances the robustness of the label handling in the IssueProperties and SpreadsheetLabelColumn components, as well as in the PeekOverviewProperties component.
* fix: ensure array checks for results in various components
Updated multiple components to include checks for array types before accessing results. This change enhances stability by preventing potential runtime errors when results are undefined or not an array. Affected components include DescriptionVersionsRoot, PrevExports, SingleIntegrationCard, ProfileActivity, and IssueSubIssuesStore.
* fix: wrap children in LayoutErrorBoundary for improved error handling
Updated the IssueLayoutHOC component to include LayoutErrorBoundary, enhancing error handling by wrapping the children. This change aims to provide a more robust user experience by catching layout-related errors effectively.
* fix: optimize handleRefresh with useCallback in PrevExports component
Refactored the handleRefresh function in the PrevExports component to use useCallback, improving performance by memoizing the function. Additionally, updated the useEffect dependency array to include handleRefresh, ensuring the effect runs correctly when dependencies change. This change enhances the efficiency of the component's refresh logic.
* fix: enhance LayoutErrorBoundary with retry functionality and improved error messaging
Refactored the LayoutErrorBoundary component to include a dedicated LayoutErrorFallback for better error presentation. Added a retry mechanism that allows users to attempt to reload the content after an error occurs. This change improves user experience by providing clearer messaging and a more interactive way to recover from errors.
* fix: improve label option handling and array checks in various components
Refactored the defaultLabelOptions logic in multiple components to use flatMap for better handling of undefined labels. Additionally, updated array checks in the PrevExports component to ensure results are properly validated before access. These changes enhance the robustness and stability of the components, preventing potential runtime errors.
* fix: refactor ProfileActivity component for improved loading and data handling
Updated the ProfileActivity component to enhance the loading state management and streamline the rendering of user activity results. The refactor includes a more efficient check for userProfileActivity, ensuring that loading indicators and empty states are displayed correctly. This change improves the user experience by providing clearer feedback during data fetching and handling scenarios with no activity results.
* fix: improve type safety and array handling in integration card and sub-issues store
Updated the SingleIntegrationCard component to use a specific type for workspace integrations, enhancing type safety. Additionally, refactored the subIssues assignment in the IssueSubIssuesStore to ensure it correctly checks for an array before assignment, improving stability and preventing potential runtime errors.
* fix: enhance handleRefresh in PrevExports component with error handling
Refactored the handleRefresh function in the PrevExports component to include error handling during the refresh process. The function now uses async/await for better readability and ensures that any errors during the mutation are logged, improving the robustness of the component's refresh logic.
* style: fix oxfmt formatting flagged by CI check:format
Multi-line flatMap guard needed reformatting to satisfy oxfmt.
* style: reformat defaultLabelOptions logic for consistency
Adjusted the formatting of the defaultLabelOptions logic in the DraftIssueProperties component to maintain consistency with the project's coding standards. This change enhances readability without altering functionality.
Replaced the icon button styling with a more generic button styling for the dropdown component to ensure consistency across the UI. This change enhances the visual coherence of the dropdown button's appearance.
list() omitted the trailing slash while every sibling method and the
Django route require /users/notifications/. Self-hosted (Traefik)
surfaces the upstream 404 as 500; unread badge still works.
Fixesmakeplane/plane#9489
* fix: resolve React Doctor errors and restore its PR baseline
The React Doctor check on PR #9160 reported 266 issues across 130 files.
Most of that is a reporting artifact: the workflow's `actions/checkout`
step used the default shallow clone, so React Doctor had no merge base to
diff against and fell back to listing every pre-existing issue in every
changed file rather than only what the PR introduced. Add `fetch-depth: 0`
so the comparison works. Also point the `push` trigger at `preview` — the
repo's default branch — instead of `main`, which does not exist here, so
the health-score trend never ran.
Fix the 7 genuine errors it surfaced:
- use-keypress: `callback` sat in the effect deps while every one of the
10 call sites passes an inline arrow, so the document listener was torn
down and re-added on every render. Latch the callback in a ref and key
the subscription on `key` alone. This clears `no-effect-with-fresh-deps`
at create-root.tsx:102 and create-project-modal.tsx:64 at the source.
- estimates/points/preview: the dblclick listener was added with no
cleanup, so listeners accumulated on every toggle. Add the matching
removeEventListener.
- issues/header, calendar/issue-block, pages/editor/editor-body: guard
`window` reads that run during render with `typeof window !== "undefined"`,
matching the pattern already used elsewhere in web and admin. The editor
case previously relied on the surrounding try/catch swallowing a
ReferenceError on the server.
Also clears the five pre-existing oxlint warnings in calendar/issue-block
that the repo's `--deny-warnings` pre-commit gate blocks on once the file
is touched: rename a shadowed `issue` parameter, and mark two presentational
wrapper divs with `role="presentation"` — CustomMenu already wraps the
first in a real <button>, and the second exists only to stop click
propagation to the surrounding ControlLink.
Verified: `turbo run check:types --filter=web` passes (11/11 tasks),
`oxlint --deny-warnings` reports 0 warnings and 0 errors on the changed files.
Claude-Session: https://claude.ai/code/session_01Hrr1nfNBiyC256drHM8BbT
* fix: import EditorAIMenu directly instead of through the ai barrel
The only issue React Doctor reports against this branch. `./ai` re-exports
both menu.tsx and ask-pi-menu.tsx, so importing through it pulls the
ask-pi menu into the page editor bundle for a symbol that lives in
menu.tsx. Pre-existing on preview rather than introduced here, but it is
a one-line fix in a file this branch already touches.
Claude-Session: https://claude.ai/code/session_01Hrr1nfNBiyC256drHM8BbT
* fix: address CodeRabbit review on the React Doctor branch
- react-doctor.yml: set persist-credentials: false on checkout. The token
otherwise stays in .git/config for the third-party millionco/react-doctor
step that runs next. fetch-depth: 0 has already fetched every ref the
merge-base diff needs, and the action authenticates to the API through
its own credentials, so nothing depends on the persisted git credential.
- calendar/issue-block: pass workspaceSlug?.toString() to handleRedirection.
The param is typed string | undefined and line 89 of the same file already
optional-chains it; this call site would have thrown on a missing route
param. Pre-existing, but it is on a line this branch already touches.
Declined the two SSR/hydration findings: apps/web sets ssr: false in
react-router.config.ts and ships a client-only bundle, so there is no
server render to diverge from. The typeof window guards satisfy the
React Doctor rule but are defensive only.
Claude-Session: https://claude.ai/code/session_01Hrr1nfNBiyC256drHM8BbT
* refactor: migrate constants (fetch-keys) from apps/web to @plane/constants
* refactor: migrate constants (ai, calenda, gaant) from apps/web to @plane/constants
* refactor: migrate constants (sidebar, favorites) from apps/web to @plane/constants
* refactor: migrate constants (editor) from apps/web to @plane/constants
* refactor: migrate constants (plans) from apps/web to @core/components
* resolved lint errors
* fix: resolve coderabbit comments
* refactor: resolve coderabbit comments
* refactor: migrate hooks (use-file-size) from web/app/ce to web/app/core
* refactor: migrate hooks (use-notification-preview) from web/app/ce to web/app/core
* refactor: migrate hooks (use-timeline-chart) from web/app/ce to web/app/core
* refactor: migrate hooks (use-page, use-page-store) from web/app/ce to web/app/core
* refactor: migrate hooks (app-rail, indexes) from web/app/ce to web/app/core
* refactor: migrate hooks (use-page-flag) from web/app/ce to web/app/core
* refactor: migrate hooks (use-editor-flagging) from web/app/ce to web/app/core
* refactor: migrate hooks (use-filters-operator-configs) from web/app/ce to web/app/core
* refactor: migrate hooks (use-additional-editor-mention) from web/app/ce to web/app/core
* refactor: migrate hooks (use-additional-favorite-item-details) from web/app/ce to web/app/core
* refactor: migrate hooks (use-extended-editor-extensions, use-pages-pane-extensions) from web/app/ce to web/app/core
* refactor: migrate hooks (use-work-items-filters-config) from web/app/ce to web/app/core
* refactor: migrate hooks (use-extended-editor-config) from web/app/ce to web/app/core
* refactor: migrate hooks (use-bulk-operations) from web/app/ce to web/app/core
* refactor: migrate hooks (use-debounced-duplicate-issues) from web/app/ce to web/app/core
* refactor: migrate hooks (use-issue-properties) from web/app/ce to web/app/core
* refactor: migrate hooks (use-workspace-issue-properties) from web/app/ce to web/app/core
* refactor: delete hook (use-issue-embed) from web/app/ce
* fix: coderabbit comments
* fix: React doctor comments
* fix: import structure for hooks
* refactor: remove command palette & sidebar components and related files from web/app/ce
* refactor: update analytics tab imports and add new analytics tab components
* feat: add project, work item, and workspace level modals for enhanced user interaction
* refactor: replace WorkspaceActiveCyclesRoot with WorkspaceActiveCyclesUpgrade and remove obsolete components
* refactor: migrate app-rail HOC to core components and remove obsolete index file
* refactor: remove unused automation components and simplify layout structure
* refactor: update import paths for CommonProjectBreadcrumbs and add new breadcrumb components
* refactor: update import path for WorkItemDetailRoot and add new work item detail component
* refactor: remove obsolete comments index file and introduce CommentBlock component in core
* refactor: update import paths for common components and introduce new ExtendedAppHeader, GlobalModals, and SubscriptionPill components
* refactor: remove obsolete index file and add MaintenanceMessage and InboxSourcePill components
* refactor: remove obsolete cycle components and introduce new cycle-related components in core
* refac: moved de-dupe directory to core
* refactor: add new desktop components and update import paths for sidebar functionality
* refactor: remove obsolete index file and introduce new version number and product updates components in core
* refactor: add EpicModal component and update import paths in issue layouts
* refactor: add HomePageHeader and HomePeekOverviewsRoot components, update import paths in home and issues sections
* refactor: remove obsolete home index file, update import paths for relation options in issue detail components, and introduce new activity helper functions
* refactor: remove AdditionalFilterValueInput from legacy path and reintroduce it in core filter value input component
* refactor: remove legacy workspace-notifications index file and introduce new notification components in core
* refactor: remove legacy license components and update import paths for PaidPlanUpgradeModal
* refactor: remove legacy navigation components and update import paths for navigation items in core
* refactor: introduce onboarding tour components and update import paths for tour-related files
* refactor: remove legacy theme switcher component and update import paths in profile settings
* refactor: update import paths for workflow components and introduce new workflow-related files in core
* refactor: remove legacy estimate components and introduce new estimate-related files in core
* refactor: remove legacy gantt-chart components and introduce new core components for gantt-chart functionality
* refactor: remove legacy helper components and introduce new access control and publish components in core
* refactor: introduce billing components and update import paths for billing-related files in core
* refactor: introduce new members components and update import paths for workspace members functionality
* refactor: update import paths for workspace components and introduce new workspace-related files
* refactor: remove deprecated components and clean up import paths across various modules
* refactor: remove unused components and clean up import paths across various modules
* refactor: remove unused sidebar components and update import paths in workspace notifications
* refactor: introduce new estimate, billing, and notification card components while updating import paths across various modules
* refactor: remove unused estimate and billing components, update import paths, and streamline workspace notification card structure
* refactor: update import paths for project components and remove unused files in the projects module
* refactor: remove unused power-k components and update import paths in the command palette module
* refactor: remove unused issue components and update import paths across the issues module
* refactor: remove unused mentions components and update import paths in the editor module
* refactor: remove unused components and update import paths across the pages module
* refactor: update type imports for issue properties in issue modal context
* fix: oxfmt
* fix: PR checks
* [GIT-254] Refactor: Store consolidation to @core/store (#9271)
* refactor: remove unused store files and update import paths across the application
* refactor: remove unused store files and update import paths across the core module
* refactor: remove unused issue filter and store files, and update import paths in the core module
* refactor: update import paths for timeline store files and introduce new base timeline store
* refactor: remove deprecated root store file and update import paths across the application
* refactor: update import paths for store files and correct root store type references
* refactor: standardize import comments and remove 'plane-web' artifacts across various components
* fix: CodeRabbit comments
* fix: format
* refactor: update TypeScript configuration and improve sorting method in TabNavigationRoot component
* refactor: replace toSorted with sort method for navigation item sorting in TabNavigationRoot component
---------
Co-authored-by: Rahulcheryala <rahulcheryala2004@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Updated the `updateCurrentUser` method in `UserStore` to clone the current user data before making updates, ensuring that the original data remains unchanged during the update process. Additionally, added logic to update the local state with the new user data after a successful update.
fix(cover-image): return absolute URLs for cover images
Modified the `handleCoverImageChange` function to return absolute URLs for cover images, ensuring compatibility with the expected format. This change includes handling both uploaded images and new images, providing a consistent return structure.
Bot service accounts (User.is_bot=True, e.g. the WORKSPACE_SEED bot) are
internal identities meant to act only through API tokens. Nothing stopped
one from being driven through the interactive login flow if its email was
known, letting a human assume a service identity.
Reject bot accounts at the shared login chokepoint,
Adapter.complete_login_or_signup(), right beside the existing
deactivated-account check. This covers every interactive provider in one
place: email/password, magic code, and all OAuth providers (Google, GitHub,
GitLab, Gitea) across both the app and space surfaces. Bot API-token access
is left untouched, since that is how bots are meant to operate.
Also add a defense-in-depth is_bot guard to InstanceAdminSignInEndpoint,
which mints its own admin session outside the chokepoint (a bot is never an
InstanceAdmin today, so this is not currently reachable, but it closes the
path regardless).
Surface the rejection with a new dedicated error code
BOT_USER_LOGIN_FORBIDDEN (5017), plumbed into the app and space frontend
error helpers as well as the shared @plane/constants and @plane/utils
packages (message map + banner-alert list) so any consumer of the shared
auth-error handler renders it correctly. The admin path reuses the existing
ADMIN_AUTHENTICATION_FAILED code so it discloses no bot-specific error.
Add contract regression tests: a bot blocked via password and via magic
code, a bot blocked at the admin sign-in endpoint, and a non-bot control
that still logs in.
Resolves the sole open Dependabot alert (GHSA-gv7w-rqvm-qjhr, high):
esbuild <0.28.1 lacks binary-integrity verification in its Deno install
path, enabling RCE via NPM_CONFIG_REGISTRY. The pnpm `overrides` entry
pinned the entire tree to esbuild 0.25.0; bump it to 0.28.1, which fixes
all transitive paths (vite, vitest, tsx, vite-node, esbuild-register).
Also bump turbo 2.9.14 -> 2.9.18 in the workspace catalog and the four
pinned production Dockerfiles (web, live, admin, space).
Verified: `pnpm audit` clean, admin react-router/vite build succeeds,
@plane/codemods vitest suite 33/33 passing.
* fix: bump npm deps to resolve Dependabot advisories
Resolve 8 open Dependabot alerts (all npm, in pnpm-lock.yaml) by bumping
the affected packages in pnpm-workspace.yaml and regenerating the lockfile:
- axios 1.15.2 -> 1.16.0 (catalog): CVE-2026-44494/44492/44490/44489
- tmp -> 0.2.6 (override): CVE-2026-44705 path traversal
- ws 8.x -> 8.20.1 (catalog + scoped override): CVE-2026-45736
- qs 6.14.2 -> 6.15.2 (override): CVE-2026-8723 DoS
- brace-expansion 5.0.5 -> 5.0.6 (override): CVE-2026-45149 DoS
brace-expansion and qs were pinned to their vulnerable versions in the
overrides block, so the pins had to be bumped directly. ws is scoped to
the 8.x major (ws@7.5.10 is below the vulnerable >=8.0.0 floor). All bumps
are semver-compatible patch/minor upgrades; no source changes required.
* fix: use named axios `create` import after 1.16.0 bump
axios 1.16.0 newly exposes `create` as a named export, so oxlint's
import/no-named-as-default-member rule now flags `axios.create(...)`.
That added one warning to @plane/services (7 > its --max-warnings=6
baseline) and to apps/web and apps/live, failing check:lint — surfaced
on this PR because the lockfile change busts Turbo's lint cache.
Switch the three `axios.create(...)` call sites to a named `{ create }`
import. `create` is a real value+type export in axios 1.16.0 (verified
via tsc). isCancel/CancelToken are left as `axios.*`: CancelToken is
only a type export (cannot be a value import under verbatimModuleSyntax)
and both were already counted within the existing baselines.
Verified locally: full `pnpm check:lint` (16/16) and `check:types`
(15/15) pass.
The web/admin/space Docker image builds fail at the Vite/PostCSS step with
"Cannot find module '@tailwindcss/postcss'". These apps load the shared
@plane/tailwind-config/postcss.config.js, which references the @tailwindcss/postcss
plugin by name, but the plugin was only declared as a dependency of
packages/tailwind-config.
The Docker build installs via turbo prune + 'pnpm fetch' + 'pnpm install --offline',
which lays out node_modules so PostCSS resolves the plugin relative to the app
directory (apps/<app>), where it is not reachable. A plain 'pnpm install' resolves
it from tailwind-config's context instead, which is why local builds passed and
masked the issue.
Declare @tailwindcss/postcss as a direct devDependency of the three apps that run
Vite/PostCSS so it is symlinked into each app's node_modules and resolves under the
isolated linker regardless of install flow.
Verified by reproducing the exact Docker flow (prune -> fetch -> --frozen-lockfile
offline install -> build) for admin, space and web: all install in sync and build
successfully with full Tailwind CSS output.
Centralize every external dependency version in the pnpm catalog
(pnpm-workspace.yaml) and reference them via `catalog:` across all
apps and packages. Packages that previously used differing versions
were unified to the highest (notably @react-pdf/renderer ^3.4.5 ->
^4.3.0 in apps/web).
* fix(web): add Safari fallback for requestIdleCallback
* fix(web): use globalThis in idle-task fallbacks
Switch idle-task fallback paths from window.* to globalThis.* so the
fallback no longer crashes in environments where window is undefined.
Also thread IdleRequestOptions through requestIdleFallback so the
caller's timeout hint is honored when falling back.
Addresses CodeRabbit review feedback on #9137.
---------
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
* chore: bump turbo to 2.9.14, migrate pnpm config to workspace yaml
- Bump turbo from 2.9.4 to 2.9.14 in root package.json and the
four production Dockerfiles (web, live, admin, space).
- Move pnpm.overrides, onlyBuiltDependencies, and
ignoredBuiltDependencies from package.json into pnpm-workspace.yaml.
pnpm v10+ no longer reads the pnpm field in package.json, so the
full overrides block and most of onlyBuiltDependencies were being
silently ignored.
- Add @plane/utils as a workspace dependency to the live server.
* chore: drop unused allowBuilds block, bump lodash-es to 4.18.1
- Remove the `allowBuilds` block from pnpm-workspace.yaml. It is not
a recognized pnpm v10/v11 key and its values were inconsistent with
the actual `onlyBuiltDependencies` / `ignoredBuiltDependencies`
configuration.
- Bump `lodash-es` catalog entry from 4.18.0 to 4.18.1. With overrides
now applied workspace-wide, 4.18.0 (marked deprecated as a "bad
release") was being enforced everywhere.
* fix: use pnpm v11 allowBuilds in place of removed legacy keys
`onlyBuiltDependencies` and `ignoredBuiltDependencies` were removed
in pnpm v11. They were being silently ignored on this branch, which
caused `ERR_PNPM_IGNORED_BUILDS` to fail CI under `--frozen-lockfile`.
Replace them with the v11-native `allowBuilds:` block, mapping the
previous allowlist to `true` and the previous denylist (sharp) to
`false`. Locally verified that the build scripts for @parcel/watcher,
@swc/core, esbuild, and msgpackr-extract now run on install.
* refactor(i18n): migrate packages/i18n from MobX to react-i18next with per-feature namespaces
Replaces the internals of packages/i18n with react-i18next while preserving the
identical public API. Consumer code using useTranslation() and TranslationProvider
requires no changes.
Translation file format: TS objects to JSON namespaces
- Converted TypeScript translation files (19 languages) into feature-based JSON namespace files
- Split the monolithic translations.ts into per-feature namespace files: workspace.json,
project.json, work-item.json, cycle.json, inbox.json, etc.
- 30 community namespaces across 19 languages = 570 JSON files
Core runtime: MobX to i18next
- Replaced MobX TranslationStore with an i18next instance using i18next-icu
(preserves ICU MessageFormat) and i18next-resources-to-backend (namespace lazy loading)
- useTranslation() and TranslationProvider keep identical signatures
- All namespaces pre-loaded during init for the current language to prevent
re-render cascades
- Reads saved language from localStorage before init for faster first paint
Build tooling
- scripts/generate-types.ts: Reads English JSON files and outputs keys.generated.ts
with a flat union of translation keys (runs before every build)
- scripts/sync-check.ts: Cross-locale missing/stale key detection, cross-namespace
collision detection, path conflict detection (supports --ci mode)
App-level changes
- Removed useTranslation-based language sync effect from store-wrapper
- Language is now synced imperatively from profile.store (fetchUserProfile,
updateUserProfile) and root.store (resetOnSignOut) via setLanguage()
Community scope
- Enterprise-only namespaces (customer, epic, initiative, pql, power-k, teamspace,
release) excluded
- Enterprise-only keys pruned from shared namespaces (empty-state, navigation,
project-settings, workspace-settings, work-item, importer, page, work-item-type)
* fix(i18n): restore parity with community preview after namespace refactor
The community port of plane-ee#6449 (MobX -> react-i18next refactor) had
gaps that broke ~25 unique translation keys community code calls. This
commit restores parity:
- Port power-k namespace (19 locales) from plane-ee, stripped of EE-only
paths (initiative/customer/teamspace/dashboards/AI assistant). Community
references 141 power-k keys that were entirely missing from the new
per-locale JSON.
- Restore epic.* keys (8 leaves) into work-item.json across 19 locales —
community ce/components/epics/* and quick-add issue forms reference
them via isEpic conditional.
- Add 'date' leaf to common.json across 19 locales (sourced from
work_item_types.settings.properties.property_type.date.label so the
proper translation, not English, is used).
- Move exporter.* subtree from importer.json to common.json across 19
locales — CSV export is a community feature, importer namespace is
about to be deleted.
- Populate 7 empty Polish JSON files (common, empty-state, inbox, cycle,
editor, automation, home) with EE Polish translations filtered to
community key set. The community port committed these as 0-byte files.
- Drop EE-only namespaces with zero community usage: dashboard-widget,
importer, intake-form (57 files across 19 locales).
- Update NAMESPACES const: drop the 3 deleted namespaces, add power-k.
- Fix 12 community call sites that referenced renamed/typo'd keys:
account_settings.api_tokens.heading -> .title
auth.common.password.toast.error.* -> .change_password.error.*
sign_out.toast.error.* -> auth.sign_out.toast.error.*
notification.toasts.un_snoozed -> .unsnoozed
profile.stats.priority_distribution.priority -> common.priority
projects.label -> common.projects
progress -> common.progress
epics -> common.epics
creating_theme -> common.saving (no localized source available)
toast.error (with trailing space typo) -> toast.error
Verified: every literal t(...) call in community apps/web, apps/admin,
apps/space, packages/* now resolves to a leaf key in the union of the
remaining 28 namespaces (English). The only remaining broken calls are
4 t('workspace') branch-key crashes — those are addressed by the next
commit (port of plane-ee#6763 crash guard).
Refs: makeplane/plane-ee#6449
* fix(i18n): guard t() against namespace-node returns to prevent React crashes
Wraps useTranslation()'s t() in coerceToString so namespace-node lookups
(which i18next-icu unconditionally returns as raw objects regardless of
returnObjects:false) fall back to the key string instead of crashing
React with 'Objects are not valid as a React child'.
Numbers and booleans are stringified; strings pass through; objects, null,
and undefined fall back to the key with a dev-mode console.warn pointing
to the bad call site. Production builds suppress the warning but keep the
guard. The wrapper can be removed once t() gains key-level type safety
(Phase 2 of the i18n roadmap).
Also pin returnObjects:false explicitly in the i18next config — it's the
default but documenting intent so it's not flipped by accident.
Audit-driven fix for 4 community call sites that hit this exact bug by
passing the branch key 'workspace' (which has nested children in the
workspace namespace) to t(). Switched to t('common.workspace') (existing
leaf with value 'Workspace').
Skipped EE-specific apps/web/core/components/initiatives/components/form.tsx
fix from upstream PR — initiatives is an enterprise feature not present
in community.
Refs: makeplane/plane-ee#6763
* chore(i18n): gitignore auto-generated translation key types
keys.generated.ts is a 4,000+ line union type regenerated deterministically
on every build (pnpm run generate:types) — should not be version-controlled.
Adding the file to .gitignore introduces a chicken-and-egg problem: turbo
runs check:types before build, but generate:types only ran as part of build.
On a fresh clone with no keys.generated.ts present, tsc --noEmit fails. Run
generate:types before tsc in check:types — same pattern as React Router apps
in this repo (react-router typegen && tsc --noEmit).
- Add packages/i18n/src/types/keys.generated.ts to root .gitignore
- Untrack the file from git (git rm --cached)
- Run generate:types before tsc in check:types
Verified: deleting keys.generated.ts and running check:types regenerates
the file correctly. After regeneration, git status shows the file remains
untracked (.gitignore is honored).
Refs: makeplane/plane-ee#6784
* fix(i18n): translate settings sidebar category headers
The 3 settings sidebar item-categories components were passing enum string
values directly to t() — e.g. t('your profile'), t('work-structure'),
t('administration'). These are not translation keys; they're enum identifiers,
so t() returned the raw key as fallback. Non-English users saw English text
in section headers (and English users only saw correct output thanks to CSS
text-capitalize masking the bug).
Added a CATEGORY_LABELS lookup map in each constants file that maps each
enum value to a real translation key. Components now call t(LABELS[category])
instead of t(category).
- Added 5 new keys to en/common.json common.* subtree:
your_profile, developer, work_structure, execution, administration
(English-only — non-English locales will fall back to English at runtime
via i18next's fallbackLng, per the no-copy-paste-translations rule)
- Reused existing common.general and common.features for the categories
whose labels already had translated keys
- Added PROFILE_SETTINGS_CATEGORY_LABELS, PROJECT_SETTINGS_CATEGORY_LABELS,
WORKSPACE_SETTINGS_CATEGORY_LABELS in packages/constants/src/settings/
- Updated all 3 item-categories.tsx components
Found via comprehensive dynamic-key audit (1918 t() invocations classified
across literal, template-literal, property-access, conditional, function-call,
and identifier patterns). Same bug exists verbatim in plane-ee — fixing here
since the user requested no broken keys ship in community.
* chore: untrack Claude Code runtime lockfile
.claude/scheduled_tasks.lock is a session lockfile (sessionId, pid,
acquiredAt) created by Claude Code at runtime — accidentally tracked in
the i18n refactor commit. Untrack from git; the file stays on disk for
the running session.
* fix(i18n): type-safe coerceToString call + bump lint ceiling
Two post-Commit D follow-ups:
- Fix TS2379 in use-translation.ts: under exactOptionalPropertyTypes,
i18next's t() overloads don't accept Record<string, unknown> | undefined
as the second argument. Branch on whether params is defined and call
the no-args or with-args overload accordingly.
- Bump @plane/i18n check:lint --max-warnings from 2 to 9. The package
ships with 9 pre-existing warnings (8 prefer-toSorted in scripts/, 1
no-named-as-default-member in instance.ts on a line untouched by my
changes). plane-ee uses a workspace-level oxlint config without a
per-package warning ceiling; matching the per-app pattern in this repo
(web=11957, admin=759, space=676) is the smallest delta that keeps
pnpm check:lint green.
Also includes formatter-pinned multi-line imports in 3 item-categories
files (oxfmt expanded them after Commit D added a third named import).
* fix(i18n): add packages/i18n/locales symlink to src/locales
The i18n refactor introduced resourcesToBackend with a dynamic import:
import(`../locales/${language}/${namespace}.json`)
That path is relative to the source file's location. From src/core/instance.ts
it correctly resolves to src/locales/. But after tsdown bundling, the same
import call lives in dist/index.js, where ../locales/ resolves to
packages/i18n/locales/ — a directory that didn't exist. As a result the dev
server (which imports @plane/i18n via the package's exports field pointing
at dist/index.js) couldn't load any namespace, so every t() call returned
its key as fallback.
Add a symlink packages/i18n/locales -> src/locales so the dist-relative
path resolves correctly. Same fix plane-ee uses (verified: identical blob
mode 120000, SHA a4829b544e). Keeps tsdown.config.ts and package.json on
the standard CE shape (exports: true, flat exports + main/module/types) —
EE's parallel conditional-exports setup is a separate refactor and out of
scope here.
* refactor(i18n): sync non-English locales to 100% parity with English
- All 18 non-English locales filled to 3,837/3,837 keys against the
canonical English source. Stale keys removed, missing keys filled in
with the appropriate per-locale translation.
- New scripts/lib/locale-io.ts module shared between sync-check and
future tooling. readJsonFile() wraps JSON.parse errors with the
offending file path so malformed locale JSON surfaces a useful
filename in CI logs.
- New .github/workflows/i18n-sync-check.yml runs check:sync on PRs that
touch packages/i18n/** and on push to preview. Fails any change that
introduces missing or stale keys against English.
- Pin tsx@4.20.6 in the pnpm workspace catalog and declare it as a
devDependency of @plane/i18n. Replace npx tsx@4.19.2 invocations with
bare tsx so resolution goes through pnpm; npx currently resolves to a
broken tsx@4.21.0 that pulls an unpublished esbuild range.
---------
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
X-Forward-For is not a real HTTP header — the standard is X-Forwarded-For.
With the typo, Nginx never replaces $remote_addr with the actual client IP,
so rate limiting and IP logging see the proxy IP instead of the real client.
Affects all three nginx configs (web, admin, space).
* chore(deps): replace dotenvx with dotenv and update dependency overrides
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: sort devDependencies in package.json files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>