Commit Graph

13168 Commits

Author SHA1 Message Date
Raphaël Bosi cb49a7a053 Add v2 onboarding loading screen while creating workspace (#22152)
https://github.com/user-attachments/assets/cc7b1d10-7495-4f21-9311-4c22c0f14771

Adds the full-screen loading screen shown while a new workspace is being
created in the v2 sign-up flow (`SignInUpV2`), building on the v2
"Create your workspace" step.

How it works:
- Submitting the v2 create-workspace form marks the flow as v2
(`isOnboardingV2State`) and creates the workspace. The flag is carried
across the cross-subdomain redirect with an `onboardingV2=true` URL
param, so v2 users land on a new `/workspace-activation-v2` route
instead of v1's `/workspace-activation`.
- `WorkspaceActivationV2` runs the real `activateWorkspace` mutation on
mount and renders the loader: a pulsing Twenty logomark above a stack of
status messages that shift up one at a time, cycling once per second.
There is no faked/minimum duration; it advances to the next onboarding
step as soon as the workspace is activated.
- On activation failure it shows a "Workspace creation failed" screen
with a Retry button.

v1 onboarding is unchanged. Storybook:
`Modules/Auth/SignInUpWorkspaceActivationV2`.

Note: The flashes will be fixed in later PRs

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22152?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-26 08:46:34 +00:00
Akash! ce4d0f3447 fix: hide "Create Workspace" button when multi-workspace is disabled (#22202)
## Description

This PR fixes a bug where the "Create Workspace" button was
unconditionally rendered in the workspace switcher dropdown, even when
single-workspace mode was active (`IS_MULTIWORKSPACE_ENABLED=false`).

This created a confusing "dead-end" action for users, as clicking the
button would do nothing (because the backend correctly blocks workspace
creation in this mode, and the frontend skips the redirect).

### Changes made
- Imported the `isMultiWorkspaceEnabledState` atom from client-config.
- Evaluated `isMultiWorkspaceEnabled` inside
`MultiWorkspaceDropdownDefaultComponents`.
- Conditionally rendered the "Create Workspace" `<MenuItem>` only if
multi-workspace is enabled.

Closes #22139


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22202?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-26 10:42:10 +02:00
Etienne b625bd1995 fix(ai-chat) - improvements (#22193)
- remove flickering at assistant message streamed end
- add copy code
- leave chat history when navigating to settings

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22193?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-26 10:26:40 +02:00
github-actions[bot] e60d790990 i18n - website translations (#22203)
Created by Github action

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22203?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-26 10:25:37 +02:00
Thomas des Francs 9b57c5dfac Set record card header height and calendar spacing (#22187)
## Summary

Sets the shared `RecordCardHeaderContainer` height to `32px`, so board
and calendar card headers use the same common header size.

Updates the board fetch-more card-height estimate to use the same `32px`
header value.

Reduces the calendar card header/content gap by removing the body top
padding while keeping the existing side and bottom padding.

## Screenshots

Before:


![Before](https://raw.githubusercontent.com/twentyhq/twenty/bonapara/pr-assets-kanban-card-header-auto-height/.github/pr-screenshots/kanban-card-header-auto-height/before.jpg)

After:


![After](https://raw.githubusercontent.com/twentyhq/twenty/bonapara/pr-assets-kanban-card-header-auto-height/.github/pr-screenshots/kanban-card-header-auto-height/after.jpg)

## Validation

- Browser verification on
`http://apple.localhost:3001/objects/opportunities?viewId=0433d066-dda7-4b2c-89e5-04d4792d193c`:
visible calendar card body padding computes to `0px 4px 4px`
- Browser verification on board view: visible board card headers compute
to `32px`
- Browser console errors: none
- `git diff --check`
- `prettier --write` on changed files
- `oxlint --type-aware` on changed files in the running checkout
2026-06-26 10:19:24 +02:00
Abdullah. 85f64abb28 Frameless ProductStepper with twenty-front-faithful scenes (#22197)
## What

Reworks the website's **ProductStepper** (the scroll-driven *Data model
/ Automation / Layout* section) to be frameless and to render all three
scenes faithfully to twenty-front — real icons, labels, colors,
structure, and connectors.

## Changes

**Frameless + scaling**
- Removed the shared white card/header; the three scenes now sit
directly on the dark dot-grid stage.
- Unified every scene on one `StageFit` primitive (fixed design box →
scaled to fit), pixel-identical at full width and uniform on smaller
screens.

**Data model scene**
- Real object schema (3 Standard + 2 Custom objects, real relation
fields); replaced an invented "Investors" object with the real
**Employment History** custom object (`IconBriefcase`, Company + Person
relations) from the server seed.
- Clean spanning-tree connections; removed the Standard/Custom badge;
fixed card sizing + edge centering.

**Workflow scene**
- Real action labels + a logical flow: *Record is Created → Filter →
Search Records → AI Agent → (Update Record · Send Email · Create
Record)*. Dropped the iterator (a loop construct shown without a loop
body).
- Per-action icon colors matching twenty-front (trigger blue, flow
green, record gray, send-email red, AI agent pink) on a gray tile.
- Rebuilt the node to twenty-front's real anatomy and the connectors
(source circle → `getBezierPath` → arrow marker) verbatim.

**Layout scene**
- Real workspace sidebar: real Tabler object icons, exact labels, the
true default sidebar (6 objects + Workflows folder),
`getIconTileColorShades` tile colors; dropped invented entries.
- Record overview + Fields editor with correct field-type labels (Links,
True/False, Date and Time); legibility + spacing tuning.

**Icons** — replaced every hand-drawn approximation with real
`@tabler/icons-react` / twenty-front object icons.

**Misc** — smoother step-to-step transitions (translate + easing
tokens).

## Testing
Marketing visual; `lint` / `typecheck` / `build` green, and each scene
reviewed visually against twenty-front.




https://github.com/user-attachments/assets/997e1b95-55c0-401a-93a4-c70545577057
2026-06-26 10:17:44 +02:00
martmull b49225df4b Reorder validation execution to match migration action order (#22200)
## Summary
Reorders the validation execution sequence in the workspace entity
migration builder to match the actual execution order of migration
actions (delete → create → update). This ensures that optimistic entity
maps accurately simulate the post-migration state during validation.

## Key Changes
- **Moved creation validation before update validation** in
`WorkspaceEntityMigrationBuilderService`: Creation validation now
executes immediately after deletion validation, allowing updates to
reference entities created in the same migration without validators
needing to peek into to-be-created maps.

- **Removed `remainingFlatEntityMapsToValidate` parameter from update
validation**: Since creation validation now completes before update
validation begins, the optimistic maps already contain all created
entities. Updates can safely reference newly created entities through
the optimistic maps without needing access to remaining-to-create maps.

- **Simplified `FlatNavigationMenuItemValidatorService`**: Removed the
logic that combined remaining-to-create maps with optimistic maps, now
relying solely on the optimistic maps which contain all previously
validated creations.

- **Updated type definition**: Modified `FlatEntityUpdateValidationArgs`
type to exclude `remainingFlatEntityMapsToValidate` since it's no longer
needed.

## Implementation Details
This change enables a more intuitive validation flow where:
1. Deletions are validated first
2. Creations are validated next (in topological order for
self-referential FKs)
3. Updates are validated last (can safely reference newly created
entities)

The optimistic maps are progressively built during creation validation,
so by the time update validation runs, they faithfully represent the
post-migration state, eliminating the need for validators to access
separate remaining-to-create maps.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22200?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-26 10:17:00 +02:00
Mani bharadwaj 7e48d36c98 fix(twenty-front): apply object type translations to details panel relation labels (#22090)
## Problem

When users customize or translate object type names (e.g. "Company" →
"Unternehmen" in German), the translated/customized names do **not**
appear in the details panel. The default English names still show
instead.

This is because the frontend's relation metadata only carried
`nameSingular`/`namePlural` (internal API identifiers), not
`labelSingular`/`labelPlural` (user-facing display labels). Components
that display relation names had no choice but to use the internal
identifiers.

Fixes #19790

## Changes

### Data layer — add labels to the pipeline
- **GraphQL fragment** (`fragment.ts`): Added
`labelSingular`/`labelPlural` to `sourceObjectMetadata` and
`targetObjectMetadata` in both `relation` and `morphRelations`
- **Type** (`FieldMetadataItemRelation.ts`): Extended the `Pick` type to
include `labelSingular`/`labelPlural`
- **Field metadata type** (`FieldMetadata.ts`): Added
`relationObjectMetadataLabelSingular`/`LabelPlural` to
`FieldRelationMetadata`
- **Mapping** (`formatFieldMetadataItemAsFieldDefinition.ts`): Maps the
new label fields with `label ?? name` fallback for backwards
compatibility

### Display layer — use labels for user-facing text
- **RecordDetailRelationRecordsListItem**: Uses
`relationObjectMetadataLabelSingular` for delete confirmation dialog
title, subtitle, and button text (falls back to `nameSingular`)
- **RecordDetailRelationRecordsList**: Threads `objectLabelSingular`
prop through
- **RecordDetailRelationSection**: Passes `labelSingular ??
nameSingular` from the looked-up object metadata
- **FieldWidgetRelationCard**: Passes
`relationObjectMetadataLabelSingular` from field metadata
- **FieldWidgetJunctionRelationCard**: Passes `labelSingular ??
nameSingular` from object metadata lookup
- **FieldWidgetMorphRelationCard**: Passes label from morph relation
hook result
- **useGetMorphRelationRelatedRecordsWithObjectNameSingular**: Carries
`labelSingular` from matched morph relation

### Test data
- Updated story/mock files with
`relationObjectMetadataLabelSingular`/`LabelPlural` fields
- Updated `SettingsDataModelRelationFieldPreview` with label fields in
morph relation objects

## Design decisions

- **Backwards compatible**: All new props are optional. Every display
usage uses `label ?? name` fallback, so if `labelSingular` isn't
available yet (e.g. before GraphQL regeneration), it falls back to the
old behavior
- **Lookup vs display separation**: `nameSingular` continues to be used
for lookups, routing, and GraphQL queries (it's the identifier).
`labelSingular` is only used for user-facing display text
- **Minimal scope**: Only changes the display paths identified in the
bug report — confirmation dialogs and relation labels in the details
panel

## Test plan

1. Set workspace language to a non-English locale (e.g. German)
2. Navigate to a record with relation fields
3. Verify relation section titles and labels show translated names
4. Try to delete a related record — verify the confirmation dialog uses
the translated name
5. Switch language back to English — verify everything still works
correctly

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22090?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-26 10:10:55 +02:00
Parship Chowdhury b3e39e2198 fix: relative date picker calendar display (#21895)
Part of
https://github.com/twentyhq/twenty/issues/19739#issuecomment-4652034526
(Bug 1-3). Maybe it feels like theses bugs are not actually bugs, but we
can maybe say it as UX improvements: specially needed in case when an
user will choose any past options.

### Bug 1: calendar open on wrong month
With Is Relative (e.g. Past 1 Quarter), the calendar opened on today’s
month instead of the range start. After the fix, it now opens on the
first month of the filtered range.

**Testing:**
View filter → Date field → Is Relative → Past 1 Quarter. Calendar opens
on January (range start), not today’s month


https://github.com/user-attachments/assets/8849d00a-4d5c-4f8a-8d31-3a62535eb311


### Bug 2: Dates not highlighted
Ranges older than ~2 months (e.g. Q1 when today is June) showed no
highlighted days. Highlighting now covers the full resolved range.

**Testing:**
Same setup: past 1 Quarter on a date when Q1 is outside the old 2‑month
window. Jan 1 - Mar 31 will highlight.


https://github.com/user-attachments/assets/d21e2272-c923-4493-80ff-bdf4228842b1


### Bug 3: No month navigation
Relative mode only showed Past - 1 - Quarter controls with no way to
browse months. Now see the new arrows move through months without
changing the filter.

<img width="377" height="455" alt="Screenshot 2026-06-20 181107"
src="https://github.com/user-attachments/assets/eb51feb9-af10-489a-b166-8b8d6c642e05"
/>


> [!NOTE]
> 1. We can't do the fixes by one by one, i have to fix them within one
PR because all the fixes are inter-related, like we can't test the bug 1
fix alone without implementing bug 3.
> 2. Bug 4 will be done in a separate PR which is actually the issue
#19739. See
https://github.com/twentyhq/twenty/issues/19739#issuecomment-4652034526
for better understanding.
> 3. If you see the screen recordings, they are actually done with the
alignment fixes from #21881 . So without that changes you will see the
alignmemt issues in the calendar grid in your local.

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2026-06-26 10:03:21 +02:00
Marie 6e2df0654b [Workflows] Allow iterator to take whole item as variable (#22031)
**Select the whole item in iterator loops, and iterate over a step's
array output**
## Summary
Two related improvements to working with lists in workflows:
- Pick the current item as a whole inside an iterator loop. Previously,
in a node inside the loop, you could only reference individual fields of
the Iterator's current item. Now you can select the whole item (e.g. a
full record) — useful for passing it straight into a downstream step.
<img width="1270" height="744" alt="Screenshot 2026-06-23 at 17 02 47"
src="https://github.com/user-attachments/assets/6b92e72e-ec25-4c1a-9841-3a438210e753"
/>
- Iterate over a step's array output. A Code / Logic Function step that
returns a top-level array couldn't be fed to the Iterator: its output
was flattened into indexed entries (0, 1, …) with no way to select the
array as a whole. A new "Whole list" option selects the step's entire
output, and the Iterator infers the per-iteration item shape from it.
<img width="1026" height="728" alt="Screenshot 2026-06-23 at 17 17 53"
src="https://github.com/user-attachments/assets/db07dcd8-4fb8-4db9-8b45-aa56051d9f3b"
/>


Together these complete the loop ergonomics: select a list → iterate →
reference the current item (whole or by field) downstream — matching the
model used by tools like Windmill.

## What changed
- The variable picker offers a "Use the whole item" option when viewing
an iterator's current item, and a "Whole list" option when a step
returns a top-level array.
- The Iterator's current-item schema can now be inferred from a variable
pointing at a step's whole output.

## Risks for existing workflows
None expected. The change is purely additive:
- No DB migration and no change to how output schemas are stored or read
— existing schemas, variables, and iterators behave identically.
- No change to runtime variable resolution; existing {{step.field}} and
current-item references are untouched.
- The new options only apply to new selections (whole item / whole
list); all existing paths take the unchanged code path.
- The only edge case: array detection is heuristic (an output whose keys
are exactly 0…n-1), so an object that happens to have those keys would
also show "Whole list". This is rare for real outputs, affects nothing
unless a user selects it, and fails safe — the Iterator validates its
input and throws a clear "items must be an array" error if a non-array
is passed.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22031?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-26 09:26:11 +02:00
Félix Malfait ea9e11581c feat(billing): replace Stripe trial emails with fair, well-timed reminders (#22186)
## Why

We currently rely on Stripe's automated trial-ending email. It misfires:
the global "remind 7 days before trial ends" setting lands the reminder
on **signup day** for the 7‑day no‑card trial, and the "your card will
be charged" copy makes no sense for a trial with no card. This replaces
it with our own honest, well‑timed, Twenty‑branded emails.

## 🔒 Safety — these emails are OFF by default

Because these reach real customers, the whole feature is gated behind a
kill‑switch that **defaults to `false`**:

- **`BILLING_REMINDER_EMAILS_ENABLED` (default `false`)** — checked
**both** at cron registration **and** on every job run (defense in
depth), so the emails can never be sent inadvertently (not on deploy,
not in staging, not via a stray trigger). They only go out once an
operator explicitly opts in.
- Also gated on `IS_BILLING_ENABLED` (cloud‑only; self‑hosters
unaffected).
- In non‑prod the email driver is typically `logger`, so even if enabled
there, nothing is actually sent.

A unit test asserts that with the switch off, **zero** emails are
produced.

## What it does

A daily cron (`0 8 * * *`) sends three honest, Twenty‑branded emails:

| Plan | Email | When |
|---|---|---|
| No‑card trial (7d) | "Add a card to keep your data" | **1 day before**
trial ends |
| Card‑on‑file trial (30d) | Upcoming‑charge heads‑up (cancel in one
click) | **7 days before** first charge |
| Yearly subscription | Renewal reminder (no surprise) | **7 days
before** each renewal |

- **Monthly renewals get no reminder** (avoids noise) — only the first
charge and annual renewals do.
- Branches no‑card vs with‑card on the customer's payment‑method flag
(with a trial‑duration fallback), so someone who adds a card mid‑trial
correctly gets the charge heads‑up instead of the add‑a‑card one.
- **Idempotent** per `(workspace, boundary date)` via workspace‑level
user vars — yearly reminders re‑fire each period, but the daily cron
never double‑sends.
- Offsets are configurable via new `BILLING_*_REMINDER_DAYS_BEFORE`
variables.

Also **warms up the tone** of the existing suspended / deleted workspace
emails (less robotic, fair, loss‑aversion framing) — these already act
as the "come back or lose your data" win‑back, so no extra win‑back
email was added.

## Rollout

1. Merge.
2. Disable Stripe's automated trial/renewal customer emails in the
Stripe dashboard.
3. Review copy/timing, then set `BILLING_REMINDER_EMAILS_ENABLED=true`
to turn the cron on.

## Notes for reviewers

- **i18n:** new English strings render via Lingui's msgid fallback;
translation catalogs are intentionally **not** included to keep the diff
focused (the repo extracts translations via its standard periodic
`lingui extract` sync — `main` already carries catalog drift). Diff is
18 code files.
- **Recipients:** reminders go to all workspace members, consistent with
the existing suspension emails. Happy to scope the charge‑related ones
to billing admins if preferred.
- **Follow‑ups discussed:** in‑app trial banner, loss‑aversion with real
record counts, and failed‑payment dunning are the higher‑leverage
conversion levers beyond this.

## Test plan

- [x] `typecheck` (twenty-server, twenty-emails)
- [x] oxlint type‑aware + oxfmt
- [x] Unit tests: no‑card path, with‑card path, idempotency, yearly
renewal, billing‑disabled, **kill‑switch off → no send** (6/6 green)
- [ ] Manual: set the flag on a staging instance with `logger` driver
and confirm the right email is logged at each boundary

https://claude.ai/code/session_0147ujzHv1X4vzimf4iGbnT4

---
_Generated by [Claude
Code](https://claude.ai/code/session_0147ujzHv1X4vzimf4iGbnT4)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22186?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-26 08:18:34 +02:00
Parship Chowdhury c635a191bf fix: uneven spacing in date picker calendar grid (#21881)
### Summary
While working on #19739, I found that in the date filter calendar
dropdown, day cells and highlighted dates looked misaligned i.e. tighter
on the right side. The solution is to apply a uniform margin in
`DatePicker.tsx` and `DateTimePicker.tsx`.

### Before:
<img width="377" height="436" alt="Screenshot 2026-06-20 021856"
src="https://github.com/user-attachments/assets/4ef62a48-6b99-4647-95f6-bd39f43eaa26"
/>
<img width="442" height="518" alt="Screenshot 2026-06-20 021932"
src="https://github.com/user-attachments/assets/e6e1e8f8-257a-41e5-825f-bd2fe91e372a"
/>


### After:
<img width="346" height="380" alt="Screenshot 2026-06-20 021813"
src="https://github.com/user-attachments/assets/5fdd03fe-e61b-4fec-a0f3-f6ac4ee9effb"
/>
<img width="322" height="457" alt="Screenshot 2026-06-20 022013"
src="https://github.com/user-attachments/assets/a33c6439-f2d5-43e7-a043-f9cf4fec770a"
/>

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21881?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Signed-off-by: Parship Chowdhury <parshipchowdhury@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-26 07:51:37 +02:00
martmull 05df528fd5 Add logging on sync catalog job (#22192) 2026-06-25 19:03:48 +00:00
Thomas des Francs eedd838189 Fix threaded draft email replies (#22175)
## Summary

Fixes Gmail and Microsoft draft replies so workflow-created drafts stay
attached to the existing provider thread.

Fixes twentyhq/core-team-issues#2597.

## Root cause

The email composer already resolved `threadExternalId` and `references`
from `inReplyTo`, but `DraftEmailTool` only forwarded `inReplyTo` to the
outbound draft service. Gmail therefore created a raw draft without
`message.threadId`, which lets the draft appear as a standalone compose
instead of an inline thread reply.

For Microsoft, the draft path used Graph `createReply`, but parent
lookup filtered on a URL-encoded `internetMessageId`. That can miss the
parent message and fall back to creating a new draft message instead of
a reply draft.

## Changes

- Forward `threadExternalId` and `references` from `DraftEmailTool` to
outbound draft creation.
- Set Gmail draft `message.threadId` when `threadExternalId` is
available.
- Make Microsoft parent lookup use Graph request query builders with
OData string escaping, so `createReply` is reached reliably.
- Add targeted Jest coverage for the Draft Email tool, Gmail draft
threading, and Microsoft reply-draft creation.

## Validation

- `NX_DAEMON=false
/Users/thomascolasdesfrancs/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node
../../node_modules/nx/dist/bin/nx.js jest twenty-server --
--runTestsByPath
src/engine/core-modules/tool/tools/email-tool/__tests__/draft-email-tool.spec.ts
src/modules/messaging/message-outbound-manager/drivers/gmail/services/__tests__/gmail-message-outbound.service.spec.ts
src/modules/messaging/message-outbound-manager/drivers/microsoft/services/__tests__/microsoft-message-outbound.service.spec.ts
--runInBand`
- `NX_DAEMON=false
/Users/thomascolasdesfrancs/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node
./node_modules/nx/dist/bin/nx.js lint:diff-with-main twenty-server`

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22175?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <FelixMalfait@users.noreply.github.com>
2026-06-25 18:39:01 +02:00
Parship Chowdhury 1076866820 fix(server): preserve anyFieldFilterValue in view manifest sync (#22004)
### Summary
- Fixes #19978 
- `shouldHideEmptyGroups` was already wired up in the type and
converter; this PR only closes the remaining gap for
`anyFieldFilterValue`.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22004?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Signed-off-by: Parship Chowdhury <parshipchowdhury@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-25 18:11:35 +02:00
alibildir 35d64ac7f2 fix(server): wrap file storage upsert and read in transaction (#21924)
## Summary
This PR fixes a race condition occurring during file and image uploads
in environments that use Pgpool-II or similar connection poolers with
read-replica scaling.

Currently, in `FileStorageService.writeFile`, `fileRepository.upsert`
writes the file record to the primary database node. However, the
immediate subsequent call to `fileRepository.findOneOrFail` is executed
outside of a transaction. Consequently, connection poolers like Pgpool
can route this `SELECT` query to a read-replica. Due to replication lag,
the replica may not yet reflect the newly inserted record, throwing an
`EntityNotFoundError` and failing the upload process (even though the
file is successfully saved in S3 and the database).

This PR wraps both operations in a TypeORM database transaction when
`queryRunner` is not provided. This ensures that the `SELECT` query
correctly targets the primary node, guaranteeing immediate
read-after-write consistency.

## Affected version
- Twenty Self-hosted (e.g. `v2.14.x`) configured with
Pgpool/read-replicas.

## Changes Made
- **`file-storage.service.ts`**: Wrapped `transactionalFileRepo.upsert`
and `transactionalFileRepo.findOneOrFail` within
`this.applicationRepository.manager.transaction` to ensure
read-after-write consistency.

## How to Test
1. Set up Twenty in a self-hosted environment using Pgpool configured
with load balancing / read-replicas.
2. Attempt to upload a file to a `Files` custom field or upload an image
as an organization logo.
3. Observe that the upload completes successfully without throwing an
`EntityNotFoundError` in the server logs.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21924?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Ali Bildir <[alibildir@gmail.com]>
Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-25 18:10:07 +02:00
github-actions[bot] ce9ca1184c i18n - docs translations (#22189)
Created by Github action

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22189?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-25 17:25:58 +02:00
martmull 1cf1c33899 Add command menu items to people data labs (#22180)
- Adds command menu items to enrich people and companies
- Improve readme

## After

<img width="1039" height="584" alt="image"
src="https://github.com/user-attachments/assets/9c6c3967-e905-4bf2-a3ef-307ae5bdca92"
/>


<img width="2560" height="332" alt="image"
src="https://github.com/user-attachments/assets/4ab4ffb8-fe52-4612-99c7-282d0ffa2c94"
/>
<img width="2560" height="478" alt="image"
src="https://github.com/user-attachments/assets/d496e21b-de18-4c52-acc7-6106afca0bb7"
/>
<img width="2560" height="452" alt="image"
src="https://github.com/user-attachments/assets/caea4de3-5bee-4de7-a347-c5d8540fd179"
/>
<img width="2560" height="335" alt="image"
src="https://github.com/user-attachments/assets/92bd17e3-f4b6-4fcb-84ae-d8ebc7ccce1e"
/>


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22180?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 15:12:30 +00:00
nitin aabffc427a Call recorder readme nitpick (#22185)
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22185?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 17:05:41 +02:00
neo773 24c042f0ef feat(messaging): skip webhook-active channels in list-fetch crons until sync is stale (#22183)
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22183?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 17:01:17 +02:00
nitin f270fb25c8 call recorder readme billing update (#22184)
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22184?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 20:25:58 +05:30
Aleksa Jankovic 0b31d2a6a0 fix(front): wrap long note/markdown text on mobile browsers (#21909)
Fixes #21929

### Issue

When viewing/reading a note's body (BlockNote rich-text / markdown
content) on Android, long text overflows horizontally off the viewport
in **Chrome** and **Firefox**, while it wraps correctly on **iOS
Safari**.

### Root cause

The note body is rendered by the BlockNote editor (`StyledEditor` in
`BlockEditor.tsx`). The block/inline content had no `overflow-wrap`, and
the flex-based `.bn-block-content` had no `min-width` constraint. WebKit
(iOS Safari) breaks the content, but Blink (Android Chrome) and Gecko
(Android Firefox) keep the intrinsic *min-content* width of the flex
children, so the container expands past the viewport instead of
wrapping.

### Fix

Add wrapping/sizing rules to the editor container so text breaks and
wraps consistently across browsers, without changing the desktop layout:

- `overflow-wrap: anywhere` on `.bn-block-content` /
`.bn-inline-content` — unlike `break-word`, this reduces the min-content
size so flex children can actually shrink.
- `min-width: 0` on `.bn-block-content` and on the editor wrapper, plus
`max-width: 100%` on the wrapper.

### Test plan

- Open a Note containing a very long word / URL or a long paragraph.
- Android Chrome & Firefox: text now wraps within the viewport (no
horizontal overflow).
- iOS Safari: unchanged (still wraps).
- Desktop: layout unchanged.

### Screenshots
Android
<img width="1080" height="1949"
alt="Screenshot_20260620_231356_Chrome(1)"
src="https://github.com/user-attachments/assets/bb1e38cf-198a-4ded-9dde-e0a26e637ed8"
/>

iPhone
<img width="686" height="1280" alt="IMG_20260620_231841_096"
src="https://github.com/user-attachments/assets/9cee7557-bb69-453d-bea4-a5c37de220af"
/>


---

###  Verified on a real Android device

Reproduced and validated on a **Samsung Galaxy A71 (Android, Chrome /
Blink)** using the exact note show-page DOM/CSS chain (`ScrollWrapper` →
full-width container → `StyledEditor` → `.bn-mantine` `container-type:
inline-size` → flex `.bn-block-content` → ProseMirror `word-wrap:
break-word`):

- **Without the fix:** a long unbreakable string stays on one line and
overflows horizontally off the viewport (`scrollWidth ≈ 1336px` in a
~360px viewport, with a horizontal scrollbar) — matching the reported
bug.
- **With the fix:** the same string wraps within the viewport.

Notably this does **not** reproduce on desktop Chromium — only on the
mobile engine — which matches the original report (Android
Chrome/Firefox broken, iOS Safari fine). The flex `.bn-block-content`
(`min-width: auto`) resolves to the unbreakable token's intrinsic width
on Android Blink; `min-width: 0` + `overflow-wrap: anywhere` lets it
shrink and wrap.

Screenshot
<img width="1080" height="2400" alt="image"
src="https://github.com/user-attachments/assets/827dc579-4b1e-43ea-8b28-0ca781b12d88"
/>
2026-06-25 16:49:11 +02:00
Thomas des Francs 9870d1e6a9 Fix source icon SVG ID collisions (#22177)
## Summary

Fixes the Gmail and Google Calendar source icons by replacing
document-global generic SVG IDs (`a`, `b`, `c`, etc.) with icon-specific
IDs. This prevents inline SVG gradients, masks, and filters from
resolving against another icon instance when the icons render together
in the record table actor/source column.

## Root cause

The Gmail and Google Calendar SVG assets both used generic IDs. Browser
SVG fragment references are document-global for inline SVG, so whichever
icon appears first can hijack the other icon's `url(#...)` references.
That made the Calendar icon pick up Gmail gradients, and could also
affect Gmail depending on DOM order.

## Before

<img width="1280" height="720" alt="Before icon collision screenshot"
src="https://github.com/user-attachments/assets/82ce20a8-47c7-4b70-8af2-e134cf88c0b8"
/>

## After

<img width="1280" height="720" alt="After icon collision screenshot"
src="https://github.com/user-attachments/assets/240120a6-b84e-4682-a116-0ccb48192543"
/>

## Validation

- Rendered Gmail + Calendar in both DOM orders via a local browser
fixture.
- Verified the before fixture had 14 generic SVG IDs and the after
fixture had 0.
- Verified 4 SVGs rendered, no cross-icon URL reference problems, and no
browser console warnings/errors.
- Ran `xmllint --noout packages/twenty-ui/src/assets/icons/gmail.svg
packages/twenty-ui/src/assets/icons/google-calendar.svg`.
- Ran `rg -n
"id=\"[a-z]\"|url\(#[a-z]\)|mask=\"url\(#[a-z]\)\"|filter=\"url\(#[a-z]\)\""
packages/twenty-ui/src/assets/icons -S` and confirmed no matches.

`yarn nx build twenty-ui` could not run in this worktree because
`node_modules` is missing, and Yarn reports: `Couldn't find the
node_modules state file`.
2026-06-25 14:22:17 +00:00
nitin f86bf637d1 [BREAKING CHANGE] remove call recording feature flag and backfill upgrade command for existing command menu items navigation command (#22176)
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22176?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 19:43:36 +05:30
Kylen Nguyen 407273cff2 fix(docs): correct typos in multiple files (#22173)
Corrects spelling errors across three documentation files.

- `indivual` -> `individual` in `./CLAUDE.md`
- `accesible` -> `accessible` in
`./packages/twenty-docs/user-guide/getting-started/how-tos/navigate-around-twenty.mdx`
- `editting` -> `editing` in
`packages/twenty-docs/user-guide/views-pipelines/overview.mdx`

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22173?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-06-25 15:43:08 +02:00
Akash! 87329c8810 fix(ask-ai): resolve stream subscription race condition on new thread… (#21916)
## Description
Resolves a race condition in the Ask AI feature where the first
assistant reply in a newly created thread does not stream into the UI
and only appears after sending a second message.

### What's Changed
- **Immediate Thread Subscription:** Updated `useAgentChat.ts` to
immediately set `currentAiChatThread` to the newly generated `threadId`
instead of deferring it until after the `SEND_CHAT_MESSAGE` mutation
finishes.
- **The Bug:** Previously, the backend worker processed the AI chat job
so quickly that the stream completed and fired the `message-persisted`
event *before* the frontend established the SSE subscription.
- **The Fix:** By setting the thread ID immediately, the
`useAgentChatSubscription` hook now properly connects and listens to the
SSE stream before the backend begins emitting chunks, guaranteeing the
first message streams seamlessly.

### How to Test
1. Open the Ask AI panel and start a completely new thread.
2. Send an initial message (e.g., "Hello!").
3. Observe that the AI's response successfully streams into the chat
without needing a workaround or page refresh.

Closes #21694

---------

Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2026-06-25 15:41:56 +02:00
Raphaël Bosi c3183f7828 Forward parent commits to Argos visual regression dispatch (#22174)
Part of the Argos orphan-build fix. The dispatch now lists the
merge-base plus its ancestors (up to 100) and forwards them as
`parent_commits`, so the self-hosted Argos can walk back to the nearest
commit with a reference build instead of orphaning when the exact
merge-base lacks one.

Companion to twentyhq/twenty-argos#11 (deploy that first) and the
ci-privileged change that passes the input through to build creation.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22174?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 15:21:43 +02:00
nitin 4f429565e1 call recorder polishes (#22170)
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22170?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 18:27:43 +05:30
Weiko 864ea452b4 fix mobile side panel close (#22169)
The side panel close (X) button was hidden on all mobile views, while
the back button only renders when there is navigation history. When the
side panel is opened at the root (e.g. viewing a record directly with a
single-item navigation stack), neither button was shown, leaving no way
to dismiss the panel on mobile.

Keep the close button available on mobile whenever there is no back
button to fall back on, so the panel is always dismissable.

## Before


https://github.com/user-attachments/assets/61891d25-26b8-4ba4-8b05-73fd44f92d89

## After


https://github.com/user-attachments/assets/41342722-bcaf-420b-83bb-3cafaec49516
2026-06-25 14:55:04 +02:00
Charles Bochet d8cb4aa15b fix(filter): resolve filter-derived date defaults with Temporal (fixes create/update crash in date-filtered views) (#22124)
## Symptom

Creating or updating an Opportunity on a board view crashes with:

```
Uncaught TypeError: e.split is not a function
    at splitDateString (date-fns) → parseISO → isMatchingDateFilter
    → isRecordMatchingFilter → <group-by optimistic effect> → createOneRecord
```

`e` is a non-string (a `Date` object, shown as `{}` in the debugger).
Distinct from the `null` case fixed in #22029.

## Root cause

Reproduced on a live board filtered by **Close date — Is relative —
"This quarter"** (a `DATE_TIME` field).

When you create a record in a filtered view, `useCreateNewIndexRecord`
derives default field values from the view's filters via
`buildRecordInputFromFilter` → `buildValueFromFilter`. For a date field,
`computeValueFromFilterDate` returned a JS **`Date` object** (`new
Date()` / `new Date(value)`), assigned to the new record verbatim. The
optimistic record's `closeDate` was then a `Date`, not an ISO string,
and the group-by optimistic effect matched it via `parseISO(dateObject)`
→ `dateString.split is not a function`, crashing every create/update in
the view.

## Fix

`computeValueFromFilterDate` now returns timezone-aware **ISO strings**
via Temporal, mirroring what `turnRecordFilterIntoGqlOperationFilter`
produces for the same filters — so a record created in a date-filtered
view actually satisfies its own filter:

- **`DATE_TIME`** → an instant. Date-only filter values (a `DATE_TIME`
"is" filter stores `yyyy-MM-dd`, no time) are resolved to the start of
day in the user's time zone, matching how the filter operands are built
— `Temporal.Instant.from()` alone would `RangeError` on them.
- **`DATE`** → a plain date `yyyy-MM-dd` resolved in the **user's time
zone** (`Temporal.Now.plainDateISO(timeZone)`). A bare `new
Date().toISOString()` would use the UTC date, which near midnight is the
wrong calendar day for non-UTC users, so the new record could miss its
own `IS_TODAY`/relative filter. The time zone is threaded from
`useUserTimezone` (same source the filter side uses).
- `IS_BEFORE` subtracts 1 day for `DATE` / 1 minute for `DATE_TIME` (the
`-1 minute` special-case moved out of `buildRecordInputFromFilter`,
which now just assigns the string).

No `Date` object ever reaches `parseISO`, and the value matches the
filter operand, so the optimistic card lands in the right place.

## Tests

- `buildValueFromFilter.spec.ts`: every date operator returns an ISO
**string** (round-tripped to the expected instant); a `DATE` block
asserts date-only `yyyy-MM-dd` output and that `IS_TODAY` resolves to
the correct calendar day **per time zone** at a UTC day boundary
(`2024-03-20` UTC vs `2024-03-21` Asia/Tokyo); a date-only `DATE_TIME`
`IS` value resolves to start-of-day in the user time zone (UTC vs
America/New_York) without throwing.
- `buildRecordInputFromFilter.test.ts`: filter-derived date values are
ISO strings, not `Date` objects.

## Verified locally (Chrome)

Reproduced the exact prod scenario on the **By Stage Opportunities
Kanban board** (the group-by optimistic effect) with a **Close date — Is
relative** filter:

- Created a card in a column → **no `split is not a function` crash**;
the card got a valid Close date (`now` for the relative filter, e.g. `25
Jun 2026 13:10`). Console clean.
- Also verified a **table view + Close date — Is** filter: created
record gets a valid start-of-day value (`25 Jun 2026 00:00`).

Both `IS_RELATIVE` (→ now instant) and the date-only `IS` (→
start-of-day in the user tz) paths produce string values that the
optimistic matcher handles without throwing.
2026-06-25 14:51:42 +02:00
Charles Bochet fe1a8ad5f0 fix(ci): patch danger to decline gzip, fixing ERR_STREAM_PREMATURE_CLOSE on Node 24 (#22171)
## Problem

The `danger-js` check (`twenty-utils:danger:ci`) started failing
intermittently with:

```
FetchError: Invalid response body while trying to fetch
https://api.github.com/repos/twentyhq/twenty/pulls/<n>/files: Premature close
  errno: 'ERR_STREAM_PREMATURE_CLOSE'
```

It fails before the Dangerfile even runs, while fetching PR files / diff
/ commits. The existing retry wrapper
([#22151](https://github.com/twentyhq/twenty/pull/22151)) reduced it but
can't absorb longer GitHub-API windows, so checks still go red.

## Root cause

Not "node-fetch is old" generically — a specific recent regression:

- Node **22.23.0 / 24.17.0** shipped a security fix for CVE-2026-48931
(http.Agent response-queue poisoning) that attaches a `'data'` listener
to idle keep-alive sockets.
- `node-fetch@2` misreads that listener as an unclean connection close —
but only on **gzip-encoded responses without `Content-Length`**, which
is exactly what `api.github.com` returns.
- The GitHub-hosted runners rolling into the patched Node 24.17.x in
recent weeks is why this surfaced now.

See
[danger/danger-js#1515](https://github.com/danger/danger-js/issues/1515),
[nodejs/node#63989](https://github.com/nodejs/node/issues/63989).

## Why this approach

- `node-fetch@2` can't be removed downstream — Danger imports it
directly, and it's pervasive transitively (gaxios/googleapis). Dropping
it is an upstream migration.
- We don't want to pin an old Node version.

So: bump `danger` 13.0.4 → 13.0.8 and backport
[danger/danger-js#1516](https://github.com/danger/danger-js/pull/1516)
via a yarn patch — set `compress: false` on Danger's shared `api()`
wrapper. GitHub then returns identity-encoded responses with
`Content-Length`, and node-fetch's faulty premature-close detector never
fires. Negligible bandwidth cost on these small JSON payloads; explicit
caller overrides are preserved via an `=== undefined` guard.

## Changes

- `packages/twenty-utils/package.json` — `danger` → patched 13.0.8
- `yarn.lock` — registers the `danger@patch:` resolution
- `.yarn/patches/danger-npm-13.0.8-48aba2788c.patch` — the `compress:
false` fix

## Verification

- Patch dry-run applies cleanly against pristine danger 13.0.8 source.
- Inspected yarn's materialized patched cache package — the `compress`
fix is present in the linked `distribution/api/fetch.js`.
- Confirmed the failing calls (`getPullRequestInfo` /
`getPullRequestCommits` / `getPullRequestDiff`) all route through
`this.api` → the patched wrapper.

## Lifecycle

Temporary backport. When #1516 ships in a Danger release, drop the patch
and bump to that version (flagged in a comment inside the patch). The
existing CI retry wrapper stays as defense-in-depth.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22171?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 14:32:25 +02:00
Thomas des Francs 2aeacf341f Flatten system object pickers (#22161)
## Summary
- Flatten system object entries into the first-level workflow object
pickers.
- Flatten dashboard Source and record-page Field picker advanced/system
entries into the main searchable list.
- Keep regular entries first, place system/advanced entries at the
bottom, and cap page-layout picker height at 340px.
- Add keyboard selection support to workflow object pickers through
`SelectableList`.

## Review notes
- Removed now-unused Advanced submenu state, submenu headers, and
duplicated filtering paths.
- Kept the width behavior scoped to each existing dropdown; the shared
page-layout wrapper only controls height/scrolling.
- No blocking issues found in the final reviewed diff.

## Screenshots

### Workflow record type picker
| Before | After |
| --- | --- |
| <img
src="https://gist.githubusercontent.com/Bonapara/d2b6754b3f1927b3d755b5f260e8b12f/raw/dc3f7ef933bdca6c39599c45c827db0f27f01e7d/before-workflow-record-type-advanced.png"
width="320" /> | <img
src="https://gist.githubusercontent.com/Bonapara/d2b6754b3f1927b3d755b5f260e8b12f/raw/2ee195a8562e13d0f4307e7891ab60cfaecae8cf/after-workflow-record-type-flat.png"
width="320" /> |

### Dashboard Source picker
| Before | After |
| --- | --- |
| <img
src="https://gist.githubusercontent.com/Bonapara/d2b6754b3f1927b3d755b5f260e8b12f/raw/45f57728ec7643c7048e857829e977d49b9b365c/before-dashboard-source-tall.png"
width="420" /> | <img
src="https://gist.githubusercontent.com/Bonapara/d2b6754b3f1927b3d755b5f260e8b12f/raw/f8b864ea5e3d04a00b9b6a613e8bf97dd545933c/after-dashboard-source-340px.png"
width="420" /> |

### Record page Field picker
| Before | After |
| --- | --- |
| <img
src="https://gist.githubusercontent.com/Bonapara/d2b6754b3f1927b3d755b5f260e8b12f/raw/ea5f14ccac6c0d9b3d953b4047dc7491702f756a/before-record-field-advanced.png"
width="320" /> | <img
src="https://gist.githubusercontent.com/Bonapara/d2b6754b3f1927b3d755b5f260e8b12f/raw/9d0717cf3cfcb47ed17d595ba83b19a37718c3eb/after-record-field-flat.png"
width="320" /> |

## Checks
- `npx oxfmt --check` on touched files
- `git diff --check`
- `npx tsc -p tsconfig.json --noEmit --pretty false --noErrorTruncation
| rg
"(ChartDataSourceDropdownContent|FieldWidgetFieldDropdownContent|PageLayoutDropdownContentContainer|WorkflowObjectDropdownContent|WorkflowEditTriggerDatabaseEventForm|WorkflowEditActionFindRecords|WorkflowEditActionPickRecord|ChartSettingItem)"`
returned no touched-file diagnostics
- Browser verification: dashboard Source top/bottom, record-page Field
top/bottom, workflow Record Type top/bottom, and ArrowDown selection in
workflow Record Type menu


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22161?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 14:10:30 +02:00
Abdul Rahman db5338cf32 Feat: group records by many to one relation (#22123)
## Group records by relation (Kanban + Table)

Adds grouping by `MANY_TO_ONE` relation fields on both board and table
views, reusing the existing `ViewGroup` storage (`fieldValue = related
record id`).

- **New group** record picker to create relation-backed groups (board
column + table row)
- Relation-aware group headers (name/avatar), filtering, and drag-drop
(writes the FK join column) — all using one canonical `${name}Id` column
- Sort menu hides alphabetical options when grouping by a relation (no
comparable title)
- A group whose backing record no longer exists renders a "Deleted" chip
instead of a blank header
- **Backend:** allow `MANY_TO_ONE` relations as the Kanban
`mainGroupByField` in the flat-view validator


https://github.com/user-attachments/assets/267077a6-2667-4506-b178-eee420a16f20



<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22123?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 13:55:50 +02:00
Weiko 108cc5b6a5 API key creation triggers unnecessary ORMEntityMetadatas cache recomputation (#22168)
## Context

Creating an API key only changes role/apiKey-related data, but the
workspace migration runner was also invalidating and recomputing the
metadata caches (`ORMEntityMetadatas` and `graphQLResolverNameMap`) on
it.

The root cause is a single `if` block in
`getLegacyCacheInvalidationPromises` that gated **all** caches with an
`||` condition combining the metadata and role/permission conditions:

```ts
if (
  shouldIncrementMetadataGraphqlSchemaVersion ||
  shouldInvalidateRoleMapCache ||
  shouldInvalidateRolesPermissionsCache
) {
  // recomputes role caches AND ORMEntityMetadatas + graphQLResolverNameMap
}
```

So any role-only change (such as API key creation, which sets
`shouldInvalidateRoleMapCache`) also recomputed `ORMEntityMetadatas` —
an expensive recomputation that does not depend on role data.

## Fix

Split the combined block into two independent blocks, each gated by its
own condition:

- `shouldIncrementMetadataGraphqlSchemaVersion` → invalidate/recompute
only the metadata-derived caches: `ORMEntityMetadatas` and
`graphQLResolverNameMap`
- `shouldInvalidateRoleMapCache ||
shouldInvalidateRolesPermissionsCache` → invalidate/recompute only the
role/permissions caches

`graphQLResolverNameMap` is built from `flatObjectMetadataMaps` (see
`WorkspaceResolverNameMapCacheService`), so it is grouped with
`ORMEntityMetadatas` in the metadata block rather than the role block.

## Result

- Role-only changes (e.g. API key creation) no longer trigger
unnecessary `ORMEntityMetadatas` / `graphQLResolverNameMap`
recomputation.
- Metadata-only changes no longer recompute the role/permissions caches.
2026-06-25 13:53:58 +02:00
Raphaël Bosi 19bbd59b53 Support CSS imports in front-components via runtime style injection (#22150)
Front-components compile to a remote-dom worker, so a CSS import like
`import 'twenty-ui/style.css'` can't load a stylesheet and was breaking
the build at the manifest step.

This makes the build inline an imported CSS file as a runtime `<style>`
injection that flows through the existing style bridge into the host.
Because the CSS is bundled alongside that same build's hashed class
names, an app's styling matches its own twenty-ui version regardless of
which version the host ships — no server, manifest, or host changes
needed.

The manifest extractor keeps the no-op CSS loader (it executes the
bundle in Node, where `document` is undefined); the inject plugin runs
only in the real build and the dev watcher.
2026-06-25 13:31:00 +02:00
Rashad Karanouh 2af5370749 v1.1.16 — Restore Partner slug in side panel and Notes tab (#22165)
## Summary

**Package version:** `1.1.16`

- Adds **slug** to the Partner record-page `FIELDS_WIDGET` view so it
appears in the side panel for admins and partners (partners remain
update-locked on slug via `partner.role.ts`).
- Restores the **Notes** tab on the custom Partner `RECORD_PAGE` layout
— the marketplace v2 layout replaced the platform default but only
included Home + Timeline.

## Test plan

- [ ] `yarn lint` in `packages/twenty-apps/internal/twenty-partners` — 0
errors
- [ ] `yarn twenty dev --once` on a local partners workspace — sync
succeeds
- [ ] Admin: open a Partner record full page → slug visible under Name
in side panel; **Notes** tab present and can create a linked note
- [ ] Partner role (My Profile): slug visible, not editable; Notes tab
works
- [ ] After merge: `deploy` + `install` on prod partners workspace

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22165?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 13:20:53 +02:00
github-actions[bot] 1284099940 i18n - website translations (#22162)
Created by Github action

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22162?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-25 13:19:57 +02:00
Emmanuel Hernández Bazán 981ee6a2d7 fix: stabilize tooltip anchor ID with useRef to prevent hover glitch (#21888)
## Context

Tooltip components were generating a new anchor ID on every render. This
caused a flicker/glitch when hovering: the tooltip would briefly
disappear and reappear because React reconciled the changed ID as a
different element.

## Solution

Changed the anchor ID generation from inline (re-created every render)
to `useRef` (stable across renders). The ID is now created once on mount
and stays the same for the lifetime of the component.

## Test plan

- [x] Hover over any element with a tooltip — no flicker or
disappear/reappear behavior
- [x] Multiple tooltips on the same page still work independently

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

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21888?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Emmanuel Hernandez <emmanuel.hernandez@clickbalance.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-25 13:12:11 +02:00
neo773 ee71a382de IMAP support non RFC compliant servers (#22153)
Some non complaint IMAP server don't send `UIDNEXT` UIDNEXT is the next
message id you subtract with 1 to get total current messages

This does a fallback to searching all UIDs and taking the highest

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22153?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 16:15:13 +05:30
github-actions[bot] a87bc4bed6 i18n - website translations (#22160)
Created by Github action

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22160?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-25 12:31:53 +02:00
martmull a20ebfa880 feat(applications): remove the application custom settings tab (#22156)
## Summary

Removes the application **custom settings tab** feature. This is one
half of #22059, split out so it can be reviewed/merged independently
from the variable-types enrichment.

## Changes

- Remove the `SettingsApplicationCustomTab` component and its tab
entry/rendering in `SettingsApplicationDetails`.
- Stop syncing `settingsCustomTabFrontComponent` from application
manifests — `ApplicationManifestMigrationService` now only syncs the
default role.
- Deprecate the now-unused fields (kept for backward compatibility, no
longer read or synced):
- `ApplicationDTO.settingsCustomTabFrontComponentId` (GraphQL
`@deprecated`)
-
`ApplicationManifest.settingsCustomTabFrontComponentUniversalIdentifier`
- the `settingsCustomTabFrontComponentId` column comment on
`ApplicationEntity`

The DB column is intentionally **not dropped**, so existing
installations upgrade cleanly.
2026-06-25 12:29:45 +02:00
Abdullah. 5a657129f0 Split website stepper into home-stepper and product-stepper sections (#22149)
Reorganizes the flat `sections/stepper/` (30 files — two distinct
steppers plus shared code) into two product-feature-style sections, and
moves the shared code to the shared layers.

- **`sections/home-stepper/`** — the home-page stepper. Renamed
`Stepper` → `HomeStepper` (and the home components → `HomeStepperLottie`
/ `HomeStepperSteps` / `HomeStepperVisualFrame`) for symmetry with
`ProductStepper`. Shell at the root + `components/`/`data/`/`utils/` +
barrel.
- **`sections/product-stepper/`** — the product-page stepper, same
structure.

The 3 files both steppers shared can't live in a shared *section* —
`check-conventions` forbids a section importing another section. So they
moved to the shared layers:
- `StepperProgressRail`, `StepperSwipeDeck` → `ui/`
- `useBreakpointStepSync` → `platform/motion`

Both consumer pages repointed (`@/sections/home-stepper`,
`@/sections/product-stepper`); the row-gap allowlist in
`check-conventions.mjs` updated to the new paths; explanatory comments
stripped across the moved files (CSS-in-template comments and `'use
client'` kept).

Pure reorganization — no behavior change. typecheck + lint + build all
green.
2026-06-25 12:24:22 +02:00
github-actions[bot] 904d3d6bff i18n - website translations (#22158)
Created by Github action

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22158?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-25 12:20:32 +02:00
Abdullah. 551a792291 Rework website TasksVisual to match twenty-front tasks tab (#22143)
- Rework `TasksVisual` to match twenty-front's tasks tab: per-group
bordered cards with row dividers, `TODO`/`DONE` headers, the `+ Add
task` button, and twenty-front's checkbox, row, and target-chip styling.
- Enable the checkbox to toggle a task between `TODO` and `DONE`.

Marketing data kept intuitive (single assignee, full dates, action-led
titles).

<img width="859" height="608" alt="image"
src="https://github.com/user-attachments/assets/085c225f-b121-44e6-aaec-553c02fd2367"
/>
2026-06-25 12:20:18 +02:00
Abdullah. d6556221d9 Tidy website ImportVisual markup and CSS (#22148)
Markup/CSS tidy-up of `ImportVisual` — no visual change.

- Collapse the redundant `Grid` wrapper into `Root` (now
`flex-direction: column`) and drop the no-op `justify-content: center`
(the child was already full-width).
- Hoist the duplicated `font-size: previewFontSize(md)` to the mapping
`Row` (the `sm` example keeps its override).

<img width="574" height="604" alt="image"
src="https://github.com/user-attachments/assets/dcc72567-5c13-4d54-884a-f3120cc5a345"
/>
2026-06-25 15:12:08 +05:00
martmull 29e0327063 fix(server): allow moving menu items into a folder created in the same sync (#22130)
## Context

Fixes
[core-team-issues#2593](https://github.com/twentyhq/core-team-issues/issues/2593).

When reorganizing navigation menu items by moving existing items into a
**newly created folder** within a single deploy, the sync failed with
`Parent navigation menu item not found`, forcing a two-step deploy
(create the folder first, then move the items into it).

## Root cause

Migration entities are validated in the fixed order **delete → update →
create** (`workspace-entity-migration-builder.service.ts`). When items
are moved into a new folder in one sync, the items are *updated* (adding
`folderUniversalIdentifier`) while the folder is *created* — but the
update phase runs before the create phase, so the folder isn't yet in
the optimistic maps.

The **creation** validator already handles "parent doesn't exist yet" by
also checking `remainingFlatEntityMapsToValidate`. The **update**
validator couldn't: `FlatEntityUpdateValidationArgs` explicitly omitted
that field, so it only looked at the optimistic maps and threw.

## Changes

- `universal-flat-entity-update-validation-args.type.ts` — stop omitting
`remainingFlatEntityMapsToValidate` from the update args.
- `workspace-entity-migration-builder.service.ts` — pass
`createdFlatEntityMaps` (entities being created in the same migration)
into update validation.
- `flat-navigation-menu-item-validator.service.ts` — resolve the parent
folder against both the optimistic maps and the to-be-created entities,
mirroring the creation validator.
- Integration test — sync an item, then in a second sync create a folder
and move the item into it, asserting it succeeds in a single deploy.

The change is generic and type-safe: all other update validators receive
the new field and simply ignore it. `createdFlatEntityMaps` is
`MetadataUniversalFlatEntityMaps<T>`, matching the field's type.

## Test plan

- [x] Added integration test `should move existing menu items into a
folder created in the same sync`
- [ ] CI green

https://claude.ai/code/session_017pmBkho9Fh6Vjv8WA4m9YE

---
_Generated by [Claude
Code](https://claude.ai/code/session_017pmBkho9Fh6Vjv8WA4m9YE)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22130?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 12:04:18 +02:00
Abdullah. 491b9e954a Rework website EmailsVisual to match twenty-front emails tab (#22146)
- Rework `EmailsVisual` to match twenty-front's emails tab: bordered
thread card with dividers, `Inbox` header sizing, and twenty-front's
row/sender/date colors and hard-clipped sender names (kept `+ Compose`).
- Replace the "not shared" row with four fully-populated shared threads
(subjects/bodies sourced from twenty-server's dev seed), two
participants each, full `MMM D, YYYY` dates.

<img width="575" height="607" alt="image"
src="https://github.com/user-attachments/assets/68d1fd6d-e030-4f3c-b775-687f7e1ab173"
/>
2026-06-25 15:02:44 +05:00
github-actions[bot] cdd1aba3e0 i18n - docs translations (#22154)
Created by Github action

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22154?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-25 11:32:08 +02:00
Charles Bochet f416f81548 fix(ci): retry Danger.js on transient GitHub API fetch errors (#22151)
## Problem

The `danger-js` job in **CI Utils** has been failing across most PRs.
The failure is not a real Danger violation — it's a transient network
error fetching the PR diff/commits from the GitHub API:

```
Failed to fetch GitHub pull request files: FetchError: Invalid response body while trying to fetch
https://api.github.com/repos/twentyhq/twenty/pulls/XXXXX/files?page=1&per_page=100: Premature close
    at Gunzip.<anonymous> (.../node_modules/node-fetch/lib/index.js:400:12)
  errno: 'ERR_STREAM_PREMATURE_CLOSE',
  code: 'ERR_STREAM_PREMATURE_CLOSE'
```

GitHub closes the gzipped HTTP response mid-stream, and Danger's bundled
`node-fetch` has **no retry** on a dropped connection — so any single
blip fails the whole check.

## Why is this happening now?

Nothing on our side changed at the boundary where failures started. The
Node 24.16 bump landed Jun 8 (the job stayed green for 2+ weeks after),
Danger has been pinned at `13.0.4` for months, and there are **zero
commits** to `.nvmrc`, `twenty-utils/package.json`, or the
`yarn-install` action since Jun 22.

What changed is GitHub's API reset rate, and it changed abruptly:

| Day | Failures | Successes | Failure rate |
|-----|----------|-----------|--------------|
| Jun 23 | 1 | 48 | ~2% (green) |
| Jun 24 | 38 | 204 | ~16% |
| Jun 25 | 23 | 25 | **~48%** |

The same PR passes on one run and fails on the next (e.g. one PR shows
up as both pass and fail; another failed 3 runs in a row) — a code bug
can't flip outcomes on identical input, only an infrastructure flake
can. When GitHub's connection-reset rate was ~0% we never noticed; now
that it's in the tens of percent, roughly half of all PRs trip it.

## Fix

Wrap the Danger invocation in a small retry loop (3 attempts, 5s
backoff) so the check absorbs these transient fetch errors instead of
red-flagging the PR. Applied to both the `danger-js` and `congratulate`
jobs since they share the same failure mode.

This is the correct mitigation rather than a code revert — there's no
change on our side to revert. 3 attempts drop a ~48% single-shot failure
rate to ~11%, and a less-degraded ~16% rate to well under 1%. If
GitHub's reliability recovers, the retries simply stop firing and cost
nothing.

This is a CI-only change — no application code is touched.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22151?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 11:27:51 +02:00
martmull 6a36fc3ba8 fix(sdk): evict compiled manifest modules from require cache in dev mode (#22129)
## Problem

`yarn twenty dev` crashes after running for a while with:

```
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
```

The crash happens during the **Manifest Build** phase, after the process
has been up for a long time (~35 min in the reported case) — the
signature of a slow memory leak in the long-running watch process, not a
single oversized operation.

Fixes the issue described in `core-team-issues#2560`.

## Root cause

`manifest-extract-config-from-file.ts``loadModule()` compiles each
entity file with esbuild (`bundle: true`, so the whole dependency graph
is inlined), writes it to a unique `mkdtemp` directory, and loads it
with `appRequire(tempFile)`.

`createRequire` shares Node's global module cache (`Module._cache`), so
**every loaded temp module is retained in the require cache forever**.
Two things made this unbounded:

1. `mkdtemp` generates a fresh random directory each call, so cache keys
never collide — entries purely accumulate.
2. The `finally` block removed the temp dir **from disk** but never
evicted the `require.cache` entry, so the evaluated, fully-bundled
module object stayed pinned in the JS heap.

In dev mode the orchestrator re-runs `buildManifest` on **every** file
change and watcher rebuild (`scheduleSync()`), and each rebuild compiles
& `require()`s ~one module per entity file (53 in the reported case).
Over a session of editing, thousands of large bundled module objects
pile up in `require.cache` until V8's heap is exhausted → FATAL OOM.

## Fix

Delete the temp module from the require cache after loading it, so
memory stays bounded to a single rebuild. Because `bundle: true` inlines
the whole graph, the temp module is the only cache entry per load, so
deleting it lets the bundled object be GC'd.

```ts
} finally {
  delete appRequire.cache[tempFile];
  await remove(tempDir);
}
```

## Test

Adds a regression test (`manifest-extract-config-from-file.spec.ts`)
that runs `extractManifestFromFile` repeatedly and asserts no
`twenty-manifest` temp modules accumulate in the require cache.

https://claude.ai/code/session_0168hN9yEYvjZbqdz7PckKn8

---
_Generated by [Claude
Code](https://claude.ai/code/session_0168hN9yEYvjZbqdz7PckKn8)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22129?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-25 11:21:51 +02:00