- Add a `pageLayoutId` foreign key to `CommandMenuItem`, allowing
command menu items to be scoped to a specific page layout instead of
being globally available
- Filter command menu items by the current page layout on the frontend.
Items with a `pageLayoutId` only appear when viewing that layout, while
items without one remain globally visible
- Create an effect to track the current page layout ID
- Include a seed example: a "Show Notification" command pinned to the
Star history standalone page layout
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
## Summary
- Move record fetching and payload building from the enrichment hook
into `TriggerWorkflowVersionEngineCommand`, following the same
component-based pattern as `DeleteRecordsCommand` and
`RestoreRecordsCommand`
- The enrichment hook now only stores workflow metadata (`trigger`,
`availabilityType`, `availabilityObjectMetadataId`); the component uses
`useLazyFetchAllRecords` for exclusion mode (Select All) with full
pagination
- `buildTriggerWorkflowVersionPayloads` is now a pure function accepting
`selectedRecords: ObjectRecord[]` instead of reading from the Jotai
store
Fixes the issue introduced by #19718 which blocked Select All with a
warning toast instead of implementing it.
## Test plan
- [ ] Select individual records → run workflow trigger from command menu
→ works as before
- [ ] Click Select All → run workflow trigger from command menu →
fetches all matching records and runs the workflow
- [ ] Select All with some records deselected → correctly excludes those
records
- [ ] Global workflows (no object context) → run without payload as
before
- [ ] Bulk record triggers → payload wraps records in `{namePlural:
[records]}`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- `SettingsApplicationCustomTab` renders `FrontComponentRenderer` which
calls `useFrontComponentExecutionContext` →
`useLayoutRenderingContext()`, but the settings page never provided a
`LayoutRenderingProvider`
- Every other render site (side panel, command menu, record pages) wraps
`FrontComponentRenderer` with this provider — it was just missed here
- Opening the "Custom" tab in Settings → Applications crashes with:
`LayoutRenderingContext Context not found`
- Fix: wrap with `LayoutRenderingProvider` using `DASHBOARD` layout type
and no target record, matching the pattern used in
`SidePanelFrontComponentPage`
## Test plan
- [ ] Open Settings → Applications → any app with a custom settings tab
- [ ] Click the "Custom" tab — should render the front component without
crashing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This regressed with the migration work
Type checker should've caught it, but didn't because of `Partial`
changed it to `Pick` instead to avoid future cases
/closes https://github.com/twentyhq/twenty/issues/19745
Add Storybook stories and example components to test event forwarding
through the front component renderer: form events (text input, checkbox,
focus/blur, submit), keyboard events (key/code/modifiers), and host API
calls (navigate, snackbar, progress, close panel)
Resolves the following two issues. The reason we had these issues was
because the maxLimit of renderers was reached and the browser started
losing context before restoring it. Now, we make sure the context that
is lost belongs to visuals that are not currently in the viewport and
when those visuals come back into viewport, they're loaded again.
https://github.com/twentyhq/core-team-issues/issues/2372https://github.com/twentyhq/core-team-issues/issues/2373
## Summary
- Bump `twenty-sdk` from `1.22.0-canary.6` to `1.22.0`
- Bump `twenty-client-sdk` from `1.22.0-canary.6` to `1.22.0`
- Bump `create-twenty-app` from `1.22.0-canary.6` to `1.22.0`
Fix isUnique not sent in field update mutation: Added isUnique and
settings to the Pick type in useUpdateOneFieldMetadataItem, which
previously silently dropped these properties from the GraphQL payload.
Invalidate cache on failed migration rollback: Added invalidateCache()
call in the migration runner's catch block so that a failed migration
(e.g., unique index creation failing due to duplicate data) regenerates
the Redis hash, preventing stale metadata from persisting in frontend
localStorage indefinitely. Was
## Summary
- import shared company logos and people avatars into
`packages/twenty-website-new/public/images/shared`
- expand the shared asset registry with the new local logo and avatar
paths
- update the home hero data to use local shared assets for matched
people and company icons
- replace synthetic or mismatched hero avatars with better-fitting named
or anonymous local assets
- prefer local shared company logos in hero visual components before
falling back to remote icons
## Testing
- Not run (not requested)
---------
Co-authored-by: Abdullah <125115953+mabdullahabaid@users.noreply.github.com>
## Summary
Route-triggered logic functions were returning empty 500 responses with
zero server-side logging when the Lambda build chain failed. This PR
makes those failures observable and returns meaningful HTTP responses to
API clients.
- **Observability** — Log errors (with stack traces) at each layer of
the execution chain: `LambdaDriver` (deps-layer fetch, SDK-layer fetch,
invocation), `LogicFunctionExecutorService`, and `RouteTriggerService`.
- **Typed exceptions** — Replace raw `throw error` sites with
`LogicFunctionException` carrying an appropriate code and
`userFriendlyMessage` (new codes: `LOGIC_FUNCTION_EXECUTION_FAILED`,
`LOGIC_FUNCTION_LAYER_BUILD_FAILED`).
- **Correct HTTP semantics** — `RouteTriggerService` maps inner
exception codes to the right `RouteTriggerExceptionCode` so
`LOGIC_FUNCTION_NOT_FOUND` returns 404 and `RATE_LIMIT_EXCEEDED` returns
429 (new code + filter case) instead of a generic 500.
- **User-facing messages** — Forward the inner
`CustomException.userFriendlyMessage` when wrapping into
`RouteTriggerException`, without leaking raw internal error text into
the public exception message.
- **Infra** — Bump Lambda ephemeral storage from 2048 to 4096 MB to
prevent `ENOSPC` errors during yarn install layer builds (root cause of
the original silent failures).
## Summary
Fixes error snackbars disappearing too quickly by **disabling
auto-dismiss for error variants by default**. Error snackbars now remain
visible until the user closes them.
Closes#19694
## What changed
- Error snackbars no longer default to a 6s timeout (they only
auto-dismiss if an explicit `duration` is provided).
- Non-error snackbars keep the existing default auto-dismiss behavior
(6s).
- Progress bar animation/visibility is tied to auto-dismiss (no progress
bar when there’s no duration).
**Files**
-
packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx
## How to test
1. Trigger a long error snackbar (example: spreadsheet/CSV import error
with a long message).
2. Confirm the snackbar **stays visible** until clicking **Close**.
3. Trigger a success/info snackbar and confirm it **still
auto-dismisses** after ~6s.
## Notes
- Call sites that explicitly pass `options.duration` for error snackbars
will continue to auto-dismiss (intentional).
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Summary
- Remove the evictViewMetadataForViewIds step from the page-layout reset
flow. It synchronously cleared viewFields/viewFieldGroups rows from the
metadata store, leaving a window where
useFieldsWidgetGroups saw a view with no fields and fell back to
buildDefaultFieldsWidgetGroups, briefly rendering a synthetic "General"
+ "Other" layout before the real reset defaults
arrived.
- invalidateMetadataStore() alone is sufficient: it marks the
collections stale and triggers MinimalMetadataLoadEffect to refetch,
which replaces current atomically. The UI now
transitions old-layout → new-default with no synthetic flash.
- Simplified refreshPageLayoutAfterReset to no longer take a
collectAffectedViewIds callback, and updated both tab/widget reset call
sites plus ObjectLayout.tsx accordingly.
- Deleted the now-unused evictViewMetadataForViewIds and
collectViewIdsFromWidgets utils.
## Summary
### Problem
The upgrade migration system required new workspaces to always start
from a workspace command, which was too rigid. When the system was
mid-upgrade within an instance command (IC) segment, workspace creation
would fail or produce inconsistent state.
### Solution
#### Workspace-scoped instance command rows
Instance commands now write upgrade migration rows for **all
active/suspended workspaces** alongside the global row. This means every
workspace has a complete migration history, including instance command
records.
- `InstanceCommandRunnerService` reloads `activeOrSuspendedWorkspaceIds`
immediately before writing records (both success and failure paths) to
mitigate race conditions with concurrent workspace creation.
- `recordUpgradeMigration` in `UpgradeMigrationService` accepts a
discriminated union over `status`, handles `error: unknown` formatting
internally, and writes global + workspace rows in batch.
#### Flexible initial cursor for new workspaces
`getInitialCursorForNewWorkspace` now accepts the last **attempted**
(not just completed) instance command with its status:
- If the IC is `completed` and the next step is a workspace segment →
cursor is set to the last WC of that segment (existing behavior).
- If the IC is `failed` or not the last of its segment → cursor is set
to that IC itself, preserving its status.
This allows workspaces to be created at any point during the upgrade
lifecycle, including mid-IC-segment and after IC failure.
#### Relaxed workspace segment validation
`validateWorkspaceCursorsAreInWorkspaceSegment` accepts workspaces whose
cursor is:
1. Within the current workspace segment, OR
2. At the immediately preceding instance command with `completed` status
(handles the `-w` single-workspace upgrade scenario).
Workspaces with cursors in a previous segment, ahead of the current
segment, or at a preceding IC with `failed` status are rejected.
### Test plan
created empty workspaces to allow testing upgrade with several active
workspaces
## Summary
- The `AddStandalonePageFastInstanceCommand` migration was failing with:
`default for column "type" cannot be cast automatically to type
core."navigationMenuItem_type_enum"`
- The migration was missing `DROP DEFAULT` / `SET DEFAULT` around the
`ALTER COLUMN TYPE` for `navigationMenuItem.type`. PostgreSQL cannot
automatically cast the existing default (`'VIEW'::old_enum`) to the new
enum type.
- The same 3-step pattern (DROP DEFAULT → ALTER TYPE → SET DEFAULT) was
already correctly applied for `pageLayout.type` in the same migration —
this fix brings `navigationMenuItem.type` in line.
- Add 72 missing HTML and SVG elements to the remote-dom component
registry (48 HTML + 24 SVG), bringing the total from 47 to 119 supported
elements
- HTML additions include semantic inline text (b, i, u, s, mark, sub,
sup, kbd, etc.), description lists, ruby annotations, structural
elements (figure, details, dialog), and form utilities (fieldset,
progress, meter, optgroup)
- SVG additions include containers (svg, g, defs), shapes (path, circle,
rect, line, polygon), text (text, tspan), gradients (linearGradient,
radialGradient, stop), and utilities (clipPath, mask, foreignObject,
marker)
- Add htmlTag override to support SVG elements with camelCase names
(e.g. clipPath, foreignObject) while keeping custom element tags
lowercase per the Web Components spec
## Summary
- Introduce a `activeNavigationMenuItemState` Jotai atom (persisted via
localStorage) to disambiguate active navigation items when multiple
items share the same URL
- Add active item evaluation for record show pages with three scenarios:
1. Navigating from a nav item → parent stays active + dedicated RECORD
item also active
2. Clicking a dedicated RECORD nav item → only that item active
3. Navigating via search/direct link → OBJECT nav item fallback, or
Opened section if none exists
- Extract shared active logic into `isNavigationMenuItemActive` utility
to eliminate duplication between orphan items and folder items
- Support multiple simultaneously active items within folders via
`Set<number>` instead of a single index
---------
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
## Context
Tab duplication was broken after the view creation logic was deferred
and moved to the FE.
- Duplicating a tab or a FIELDS widget now produces a fully independent
copy: new view, new view field groups, new view fields — all with fresh
IDs — while preserving any unsaved edits
from the source widget.
- Removed the backend auto-seed of default view fields / view field
groups in ViewService.createOne for FIELDS_WIDGET views. The frontend
always sends the complete layout via
upsertFieldsWidget, so the auto-seed was both redundant and the source
of potential bugs.
- Extracted a shared useDuplicateFieldsWidgetForPageLayout hook used by
both tab and widget duplication paths, plus a small
useCloneViewInMetadataStore helper that clones the FlatView
in the metadata store and returns the copied flat view fields/groups for
the caller.
Fix a stack overflow (Maximum call stack size exceeded) in
getAllStepIdsInLoop caused by an If/Else branch inside an iterator loop
pointing back to the enclosing iterator. The traversal incorrectly
treated the enclosing iterator as a nested iterator, calling
getAllStepIdsInLoop recursively with fresh visited sets, causing
infinite recursion.
Add the enclosing iterator's own ID to the skip condition in
traverseSteps so back-edges from If/Else branches are handled the same
way as back-edges from regular nextStepIds.
<img width="1054" height="723" alt="Capture d’écran 2026-04-15 à 11 00
42"
src="https://github.com/user-attachments/assets/aee1477b-5059-4552-809e-7c8a34a9ec4a"
/>
## Summary
- replace static home card imagery with code-driven visuals for the
familiar interface, fast path, and live data cards
- refine the live data card interaction details, including hover states,
animated cursors, filter chips, table styling, and inline tag editing
- add shared company logos, people avatars, and updated partner
testimonial assets to support the new marketing visuals
- refresh related home, partner, case study, signoff, testimonials, and
design-system content/components to align with the updated marketing
presentation
## Testing
- `npx tsc -p tsconfig.json --noEmit` in `packages/twenty-website-new`
Co-authored-by: Abdullah <125115953+mabdullahabaid@users.noreply.github.com>
## Summary
Introduces a dedicated **metadata** mutation to update **standard
(non-custom)** workspace member settings, moves profile-related UI to
use it, and aligns **workspace member** record permissions with the rest
of the CRM so users cannot escalate visibility via RLS by editing their
own member record.
## Product behaviour
### Profile and appearance (standard fields)
- Users can still update **their own** standard workspace member fields
that the product exposes in **Settings / Profile** (e.g. name, locale,
color scheme, avatar flow) via the new
**`updateWorkspaceMemberSettings`** mutation.
- The mutation returns a **boolean**; the app **merges** the updated
fields into local state so the UI stays in sync without refetching the
full workspace member record.
- **Locale** changes also keep **`userWorkspace`** in sync when a locale
is present in the payload (including from the workspace `updateOne` path
when applicable).
### Custom fields on workspace members
- The dedicated metadata mutation **rejects** any **custom** workspace
member field (and unknown keys). Those updates must go through the
normal **object** `updateOne` pipeline, which is subject to **object-
and field-level** permissions like other records. But since we don't
have object- and field-level permission configuration for system objects
yet, this permission is derived from Workspace member settings
permission.
- **Workspace member** is no longer exempt from ORM permission
validation for updates merely because it is a **system** object. Users
who **do not** have workspace member access (e.g. no **Workspace
members** settings permission and no equivalent broad settings access on
the role) **cannot** use `updateOne` on `workspaceMember` to change
**custom** (or other) fields on their own row—even though that row is
used for RLS predicates.
- This closes a path where someone could widen what they can see by
writing to fields that drive row-level rules.
### Who can change another member
- Updating **another** user’s workspace member still requires
**Workspace members** (or equivalent) settings permission, consistent
with admin tooling.