Files
twenty/packages
Charles BochetandClaude Opus 4.7 de8f2addfb fix(server): derive system field universalIdentifiers from v5 and auto-attach them in manifest sync
Fixes app re-install on workspaces that have already received the v2.5
`NormalizeCompositeFieldDefaultsCommand` backfill: those workspaces now
have non-null `defaultValue` on every composite system field (createdBy,
updatedBy, position, searchVector, …). The next `installApplication` →
`synchronizeFromManifest(isSystemBuild: false)` rebuilds the diff and
hits `FIELD_MUTATION_NOT_ALLOWED` on each row because the manifest TO
state never declared system fields, so the dispatcher saw them as
FROM-only and emitted bad delete/update actions.

## Root cause

`computeApplicationManifestAllUniversalFlatEntityMaps` walks
`manifest.objects[].fields` and never scaffolds the eight standard
system fields the runtime actually attaches on object creation
(`buildDefaultFlatFieldMetadatasForCustomObject`). On top of that,
the scaffolder seeded each system field's `universalIdentifier` with
`v4()`, so even if we wanted to mirror them on the TO side from the
manifest converter, the random UIDs would never line up with the rows
already in the workspace.

## Fix (three pieces)

1. **Switch `buildDefaultFlatFieldMetadatasForCustomObject` from `v4` to
   `v5`.** Same pattern as `NAVIGATION_COMMAND_UUID_NAMESPACE`: every
   system field's universalIdentifier is derived deterministically from
   `${objectMetadataUniversalIdentifier}/${name}` against a new
   `SYSTEM_FIELD_UUID_NAMESPACE`. Exported as
   `computeSystemFieldUniversalIdentifier` so the manifest converter
   and the upgrade migration both compute identical UUIDs.

2. **Inject the eight system fields into manifest TO.** Inside
   `computeApplicationManifestAllUniversalFlatEntityMaps`, after each
   `flatObjectMetadata` is added, call
   `buildDefaultFlatFieldMetadatasForCustomObject({ skipNameField: true })`
   and merge its eight rows into `flatFieldMetadataMaps`. Apps that
   explicitly declare a field with one of the system names (e.g. an
   `id: TEXT` regression) are left untouched so the existing
   `validateObjectMetadataSystemFieldsIntegrity` validator still
   surfaces the mistake.

3. **2-5 workspace upgrade migration:**
   `2-5-workspace-command-1798600000000-refactor-system-field-universal-identifiers.command.ts`
   rewrites every existing `isSystem: true` field whose name is one of
   the eight standard names to the new v5 identifier (direct
   `UPDATE core."fieldMetadata"` on the `universalIdentifier` column —
   no FK references it, all DB joins are on the per-workspace `id`
   PK), then invalidates the `flatFieldMetadataMaps` and
   `flatObjectMetadataMaps` cache entries. Direct analogue of
   `1-21-workspace-command-1775500013000-refactor-navigation-commands`.

After (1)+(2)+(3) ship, the dispatcher computes an empty diff for
system fields on app re-installs: TO and FROM contain the same eight
rows with the same v5 universalIdentifiers and the same scaffolder
defaults. The validator path is never hit.

## CI

`sync-application-system-fields-auto-attached.integration-spec.ts`
exercises three scenarios that would have caught this regression:

- First sync of an app whose object declares zero system fields ends
  with the eight rows present and v5-addressed.
- Re-syncing the identical manifest is a no-op.
- A re-sync after a manually-cleared `defaultValue` on `createdBy`
  (the exact post-2.5-backfill shape) succeeds — this is the case
  that failed on prod after PR #20581 / the rolling 2.5 upgrade.

The existing dev-sync vitest suite never exercised
`installApplication` directly because the LOCAL sourceType short-
circuits the install handler, so the bug stayed silent until the CD
pipeline hit it on the registry-install path.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-15 16:06:20 +02:00
..
2026-05-04 11:09:34 +02:00
2026-05-15 13:08:34 +02:00