diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/__tests__/field-index-view-field-on-create-side-effect-handler.service.spec.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/__tests__/field-index-view-field-on-create-side-effect-handler.service.spec.ts index 3faf582ee4..8eef9d3bc9 100644 --- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/__tests__/field-index-view-field-on-create-side-effect-handler.service.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/__tests__/field-index-view-field-on-create-side-effect-handler.service.spec.ts @@ -331,7 +331,7 @@ describe('FieldIndexViewFieldOnCreateSideEffectHandlerService', () => { }); describe('field created on an existing object (historical createOneField behavior)', () => { - it('should append a hidden view field to the INDEX view resolved by its derived identifier', () => { + it('should append a visible view field to the INDEX view resolved by its derived identifier', () => { const result = handler.buildSideEffects( buildArgs({ triggerFieldMetadata: PRIORITY_FIELD, @@ -376,7 +376,7 @@ describe('FieldIndexViewFieldOnCreateSideEffectHandlerService', () => { SYNCED_INDEX_VIEW.universalIdentifier, ); expect(viewFields[0].position).toBe(5); - expect(viewFields[0].isVisible).toBe(false); + expect(viewFields[0].isVisible).toBe(true); expect(viewFields[0].isSystemSideEffect).toBe(true); }); @@ -487,7 +487,7 @@ describe('FieldIndexViewFieldOnCreateSideEffectHandlerService', () => { expect(viewFields[0].position).toBe(0); }); - it('should emit a hidden view field for a relation field', () => { + it('should emit a visible view field for a relation field', () => { const relationField = buildPendingFieldMetadata( 'assignee', FieldMetadataType.RELATION, @@ -513,7 +513,7 @@ describe('FieldIndexViewFieldOnCreateSideEffectHandlerService', () => { ); expect(viewFields).toHaveLength(1); - expect(viewFields[0].isVisible).toBe(false); + expect(viewFields[0].isVisible).toBe(true); }); // The emitted view field is engine-owned whatever the view provenance, so diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/field-index-view-field-on-create-side-effect-handler.service.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/field-index-view-field-on-create-side-effect-handler.service.ts index f23c8c1f54..6893afae35 100644 --- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/field-index-view-field-on-create-side-effect-handler.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/field-index-view-field-on-create-side-effect-handler.service.ts @@ -29,7 +29,7 @@ export class FieldIndexViewFieldOnCreateSideEffectHandlerService extends Metadat metadataName: 'fieldMetadata', name: 'fieldIndexViewFieldOnCreate', description: - 'When a field is created, provision its view field on the parent object INDEX view. This handler owns the view fields of every caller-provided field. When the parent object is created in the same batch, the default view assembly applies: only displayable fields (no relations) get a visible view field, positioned before the system-field view fields emitted by objectSystemFieldsAndIndexViewOnCreate; both handlers derive positions from the same caller-input list so no ordering dependency exists. When the parent object pre-exists, every created field (relations included) gets a HIDDEN view field appended to the INDEX view, preserving the historical createOneField behavior previously implemented caller-side. Emitted view fields are always isSystemSideEffect, whatever the provenance of the view they land on: the engine authored them, so manifest sync deletion inference must never drop them. Both branches resolve the INDEX view through its name-free deterministic identifier (object identifier + INDEX key), so the lookup is a single map access and never a scan; workspaces synced before the 2-26 reconcile command hold underived INDEX view identifiers and are simply skipped until it runs. Engine-owned fields (the reserved system fields, the searchVector) never reach this handler: they are emitted as side effects and the engine only triggers handlers on the original input, so their view fields belong to the handler that emits them. Noop when the created field is not displayable in the default view (object created in the same batch) or when the object has no active INDEX view (pre-existing object). The engine owns the INDEX view, so this handler never defers to a caller-provided one: the flat view validator rejects caller-created INDEX views outright, and a second writer claiming the same INDEX view field is a genuine conflict left to surface downstream (engine universal-identifier collision, then the flat view field validator on the (view, field) pair).', + 'When a field is created, provision its visible view field on the parent object INDEX view. Owns the view fields of every caller-provided field; engine-emitted fields get theirs from the handler that emits them.', }, ) { buildSideEffects({ @@ -267,7 +267,7 @@ export class FieldIndexViewFieldOnCreateSideEffectHandlerService extends Metadat sourceFlatFieldMetadata.universalIdentifier, viewUniversalIdentifier: indexViewUniversalIdentifier, viewFieldGroupUniversalIdentifier: null, - isVisible: false, + isVisible: true, size: DEFAULT_VIEW_FIELD_SIZE, position: appendBasePosition + indexAmongCallerFlatFieldMetadatas, aggregateOperation: null, diff --git a/packages/twenty-server/test/integration/graphql/suites/view/__snapshots__/successful-find-view.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/view/__snapshots__/successful-find-view.integration-spec.ts.snap index 9c023a72d4..fb440f0b4e 100644 --- a/packages/twenty-server/test/integration/graphql/suites/view/__snapshots__/successful-find-view.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/view/__snapshots__/successful-find-view.integration-spec.ts.snap @@ -106,7 +106,7 @@ exports[`successful find view with all sub-relations (e2e) Company View Structur "deletedAt": null, "fieldMetadataId": Any, "id": Any, - "isVisible": false, + "isVisible": true, "position": 7, "size": 180, "updatedAt": Any, @@ -118,7 +118,7 @@ exports[`successful find view with all sub-relations (e2e) Company View Structur "deletedAt": null, "fieldMetadataId": Any, "id": Any, - "isVisible": false, + "isVisible": true, "position": 8, "size": 180, "updatedAt": Any, @@ -130,7 +130,7 @@ exports[`successful find view with all sub-relations (e2e) Company View Structur "deletedAt": null, "fieldMetadataId": Any, "id": Any, - "isVisible": false, + "isVisible": true, "position": 9, "size": 180, "updatedAt": Any, @@ -142,7 +142,7 @@ exports[`successful find view with all sub-relations (e2e) Company View Structur "deletedAt": null, "fieldMetadataId": Any, "id": Any, - "isVisible": false, + "isVisible": true, "position": 10, "size": 180, "updatedAt": Any, @@ -154,7 +154,7 @@ exports[`successful find view with all sub-relations (e2e) Company View Structur "deletedAt": null, "fieldMetadataId": Any, "id": Any, - "isVisible": false, + "isVisible": true, "position": 11, "size": 180, "updatedAt": Any, @@ -166,7 +166,7 @@ exports[`successful find view with all sub-relations (e2e) Company View Structur "deletedAt": null, "fieldMetadataId": Any, "id": Any, - "isVisible": false, + "isVisible": true, "position": 12, "size": 180, "updatedAt": Any, @@ -178,7 +178,7 @@ exports[`successful find view with all sub-relations (e2e) Company View Structur "deletedAt": null, "fieldMetadataId": Any, "id": Any, - "isVisible": false, + "isVisible": true, "position": 13, "size": 180, "updatedAt": Any, diff --git a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap index 253415c041..2059333b2b 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap @@ -44,7 +44,7 @@ exports[`syncApplication should create a TEXT field on the standard Company obje "fieldMetadataUniversalIdentifier": Any, "isActive": true, "isSystemSideEffect": true, - "isVisible": false, + "isVisible": true, "position": 14, "size": 180, "universalIdentifier": Any, @@ -1058,7 +1058,7 @@ exports[`syncApplication should delete old field and create equivalent one when "fieldMetadataUniversalIdentifier": Any, "isActive": true, "isSystemSideEffect": true, - "isVisible": false, + "isVisible": true, "position": 6, "size": 180, "universalIdentifier": Any,