Files
twenty/packages
martmull 67ed2689ce Unify manifest apply pipeline between application install and dev sync (#22921)
First step of unifying application behaviors across sources (NPM,
TARBALL, LOCAL), following up on #22868. No storage layout changes.

## Problem

Marketplace/tarball installs (`ApplicationInstallService`) and CLI dev
sync (`ApplicationDevelopmentService`) each implemented the second half
of application delivery — metadata sync, SDK client generation,
registration refresh — with quietly diverging behavior:

- Install decided SDK regeneration on `!isVersionUpgrade` (application
row exists), dev sync on `!application.version`. The version-based check
is the robust one: a failed first install leaves an application row
without a version, and the row-based check then skipped SDK generation
on retry unless the schema changed.
- Install refreshed the registration manifest (`updateFromManifest`)
without syncing its variable schemas, while catalog sync, tarball
upload, and dev sync all do. An NPM install that bumped
`latestAvailableVersion` with new `serverVariables` left the
registration's variable schemas stale until the next catalog cron.
- The guards protecting shared registrations from workspace writes
(npm-sourced or not owned by the workspace) lived only inside dev sync's
`syncRegistrationMetadata`.

## Change

New `ApplicationManifestApplyService` (application-manifest module) owns
those steps for both flows:

- `applyManifestToWorkspace`: `synchronizeFromManifest` + SDK client
generation when it's the first successful apply (no persisted version)
or the schema changed. Used by install and dev sync.
- `refreshRegistrationFromManifest`: guarded registration update +
variable schema sync. Callers acting for a workspace (dev sync) pass
`onlyIfOwnedByWorkspaceId`, moving the npm/ownership guard into the
shared service; installs pass `latestAvailableVersion` +
`preventVersionDowngrade` as before. Returns whether anything was
written so dependent side effects (registration asset store in dev sync)
skip together with it.
- `ApplicationRegistrationService.updateFromManifest` now returns
whether it wrote, so variable schemas are only synced from manifests
that were actually applied — previously a downgraded install would still
have synced variables from the older manifest if we had naively added
the sync there.

Install and dev services lose the duplicated logic (and their direct
`SdkClientGenerationService` / variable-service dependencies); hooks and
file writes stay where they were.

## Behavior deltas (all deliberate)

- NPM/TARBALL installs now sync registration variable schemas when they
refresh the registration.
- Retrying a failed first install regenerates the SDK client even when
the schema diff is empty.

## Follow-ups (agreed plan, separate PRs)

Single semver version gate; one registration-metadata writer with lock
coverage for dev-sync/tarball; unified upgrade incl. TARBALL;
recoverable upgrades + stale file cleanup; tarball storage decoupled
from the owner workspace; orphan cleanup cron; faster public-asset
serving (ETag/304); parallel install file writes; PREBUILT execution
mode for app logic functions.

## Validation

- New spec `application-manifest-apply.service.spec.ts` (9 tests: SDK
generation matrix, ownership/npm guards, downgrade-skip short-circuits
variable sync)
- All 26 application suites pass; typecheck, oxlint (type-aware) and
oxfmt green on twenty-server

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

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22921?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-07-15 19:36:01 +02:00
..
2026-07-15 18:58:32 +02:00