Commit Graph
7962 Commits
Author SHA1 Message Date
Baptiste DevessierandGitHub ccd40e7633 feat: make feature flag image optional (#17679)
Prevent layout shift when no image is provided for a feature flag

## Before


https://github.com/user-attachments/assets/b1a98da9-1208-46f7-9fc6-9c34a9012c02

## After 


https://github.com/user-attachments/assets/ed12df0c-1651-4fc9-a0e7-60f5fb1533a4
2026-02-03 17:03:37 +00:00
3db961c038 i18n - translations (#17682)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-02-03 18:04:07 +01:00
Paul RastoinandGitHub 476bdf764c Refactor flat entity maps to be universal oriented (#17665)
# Introduction
In preparation of the workspace agnostic builder, we're migrating
`FlatEntityMaps` to be universal identifier oriented and based
As in the builder context there're won't be any ids at all
Please also note that the FlatEntity is a UniversalFlatEntity superset

From
```ts
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';

export type FlatEntityMaps<T extends SyncableFlatEntity> = {
  byId: Partial<Record<string, T>>;
  idByUniversalIdentifier: Partial<Record<string, string>>;
  universalIdentifiersByApplicationId: Partial<Record<string, string[]>>;
};
```

To
```ts
export type FlatEntityMaps<
  T extends SyncableFlatEntity | UniversalSyncableFlatEntity,
> = {
  byUniversalIdentifier: Partial<Record<string, T>>;
  universalIdentifierById: Partial<Record<string, string>>;
  universalIdentifiersByApplicationId: Partial<Record<string, string[]>>; // this might make more sense to be migrated to universalIdentifiersByApplicationUniversalIdentifier but it's the main topic of this PR
};
```

## Low level maps tools
Had to refactor find | create | delete | replace | find-many | get-sub
tools ( through mutations and or throw equivalent )
2026-02-03 16:16:02 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>DevessierDevessier
43042d55b2 Scaffold Fields widget edition (#17548)
https://github.com/user-attachments/assets/960b8b0d-10e8-42a1-bf61-e875359ea302

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
2026-02-03 15:56:41 +00:00
Raphaël BosiandGitHub 66d7182d02 Remote dom twenty UI POC (#17652)
Create a proof of concept which allows us to use twenty-ui in the
remote-dom.
For now only the button is allowed.


https://github.com/user-attachments/assets/e5441d2c-eb63-4b99-931b-86ee14621393

Known limitation: The icons are not yet available
2026-02-03 15:54:18 +00:00
WeikoandGitHub 5c91a96e84 Add position to page layout widgets (#17643)
## Context
Introducing a new position column in PageLayoutWidget. This is because
we already have gridPosition but all widgets are not supposed to fit in
a grid (see pageLayoutTab layoutMode (GRID, VERTICAL_LIST, CANVAS...),
position will now support the different layoutMode

## Implementation
- Adding the new column (not renaming to avoid breaking changes in the
dashboards)
- Add proper validation for each layout mode
- Update standard page layout to not always use GRID but rely on the tab
layout mode

<details>

<summary>Graphql Query</summary>

```graphql
{
  "data": {
    "getPageLayouts": [
      {
        "tabs": [
          {
            "title": "Tab 1",
            "layoutMode": "GRID",
            "widgets": [
              {
                "title": "Untitled Rich Text",
                "position": {
                  "column": 0,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 0,
                  "rowSpan": 6
                }
              },
              {
                "title": "Deals by Company",
                "position": {
                  "column": 6,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 0,
                  "rowSpan": 6
                }
              },
              {
                "title": "Pipeline Value by Stage",
                "position": {
                  "column": 0,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 6,
                  "rowSpan": 6
                }
              },
              {
                "title": "Revenue Timeline",
                "position": {
                  "column": 6,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 6,
                  "rowSpan": 6
                }
              },
              {
                "title": "Opportunities by Owner",
                "position": {
                  "column": 0,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 12,
                  "rowSpan": 6
                }
              },
              {
                "title": "Stock market (Iframe)",
                "position": {
                  "column": 6,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 12,
                  "rowSpan": 8
                }
              },
              {
                "title": "Deals created this month",
                "position": {
                  "column": 0,
                  "columnSpan": 3,
                  "layoutMode": "GRID",
                  "row": 18,
                  "rowSpan": 2
                }
              },
              {
                "title": "Deal value created this month",
                "position": {
                  "column": 3,
                  "columnSpan": 3,
                  "layoutMode": "GRID",
                  "row": 18,
                  "rowSpan": 2
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Tasks",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Tasks",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Notes",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Notes",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Emails",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Emails",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Calendar",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Calendar",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Tasks",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Tasks",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Notes",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Notes",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Emails",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Emails",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Calendar",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Calendar",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Tasks",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Tasks",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Notes",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Notes",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Emails",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Emails",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Calendar",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Calendar",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              },
              {
                "title": "Note",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 1
                }
              }
            ]
          },
          {
            "title": "Note",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Note",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              },
              {
                "title": "Note",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 1
                }
              }
            ]
          },
          {
            "title": "Note",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Note",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Flow",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Flow",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Flow",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Flow",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Flow",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Flow",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      }
    ]
  }
}
```

</details>
2026-02-03 15:54:01 +00:00
f009913cbe i18n - translations (#17678)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-02-03 17:01:31 +01:00
Charles BochetandGitHub 97867c11e6 Upgrade application model (#17673) 2026-02-03 16:44:00 +01:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>DevessierBaptiste Devessier
1a39cd6019 Disable initial animation in Toggle component (#17648)
## Before


https://github.com/user-attachments/assets/1d915d04-9917-4999-83ea-5fd2e97867e6

## After



https://github.com/user-attachments/assets/091c7e59-f545-43d7-b077-c502b9509799

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
2026-02-03 15:15:11 +00:00
nitinandGitHub c31ffef510 [Dashboard] fix rich text widget bugs + text selection in non edit mode (#17624)
closes -
https://discord.com/channels/1130383047699738754/1467809736715141211 and
https://discord.com/channels/1130383047699738754/1467810112453345333
 
before - 

text selection -- didn't work - 


https://github.com/user-attachments/assets/a12c5301-5eb1-4641-8386-87e534675c67

tripple click color picker bug - 


https://github.com/user-attachments/assets/8f2ebdd9-53c1-4635-8b28-6c82110b88a0


immediate cancel (before 300ms) - draft persist -- have to refresh to
get correct oncancel data



https://github.com/user-attachments/assets/d56e5738-d42f-4ba8-a9d3-b83acb8546ca



after - 
text selection - 


https://github.com/user-attachments/assets/e358035a-1424-45c5-a062-bb2ae6b6ca47

tripple click color picker bug - 


https://github.com/user-attachments/assets/d70947b0-b68b-4a85-93d4-8bed50e308fd



immediate cancel (before 300ms) - 


https://github.com/user-attachments/assets/f3cfb013-1d48-4f2d-b535-4b8bc5bb1c50



immediate save (before 300ms) - 



https://github.com/user-attachments/assets/3b017755-8523-429e-a511-f65732edec34
2026-02-03 15:10:34 +00:00
nitinandGitHub 19b56c5d3a [FRONT COMPONENTS] Frontend component widget creation (#17653)
closes https://github.com/twentyhq/core-team-issues/issues/2182



https://github.com/user-attachments/assets/babf154c-9cda-40ff-b2e8-5053e447c35f
2026-02-03 15:09:52 +00:00
Félix MalfaitandGitHub a1f26cda0d fix: increase findByText timeout for lazy-loaded DateTimePicker tests (#17672)
## Root Cause

The `DateTimePicker` component lazy-loads `react-datepicker` using
React's `lazy()` with a `Suspense` fallback that shows skeleton loaders:

```typescript
const ReactDatePicker = lazy<ComponentType<DatePickerPropsType>>(() =>
  import('react-datepicker').then(...)
);

// In render:
<Suspense fallback={<SkeletonLoader />}>
  <ReactDatePicker ... />
</Suspense>
```

On slower CI runners (GitHub Actions vs depot.dev), the lazy load takes
longer, causing tests to timeout while still showing skeletons instead
of the actual date picker content.

## Fix

Increased `findByText` timeout from the default 1000ms to 10000ms for
tests that wait for the date picker to load:

- `DateTimeFieldInput.stories.tsx` - 4 stories fixed
- `InternalDatePicker.stories.tsx` - 2 stories fixed

## Why This Wasn't Flaky Before

depot.dev runners have faster I/O and more consistent performance, so
the lazy load completed quickly. GitHub Actions runners have more
variable performance, causing the load to sometimes exceed the 1000ms
default timeout.
2026-02-03 15:44:58 +01:00
Thomas TrompetteandGitHub f2284579f0 Fix code container display (#17666)
Display is broken on code step
2026-02-03 14:12:37 +00:00
Charles BochetandGitHub 10ba8b9a97 Improve cache-clear behavior (#17662)
## Cache flush: scope and options

**Scope**
- `cache:flush` only flushes the **cache** Redis (REDIS_URL). It no
longer touches the queue Redis.

**Options**
- **`--namespace`** (optional): Flush one namespace or omit to flush
all. Valid: `module:messaging`, `module:calendar`, `module:workflow`,
`engine:workspace`, `engine:lock`, `engine:health`,
`engine:subscriptions`.
- **`--pattern`** (optional, default `*`): Key pattern inside the chosen
namespace(s).

**Troubleshooting**
- **`cache:flush:verify`**: Inserts keys inside and outside
`engine:workspace`, flushes, and checks only namespace keys are removed
(confirms flush scope).

**Usage**
- `yarn command:prod cache:flush` — flush all **KNOWN** namespaces
- `yarn command:prod cache:flush -n engine:workspace` — flush one
namespace
- `yarn command:prod cache:flush -n engine:workspace -p
"feature-flag:*"` — flush keys matching pattern in given namespace

FYI, existing namespaces:
```
export enum CacheStorageNamespace {
  ModuleMessaging = 'module:messaging',
  ModuleCalendar = 'module:calendar',
  ModuleWorkflow = 'module:workflow',
  EngineWorkspace = 'engine:workspace',
  EngineLock = 'engine:lock',
  EngineHealth = 'engine:health',
  EngineSubscriptions = 'engine:subscriptions',
}
```
2026-02-03 15:13:23 +01:00
Félix MalfaitandGitHub 9b063af7ab fix: increase RelationToOneFieldDisplay perf threshold to 0.4ms (#17671)
## Summary

Follow-up to #17656 - the `RelationToOneFieldDisplay` performance test
is still failing on GitHub Actions runners.

**Failure:** 0.313ms actual vs 0.3ms threshold

**Fix:** Increased threshold from 0.3ms → 0.4ms to provide more headroom
for runner variability.
2026-02-03 15:06:33 +01:00
Thomas TrompetteandGitHub 99aab9f40d Fix spaces in code step + suggestion overflow (#17664)
- fixedOverflowWidgets fixes the suggestions being overflow outside the
editor
- on focus, stop event propagation to avoid catching spaces as document
level events
2026-02-03 13:36:51 +01:00
martmullandGitHub 65678ed99f Upload files instead ofsources (#17608) 2026-02-03 12:11:10 +01:00
MarieandGitHub cdaa8f1d9d Allow twenty standard app to access messaging items (#17659) 2026-02-03 11:40:21 +01:00
Thomas des FrancsGitHubclaude[bot] <41898282+claude[bot]@users.noreply.github.com>Thomas des Francs
963066c7e3 fix: use red3 for dark mode danger background color (#17658)
## Summary

Fixes the dark mode error chip background color issue by changing
`background.danger` from `red12` to `red3` in the dark theme constants.

Fixes #17657

Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Thomas des Francs <Bonapara@users.noreply.github.com>
2026-02-03 11:00:28 +01:00
Félix MalfaitandGitHub 2826540bb1 fix: increase performance test thresholds for GitHub Actions runners (#17656)
## Summary

After migrating from depot.dev runners to GitHub Actions runners, three
performance tests are failing due to slower single-threaded performance
on the new infrastructure (even with 16 cores).

## Problem

The performance tests use React's `<Profiler>` API to measure component
render times. depot.dev runners have better single-core performance and
lower memory latency compared to GitHub Actions runners, even larger
ones.

Failed tests:
| Test | Threshold | Actual | Overage |
|------|-----------|--------|---------|
| `DateTimeFieldDisplay` | 0.2ms | 0.281ms | +41% |
| `ChipFieldDisplay` | 0.2ms | 0.222ms | +11% |
| `RelationToOneFieldDisplay` | 0.22ms | 0.237ms | +8% |

## Solution

Increased thresholds to account for GitHub Actions runner performance:

- `DateTimeFieldDisplay`: 0.2ms → 0.35ms
- `ChipFieldDisplay`: 0.2ms → 0.3ms  
- `RelationToOneFieldDisplay`: 0.22ms → 0.3ms

## Future Considerations

For a more robust long-term solution, we could implement baseline
comparison that:
1. Saves performance results on main branch builds
2. Compares PR results against baseline with tolerance (±25%)
3. Self-calibrates to actual runner performance

This would catch real regressions without being sensitive to
infrastructure differences.
2026-02-03 10:03:38 +01:00
Paul RastoinandGitHub d35d5c0463 [BREAKING_CHANGE] Deprecate remaining entities standardId (#17639)
# Introduction
Following https://github.com/twentyhq/twenty/pull/17632 and
https://github.com/twentyhq/twenty/pull/17572
This PR deprecates the agent, skill, field metadata and role
`standardId` in favor of the `universalIdentifier` usage

## Note
- Removed previous standard ids declaration modules
- Twenty-sdk now re-exports the `STANDARD_OBJECTS` universalIdentifier
hashmap constant
- deleted some sync-metadata deadcode too ( mainly types )
2026-02-03 09:06:24 +01:00
cfdcc0065c Migrate workflow serverless to logic (#17646)
Migrations commands

---------

Co-authored-by: Charles Bochet <charles@macbook-pro-de-charles-1.home>
2026-02-02 19:39:20 +01:00
Thomas TrompetteandGitHub fd47d5a1a9 Silent errors on code step build (#17651)
As title. Temporary to unblock push to prod
2026-02-02 19:23:14 +01:00
Raphaël BosiandGitHub 7dd8d573ed Fix build docker image error (#17649)
The docker image build is failing after
https://github.com/twentyhq/twenty/pull/17587.
This error happens because now twenty-front now depends on twenty-sdk.
2026-02-02 17:43:46 +00:00
5f0ec8ed9e i18n - translations (#17650)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-02-02 18:45:38 +01:00
MarieandGitHub 7b48efb5d6 Fix creation of objects with acronym names (e.g. "O&J") (#17633)
Fixes https://github.com/twentyhq/twenty/issues/17544

**Problem**
When users create custom objects with short acronym names like "O&J",
the system generates an object name oJ. When creating relation fields,
the morph field name was built using string concatenation:
const morphFieldName = `target${capitalize("oJ")}`; // → "targetOJ"
This produced "targetOJ", which failed validation because the camelCase
check performed in `validateFlatFieldMetadataName` (camelCase(name) ===
name) returns "targetOj" for "targetOJ". The issue comes from
consecutive camelCase() operations.

**Solution**
Actually, the `camelCase(name) === name` check is questionnable. 
What we want to check is that a name is in camelCase format, not that it
corresponds to the camelCase version of a given string, while that's we
are doing here. lodash does not provide camelCase validator, only
camelCase convertor, so we used it as a way to validate the format of
the name.
We may feel like `camelCase(name) === name` checks whether a name is
camel-cased, but in addition to that it is also checking for a camel
case "idempotency" we don't necessarily have and do not need: for
instance if an object's name is "iOS" (which could be inferred from a
label "I O S"), it won't pass the check: camelCase("iOS") is "ios" and
"ios" !== "iOS".

The existing check with
`STARTS_WITH_LOWER_CASE_AND_CONTAINS_ONLY_CAPS_AND_LOWER_LETTERS_AND_NUMBER_STRING_REGEX`
acts as a camel case validator, so we don't need that camelCase() check.
2026-02-02 17:11:20 +00:00
Paul RastoinandGitHub 75921e79bf [FIXES_MAIN] Remove objectMetadata standardId (#17632)
# Introduction
In this PR we're deprecating the object metadata standard id and
replacing it to the universalIdentifier usage
As we've totally removed its insertion for both new field and object in
https://github.com/twentyhq/twenty/pull/17572

## Note
- Removed upgrade commands before `1.17`
2026-02-02 14:26:27 +00:00
48b4127afa i18n - docs translations (#17634)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-02-02 15:40:19 +01:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>DevessierBaptiste Devessier
28763428f6 Differentiate edit mode behavior for record pages vs dashboards (#17550)
Temporary disable tabs edition for record page layouts


https://github.com/user-attachments/assets/c1f5d7fd-f125-4fb0-bf9c-96fadec14cd2

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
2026-02-02 13:28:51 +00:00
nitinandGitHub b643fbe425 [Dashboards] Auto focus effect on standalone widget (#17623)
closes
https://discord.com/channels/1130383047699738754/1467809255771078719

before - 



https://github.com/user-attachments/assets/3af6dbfd-358c-44fd-9419-338791fd5cfc


after - 



https://github.com/user-attachments/assets/2e8f89f8-5989-4e62-9bc0-6ddd47c0d421
2026-02-02 13:14:27 +00:00
Thomas des FrancsandGitHub 7f33286274 Update discord link (#17627) 2026-02-02 13:03:13 +00:00
492847a693 i18n - translations (#17626)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-02-02 14:12:14 +01:00
Paul RastoinandGitHub bd9688421f ObjectMetadata and FieldMetadata agnostic workspace migration runner (#17572)
# Introduction
Important note: This PR officially deprecates the `standardId`, about to
drop col and entity property after this has been merged

Important note2: Haven't updated the optimistic tool to also update the
universal identifier aggregators only the ids one, they should not be
consumed in the runner context -> need to improve typing or either the
optimistic tooling

In this PR we're introducing all the devxp allowing future metadata
incremental universal migration -> this has an impact on all existing
metadata actions handler ( explaining its size )
This PR also introduce workspace agnostic create update actions runner
for both field and object metadata in order to battle test the described
above devxp

Noting that these two metadata are the most complex to handle

Notes:
- A workspace migration is now highly bind to a
`applicationUniversalIdentifier`. Though we don't strictly validate
application scope for the moment

## Next
Migrate both object and field builder to universal comparison

## Universal Actions vs Flat Actions Architecture

### Concept

The migration system uses a two-phase action model:

1. **Universal Actions** - Actions defined using `universalIdentifier`
(stable, portable identifiers like `standardId` + `applicationId`)
2. **Flat Actions** - Actions defined using database `entityId` (UUIDs
specific to a workspace)

### Why This Separation?

- **Universal actions are portable**: They can be serialized, stored,
and replayed across different workspaces
- **Flat actions are executable**: They contain the actual database IDs
needed to perform operations
- **Decoupling**: The builder produces universal actions; the runner
transpiles them to flat actions at execution time

### Transpiler Pattern

Each action handler must implement
`transpileUniversalActionToFlatAction()`:

```typescript
@Injectable()
export class CreateFieldActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
  'create',
  'fieldMetadata',
) {
  override async transpileUniversalActionToFlatAction(
    context: WorkspaceMigrationActionRunnerArgs<UniversalCreateFieldAction>,
  ): Promise<FlatCreateFieldAction> {
    // Resolve universal identifiers to database IDs
    const flatObjectMetadata = findFlatEntityByUniversalIdentifierOrThrow({
      flatEntityMaps: allFlatEntityMaps.flatObjectMetadataMaps,
      universalIdentifier: action.objectMetadataUniversalIdentifier,
    });
    
    return {
      type: action.type,
      metadataName: action.metadataName,
      objectMetadataId: flatObjectMetadata.id, // Resolved ID
      flatFieldMetadatas: /* ... transpiled entities ... */,
    };
  }
}
```

### Action Handler Base Class

`BaseWorkspaceMigrationRunnerActionHandlerService<TActionType,
TMetadataName>` provides:

- **`transpileUniversalActionToFlatAction()`** - Abstract method each
handler must implement
- **`transpileUniversalDeleteActionToFlatDeleteAction()`** - Shared
helper for delete actions

## FlatEntityMaps custom properties
Introduced a `TWithCustomMapsProperties` generic parameter to control
whether custom indexing structures are included:

- **`false` (default)**: Returns `FlatEntityMaps<MetadataFlatEntity<T>>`
- used in builder/runner contexts
- **`true`**: Returns the full maps type with custom properties (e.g.,
`byUserWorkspaceIdAndFolderId`) - used in cache contexts

## Create Field Actions Refactor

Refactored create-field actions to support relation field pairs
bundling.

**Problem:** Relation fields (e.g., `Attachment.targetTask` ↔
`Task.attachments`) couldn't resolve each other's IDs during
transpilation because they were in separate actions with independent
`fieldIdByUniversalIdentifier` maps.

**Solution:** 
- Removed `objectMetadataUniversalIdentifier` from
`UniversalCreateFieldAction` and `objectMetadataId` from
`FlatCreateFieldAction` - each field now carries its own
- Runner groups fields by object internally and processes each table
separately
- Split aggregator into two focused utilities:
- `aggregateNonRelationFieldsIntoObjectActions` - merges non-relation
fields into object actions
- `aggregateRelationFieldPairs` - bundles relation pairs with shared
`fieldIdByUniversalIdentifier`
2026-02-02 12:22:38 +00:00
Raphaël BosiandGitHub f0bc9fcb43 [FRONT COMPONENTS] Move to twenty-sdk (#17587)
Move front components from twenty-shared to twenty-sdk
2026-02-02 12:21:53 +00:00
749bda92e3 feat: lazy dev env setup for Claude CI workflow (#17621)
## Summary
- Move build/env/DB setup out of the GitHub Actions workflow into an
on-demand script (`packages/twenty-utils/setup-dev-env.sh`) that Claude
invokes only when needed
- Add Nx/build cache restore/save steps to speed up repeated runs
- Add `allowed_tools` so Claude can run the setup script and common dev
commands (nx, jest, yarn)
- Add `PG_DATABASE_URL` env var via `settings` for the postgres MCP
server
- Remove unnecessary `actions: read` permission grant
- Document the lazy setup pattern in CLAUDE.md

This makes read-only tasks (code review, architecture questions, docs)
fast by skipping the ~2min build/setup overhead, while still letting
Claude run tests and builds when it needs to.

## Test plan
- [ ] Comment `@claude what is the architecture of the backend?` —
should answer fast without running setup
- [ ] Comment `@claude run the twenty-server unit tests` — should call
setup script first, then run tests
- [ ] Verify cross-repo dispatch still works

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 12:43:47 +01:00
Lucas BordeauandGitHub f6e182ac23 Handle SSE event stream reconnection (#17523)
This PR handles SSE event stream edge cases. 

- When a pod restarts, the front clients have to reconnect to SSE
- When the dev server restarts or is hot reloaded, the front client has
to reconnect to SSE
- When redis server restarts or the redis key is cleared for any reason,
the server has to recreate the event stream in redis, this can happen
when navigating for example.
- Log in / log out flow

With this PR we avoid error messages in the front end due to TTL or pod
crash, we implement a resilient way of reconnecting silently.

To avoid DDoSing our servers if pods crash or a full restart of the
cluster is made, we evenly space retry attempts to reconnect from all
the clients, to avoid n clients reconnection at the same time, we use a
random wait time between 0 and a constant max wait time (set to 2 mins
for now). This is the cheapest and most effective solution, clients who
want to force reconnect have to refresh or navigate to another page.

Fixes https://github.com/twentyhq/core-team-issues/issues/2045
2026-02-02 11:16:28 +00:00
82619d9e66 i18n - translations (#17620)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-02-02 12:25:29 +01:00
MarieandGitHub 54cf857a1a Revert "fix: prevent default object re-selection in relation field fo… (#17619)
I suggest to revert [this
PR](https://github.com/twentyhq/twenty/pull/17313) as it had already
been fixed [in this
PR](https://github.com/twentyhq/twenty/pull/17209/changes) (which fixes
more than it says in its title). Issue was tracked by [this
ticket](https://github.com/twentyhq/core-team-issues/issues/2049) not
very explicit - sorry, my fault.
Code added in the PR does not add value
2026-02-02 10:51:11 +00:00
Abdul RahmanandGitHub c9d5f48ecc Fix: Favorite records not showing in sidebar (#17614)
## Problem
Favorite records (`NavigationMenuItems`) were not showing in the sidebar
under Favorites as the BE was returning `null` for
`targetRecordIdentifier`.
## Root cause
The `targetRecordIdentifier` resolver used `context.req`, which does not
have the typed `WorkspaceAuthContext` shape.
## Fix
Use `getWorkspaceAuthContext()` instead of `context.req` so the resolver
receives the typed auth context.
2026-02-02 10:33:50 +00:00
fb97c40cad [Dashboards] Replace nivo bar chart with custom canvas bar chart (#17441)
before - 


https://github.com/user-attachments/assets/01d1ce73-1732-4516-bde0-d43c1bbcb734

after - 
I got rid of line chart in the after clip -- because now its the line
chart thats more laggy :) -- but now could be easily migrated away from
nivo


https://github.com/user-attachments/assets/430f4697-68cd-47be-b63e-f8df34a2ee0e

stress test - 

before - 



https://github.com/user-attachments/assets/c3d3e05c-943e-48dc-9429-a2ecf41cd4fe



after - 




https://github.com/user-attachments/assets/98b35a43-f918-4a46-9b66-3bc8deabfdb8

---------

Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
2026-02-02 07:42:20 +00:00
1976ad58c4 i18n - docs translations (#17599)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-31 14:22:36 +01:00
be8629d8f6 i18n - docs translations (#17597)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-30 22:07:41 +01:00
Charles BochetandGitHub 46d28509b9 Keep simplifying logic functions (#17595)
## Summary

Refactors the `LogicFunctionService` API by consolidating v1 and v2
services:

In metadata-module (presentation layer module)
- **Renamed methods**: `deleteOneLogicFunction` → `destroyOne`,
`updateOneLogicFunction` → `updateOne`, `createOneLogicFunction` →
`createOne`
- **Added duplicate methods**: `duplicateLogicFunction`,
`createLogicFunctionFromExistingLogicFunctionById`
- **Removed soft delete/restore** functionality - only hard delete
(`destroyOne`) is supported

In core-module (lower level module)
- **Moved execution methods** to `LogicFunctionExecutorService` which is
lower level: `executeOneLogicFunction`, `getAvailablePackages`,
`getLogicFunctionSourceCode`
2026-01-30 20:30:52 +01:00
db31b83a86 fix: allow board column tag to shrink so aggregate value remains visible (#17372)
## Summary
When a select option label is long, it was pushing the aggregate value
(e.g., '12.6m') out of view because the tag had `flex-shrink: 0`.

Changed to `min-width: 0` and `overflow: hidden` to allow the tag to
shrink and truncate with ellipsis, keeping the aggregate value visible.

## Changes
- Modified `StyledTag` in
[RecordBoardColumnHeader.tsx](cci:7://file:///root/74/bronze/twenty/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx:0:0-0:0)
to allow shrinking

The
[Tag](cci:1://file:///root/74/bronze/twenty/packages/twenty-ui/src/components/tag/Tag.tsx:100:0-141:2)
component already has built-in text truncation with
`OverflowingTextWithTooltip`, so this change enables that functionality
to work properly.

Fixes #17350
```**

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2026-01-30 18:35:52 +00:00
394b6f5717 i18n - translations (#17594)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-30 19:43:46 +01:00
a97ca14289 i18n - docs translations (#17593)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-30 19:28:32 +01:00
Charles BochetandGitHub 4a770eafa1 Rework logic function module (#17588)
core-modules/logic-function/
├── logic-function.module.ts
├── logic-function-executor/
│   ├── logic-function-executor.module.ts
│   ├── commands/
│   │   └── add-packages.command.ts
│   ├── constants/
│   │   └── logic-function-executor.constants.ts
│   ├── factories/
│   │   └── logic-function-module.factory.ts
│   ├── interfaces/
│   │   └── logic-function-executor.interface.ts
│   └── services/
│       └── logic-function-executor.service.ts
├── logic-function-build/
│   ├── logic-function-build.module.ts
│   ├── services/
│   │   └── logic-function-build.service.ts
│   └── utils/
│       └── get-logic-function-base-folder-path.util.ts
├── logic-function-drivers/
│   ├── logic-function-drivers.module.ts
│   ├── constants/
│   │   └── ...
│   ├── drivers/
│   │   ├── disabled.driver.ts
│   │   ├── lambda.driver.ts
│   │   └── local.driver.ts
│   ├── interfaces/
│   │   └── logic-function-executor-driver.interface.ts
│   ├── layers/
│   │   └── ...
│   └── utils/
│       └── ...
├── logic-function-layer/
│   ├── logic-function-layer.module.ts
│   └── services/
│       └── logic-function-layer.service.ts
└── logic-function-trigger/
    ├── logic-function-trigger.module.ts
    ├── jobs/
    │   └── logic-function-trigger.job.ts
    └── triggers/
        ├── cron/
        ├── database-event/
        └── route/
            ├── exceptions/
            ├── services/
            │   └── route-trigger.service.ts
            └── utils/
2026-01-30 19:28:20 +01:00
cb5e5e4622 i18n - translations (#17592)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-30 19:09:43 +01:00
MarieandGitHub 0001c2e7d0 [Apps] Apps marketplace (first draft) (#17562)
https://github.com/user-attachments/assets/c4e63edb-6e98-44bc-841f-ee110ae712d4

How it works
- `manifest.json` files are now committed when apps are published to our
repo
- to display available apps, from the server, we read into our github
repo, using a pod-scoped cache
- feature flagged
- app installation will be behind permission gate MARKETPLACE_APPS

Limitations and what is yet to develop
- content and settings tabs
- installed apps tab
- app installation
- test and potentially fix reading from .manifest.json once [Reshape
manifest
structure](https://github.com/twentyhq/core-team-issues/issues/2183) is
done. additional work is expected on assets notably. (couldnt properly
do it here as manifest.json will only be committed after this pr)
- we only read in community/ folder for now - we may want tochange that
- the cache is rather artisanal for now and scoped by pod - we may want
to change that
2026-01-30 17:32:32 +00:00
36cf388c81 i18n - translations (#17591)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-30 18:39:54 +01:00