feat(app-dev): surface metadata diff in dev sync and name failing migration actions (#21249)

Split out of #21240.

- Render the applied metadata changes (created/updated/deleted +
identifiers) in the dev sync output instead of a bare `✓ Synced`.
- Include the failing entity's `universalIdentifier` in
`WorkspaceMigrationRunnerException` messages so conflicts are
diagnosable.

<img width="637" height="114" alt="image"
src="https://github.com/user-attachments/assets/61422a16-370c-4e9b-a2f6-c29ce17f3b1b"
/>

<img width="497" height="104" alt="image"
src="https://github.com/user-attachments/assets/d493c398-da29-49c9-ac5e-aa0f26cd7389"
/>

<img width="593" height="127" alt="image"
src="https://github.com/user-attachments/assets/15e26edc-c0e4-4427-bd34-909040e970c9"
/>

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
martmull
2026-06-05 16:35:50 +02:00
committed by GitHub
parent f72898063a
commit f20d04eb6e
13 changed files with 364 additions and 5 deletions
@@ -5,6 +5,7 @@ import { FileApi } from '@/cli/utilities/api/file-api';
import { LogicFunctionApi } from '@/cli/utilities/api/logic-function-api';
import { SchemaApi } from '@/cli/utilities/api/schema-api';
import { type Manifest } from 'twenty-shared/application';
import { type SyncAction } from 'twenty-shared/metadata';
type ApiServiceOptions = {
disableInterceptors?: boolean;
@@ -72,7 +73,12 @@ export class ApiService {
return this.applicationApi.createDevelopmentApplication(...args);
}
syncApplication(manifest: Manifest): Promise<ApiResponse> {
syncApplication(manifest: Manifest): Promise<
ApiResponse<{
applicationUniversalIdentifier: string;
actions: SyncAction[];
}>
> {
return this.applicationApi.syncApplication(manifest);
}
@@ -1,6 +1,7 @@
import { type ApiResponse } from '@/cli/utilities/api/api-response-type';
import axios, { type AxiosInstance, type AxiosResponse } from 'axios';
import { type Manifest } from 'twenty-shared/application';
import { type SyncAction } from 'twenty-shared/metadata';
export class ApplicationApi {
constructor(private readonly client: AxiosInstance) {}
@@ -251,7 +252,12 @@ export class ApplicationApi {
}
}
async syncApplication(manifest: Manifest): Promise<ApiResponse> {
async syncApplication(manifest: Manifest): Promise<
ApiResponse<{
applicationUniversalIdentifier: string;
actions: SyncAction[];
}>
> {
try {
const mutation = `
mutation SyncApplication($manifest: JSON!) {