application.service.create() was overriding sourceType to LOCAL
regardless of what the caller passed. For npm/tarball installs, the
correct sourceType was set via updateApplicationSourceType, but it
ran BEFORE synchronizeFromManifest (which creates the Application
record), so the update hit zero rows on first install.
Fix: remove the hardcoded override (column default handles the
fallback) and move updateApplicationSourceType after the sync so it
correctly updates the newly-created record.
Made-with: Cursor
- Update app-distribution tests: 'none' sourceType → 'local'
- Handle ApplicationRegistrationException in REST API exception filter
so tarball upload errors return proper HTTP status codes
- Catch manifest.json read failure in tarball upload and throw
INVALID_INPUT (400) instead of PACKAGE_RESOLUTION_FAILED (500)
- Update marketplace-catalog-sync test to include marketplaceDisplayData
in raw SQL inserts (matching what the real sync cron produces)
Made-with: Cursor
Closes https://github.com/twentyhq/core-team-issues/issues/1627
**FilterArgProcessor consolidation:**
Refactored to both validate AND transform filter values in a single pass
Coerced string inputs to native types (e.g., "1" → 1, "true" → true -
useful for Rest input)
Returns transformed filter instead of just validating
Removed overrideFilterByFieldMetadata calls from all computeArgs methods
**QueryRunnerArgsFactory cleanup**
**Testing:**
Add unit testing
uncomment integration tests
Break circular import chain: application-registration.entity →
user.entity → app-token.entity → workspace.entity →
application.entity → (back to) application-registration.entity.
The enum is now in its own file with no entity imports, so both entity
files can reference it without triggering a circular dependency at
module load time.
Made-with: Cursor
## Summary
- Move Modal UI components (`Modal`, `ModalContent`, `ModalHeader`,
`ModalFooter`, `ModalBackdrop`) from `twenty-front` to `twenty-ui` as
stateless, reusable components
- Create `ModalStatefulWrapper` in `twenty-front` that connects Jotai
state (`isModalOpenedComponentState`) to the stateless `Modal` via an
`isOpen` prop
- Rename `modalVariant` prop to `overlay` with clearer values: `'dark'`
(default), `'light'` (in-container), `'transparent'` (invisible panel).
Remove unused `'medium'` overlay
- Rename `modalId` to `modalInstanceId` across the entire modal zone
(~30 consumer files)
- Extract `ModalProps` to its own file in
`twenty-ui/types/ModalProps.ts`; extract `ModalStatefulWrapperProps` to
its own file using `Pick<ModalProps, ...>` for shared props
- Extract `ModalBackdrop` to its own file and export from `twenty-ui`;
use it in `UserOrMetadataLoader` instead of a local styled component
- Use `ModalFooter` in `StepNavigationButton` and `ModalHeader` in
`SpreadsheetImportStepperContainer` instead of duplicated `styled.div`
definitions
- Remove unused `onClose` prop from stateless `Modal`; fix `typeof
document` guard in `ModalStatefulWrapper`
- Split shared types into individual files: `ModalSize.ts`,
`ModalPadding.ts`, `ModalOverlay.ts`
- Extract wyw profiling instrumentation from `vite.config.ts` into
reusable `createWywProfilingPlugin` with parametrized threshold and
improved logging
- Delete old `Modal.tsx`, `Modal.styles.ts`, `ModalContent.tsx`,
`ModalHeader.tsx`, `ModalFooter.tsx` from `twenty-front`
- Add comprehensive Storybook stories in `twenty-ui` covering Default,
Confirmation, Small, ExtraLarge, Closed, and Interactive variants
# Introduction
Adding integration test scaffold to the create twenty app and an example
to the hello world app
This PR also fixes all the sdk e2e tests in local
## `HELLO_WORLD`
Removed the legacy implem in the `twenty-apps` folder, replacing it by
an exhaustive app generation
## Next step
Will in another PR add workflows for CI testing
## Open question
- Should we still add vitest config and dep even if the user did not ask
for the integration test example ? -> currently we don't
- That's the perfect timing to identify if we're ok to handle seed
workspace authentication with the known api key
MarketplaceModule was only imported in DatabaseCommandModule (CLI) but
not in the main app module tree, so the resolver and cron were not
loaded at runtime.
Made-with: Cursor
The per-registration registry URL override was never set by any code
path — all npm operations already fall back to the server-level
APP_REGISTRY_URL config. Remove the column, entity field, and migration
to avoid unnecessary schema complexity. Can be re-added later if
per-app private registries become a real use case.
Made-with: Cursor
1. Advisory lock: use pg_advisory_xact_lock(key1, key2) with two
separate INT4 hashes for 64-bit key space instead of single 32-bit
2. Update badge: use proper semver comparison (isNewerSemver) instead
of simple inequality check, preventing false positives on downgrades
3. Catalog sync: replace boolean hasSyncedOnce with timestamp-based
cooldown (5min), enabling retries after transient failures
4. Yarn engine: dynamically resolve yarn path from .yarnrc.yml instead
of hardcoding the version in the filename
5. Temp directory: clean up APP_RESOLVER_TMPDIR on module init to
remove stale files from previous server runs
Made-with: Cursor
1. SDK push: fix endpoint URL (/api/app-registrations/) and send
base64 JSON body instead of multipart/form-data
2. Security: skip symlinks in collectFiles to prevent path traversal
3. Security: use resolve()+startsWith() instead of includes('..')
for tarball path traversal protection
4. Multi-tenant: scope upsertRegistration by workspaceId
5. Data consistency: set sourceType=TARBALL on creation instead of
saving as NONE then updating
6. Correctness: move hasSyncedOnce after successful sync
7. Correctness: URL-encode scoped npm package names in registry URLs
8. Error handling: wrap JSON.parse in try-catch for manifest parsing
9. Security: remove filesystem paths from error messages
10. Robustness: add default branch to REST exception filter
11. UX: gate modal close on install success
12. Robustness: add default case to mapSourceType
13. UX: improve userFriendlyMessage for package resolution failure
Made-with: Cursor
## Summary
Completes the migration of the frontend styling system from **Emotion**
(`@emotion/styled`, `@emotion/react`) to **Linaria** (`@linaria/react`,
`@linaria/core`), a zero-runtime CSS-in-JS library where styles are
extracted at build time.
This is the final step of the migration — all ~494 files across
`twenty-front`, `twenty-ui`, `twenty-website`, and `twenty-sdk` are now
fully converted.
## Changes
### Styling Migration (across ~480 component files)
- Replaced all `@emotion/styled` imports with `@linaria/react`
- Converted runtime theme access patterns (`({ theme }) => theme.x.y`)
to build-time `themeCssVariables` CSS custom properties
- Replaced `useTheme()` hook (from Emotion) with
`useContext(ThemeContext)` where runtime theme values are still needed
(e.g., passing colors to non-CSS props like icon components)
- Removed `@emotion/react` `css` helper usages in favor of Linaria
template literals
### Dependency & Configuration Changes
- **Removed**: `@emotion/react`, `@emotion/styled` from root
`package.json`
- **Added**: `@wyw-in-js/babel-preset`, `next-with-linaria` (for
twenty-website SSR support)
- Updated Nx generator defaults from `@emotion/styled` to
`@linaria/react` in `nx.json`
- Simplified `vite.config.ts` (removed Emotion-specific configuration)
- Updated `twenty-website/next.config.js` to use `next-with-linaria` for
SSR Linaria support
### Storybook & Testing
- Removed `ThemeProvider` from Emotion in Storybook previews
(`twenty-front`, `twenty-sdk`)
- Now relies solely on `ThemeContextProvider` for theme injection
### Documentation
- Removed the temporary `docs/emotion-to-linaria-migration-plan.md`
(migration complete)
- Updated `CLAUDE.md` and `README.md` to reflect Linaria as the styling
stack
- Updated frontend style guide docs across all locales
## How it works
Linaria extracts styles at build time via the `@wyw-in-js/vite` plugin.
All expressions in `styled` template literals must be **statically
evaluable** — no runtime theme objects or closures over component state.
- **Static styles** use `themeCssVariables` which map to CSS custom
properties (`var(--theme-color-x)`)
- **Runtime theme access** (for non-CSS use cases like icon `color`
props) uses `useContext(ThemeContext)` instead of Emotion's `useTheme()`
When universalIdentifier is a UUID and no matching registration exists,
throw instead of falling through to create a new npm registration.
Made-with: Cursor
Implement a comprehensive app distribution system supporting both public
(npm registry) and private (tarball upload) installation channels.
Backend:
- Add AppRegistrationSourceType enum (npm, tarball, none) to track app origin
- Add AppPackageResolverService for resolving packages from npm or tarball sources
- Add ApplicationInstallService with PostgreSQL advisory locks for safe installs
- Add AppUpgradeService with version checking and rollback support
- Add tarball upload REST endpoint with secure extraction (path traversal protection)
- Add marketplace catalog sync cron job (hourly) from hardcoded catalog index
- Add app version check cron job (every 6 hours) to detect available updates
- Disable yarn lifecycle scripts (enableScripts: false) to prevent RCE via postinstall
- Add database migration for sourceType, sourcePackage, latestAvailableVersion fields
Frontend:
- Add "Install from npm" modal for manual package installation
- Add "Upload tarball" modal for direct .tar.gz uploads
- Add upgrade mutation and version container with upgrade button
- Add blue "Update" badge on installed apps table when newer version available
- Fetch application registrations to compare installed vs latest versions
- Migrate styled components from Emotion to Linaria (matching main migration)
- Remove redundant sourcePackage mutation argument (derived from universalIdentifier)
Testing:
- Add integration tests for app distribution (install, upgrade, tarball upload)
- Add integration tests for marketplace catalog sync
Made-with: Cursor
- apollo enrich application (via OAuth 2)
- add applicationId to var env in logic function executor
- update `getDefaultUrl` logic
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
<img width="450" height="212" alt="Capture d’écran 2026-03-03 à 11 41
54"
src="https://github.com/user-attachments/assets/b2c29a48-7dc0-4b16-a085-8f305d21f7ca"
/>
New status `FAIL_SAFE` added. This status propagates to the following
nodes until reaching the iterator, that will start the new iteration.
The difference with `SKIP` is that, when the parent nodes have at least
one `FAIL_SAFE`, it becomes `FAIL_SAFE` too. While a parent 1 `SKIP` +
parent 2 `SUCCESS` => to be executed.
I also thought about just going back to the iterator as a break would,
but since we have branches, it may lead to inconsistent statuses with
parallel updates.
## Summary
- Replaces all `depot-ubuntu-24.04` runners with `ubuntu-latest`
- Replaces all `depot-ubuntu-24.04-8` runners with
`ubuntu-latest-8-cores`
- Updates storybook build cache keys in ci-front.yaml to reflect the
runner name change
Reverts the temporary Depot migration introduced in #18163 / #18179
across all 23 workflow files.