Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Refactor createConfigExtensionSpecification to support direct deployConfig and transform params#6908

Draft
ryancbahan wants to merge 1 commit intomainfrom
02-27-rcb_contract-migration-1
Draft

Refactor createConfigExtensionSpecification to support direct deployConfig and transform params#6908
ryancbahan wants to merge 1 commit intomainfrom
02-27-rcb_contract-migration-1

Conversation

@ryancbahan
Copy link
Contributor

@ryancbahan ryancbahan commented Feb 28, 2026

Summary

Refactors createConfigExtensionSpecification to accept optional deployConfig, transformLocalToRemote, and transformRemoteToLocal parameters directly, alongside the existing transformConfig. This is a prerequisite for the app module contracts migration — it unblocks all per-module PRs (#6909#6915).

Why this is necessary

Today, createConfigExtensionSpecification derives both forward (transformLocalToRemote) and reverse (transformRemoteToLocal) transforms from a single transformConfig parameter. There is no way to express "no forward transform"resolveAppConfigTransform always returns a function.

The contract migration requires exactly this: each core app config module needs to stop sending CLI-transformed payloads and instead send TOML-shaped data directly via deployConfig, while preserving the reverse transform (since the server still returns Layer 2 field names and app config link/pull depends on transformRemoteToLocal).

Without this change, per-module migrations would have to either:

  1. Hack transformConfig with dummy forward functions that are never called
  2. Switch to createContractBasedModuleSpecification, which removes the reverse transform entirely and breaks app config link and app config pull

What changes

createConfigExtensionSpecification now accepts three new optional parameters:

  • deployConfig — passed through to the extension spec. When set, the deploy path uses this instead of transformLocalToRemote (the existing fallback chain in extension-instance.ts:211 is deployConfig ?? transformLocalToRemote ?? undefined).
  • transformLocalToRemote — overrides the forward transform derived from transformConfig. Can be left undefined to express "no forward transform."
  • transformRemoteToLocal — overrides the reverse transform derived from transformConfig.

Resolution logic:

transformLocalToRemote = direct param ?? (derive from transformConfig if present) ?? undefined
transformRemoteToLocal = direct param ?? derive from transformConfig/schema

Backward compatibility

  • transformConfig is now optional but fully supported — all existing specs using it are unchanged
  • When only transformConfig is provided, behavior is identical to before
  • Direct params take precedence over transformConfig-derived values when both are provided
  • No existing spec files are modified in this PR

How per-module PRs (#6909#6915) use this

Each module migrates from:

createConfigExtensionSpecification({
  identifier: 'branding',
  schema: BrandingSchema,
  transformConfig: {name: 'name', app_handle: 'handle'},
})

To:

createConfigExtensionSpecification({
  identifier: 'branding',
  schema: BrandingSchema,
  deployConfig: async (config) => configWithoutFirstClassFields(config),
  transformRemoteToLocal: (content: object) => ({
    name: (content as {name: string}).name,
    handle: (content as {app_handle: string}).app_handle,
  }),
})

This sends TOML-shaped data directly (via deployConfig), with no forward transform. The server's write adapter normalizes to Layer 2 for storage. The reverse transform is preserved for app config link/pull.

Test plan

  • Existing transformConfig usage still derives both transforms correctly
  • deployConfig + transformRemoteToLocal work without transformConfig (forward transform is undefined)
  • Direct params take precedence over transformConfig-derived values
  • All 47 existing specification integration tests pass

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) [email protected]

@github-actions
Copy link
Contributor

github-actions bot commented Feb 28, 2026

Coverage report

St.
Category Percentage Covered / Total
🟡 Statements 78.81% 14514/18417
🟡 Branches 73.16% 7221/9870
🟡 Functions 79.01% 3690/4670
🟡 Lines 79.15% 13721/17336

Test suite run success

3781 tests passing in 1449 suites.

Report generated by 🧪jest coverage report action from 8b9720e

@github-actions
Copy link
Contributor

We detected some changes at packages/*/src and there are no updates in the .changeset.
If the changes are user-facing, run pnpm changeset add to track your changes and include them in the next release CHANGELOG.

Caution

DO NOT create changesets for features which you do not wish to be included in the public changelog of the next CLI release.

@ryancbahan ryancbahan force-pushed the 02-27-rcb_contract-migration-1 branch from ff7c0de to 8b9720e Compare February 28, 2026 02:47
const transformLocalToRemote =
spec.transformLocalToRemote ?? (spec.transformConfig ? resolveAppConfigTransform(spec.transformConfig) : undefined)
const transformRemoteToLocal =
spec.transformRemoteToLocal ?? resolveReverseAppConfigTransform(spec.schema, spec.transformConfig)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transformRemoteToLocal is now always set even when transformConfig is omitted

createConfigExtensionSpecification now computes const transformRemoteToLocal = spec.transformRemoteToLocal ?? resolveReverseAppConfigTransform(spec.schema, spec.transformConfig) and resolveReverseAppConfigTransform returns a default reverse-transform when transformConfig is undefined. This means every config extension spec will have a reverse transform unless it explicitly sets transformRemoteToLocal: undefined (not really expressible) or bypasses the factory. Previously, transformConfig was required, so the behavior was explicit.

Why this is risky: callers relying on “no reverse transform” semantics may see keys dropped, content nested according to schema, or input mutation (see next comment), leading to confusing diffs or broken local config; and potentially omitting server fields if the transformed output is re-sent.

Scale: affects any configuration extension spec created without transformConfig (now allowed by types).

@binks-code-reviewer
Copy link

🤖 Code Review · #projects-dev-ai for questions
React with 👍/👎 or reply — all feedback helps improve the agent.

Complete - 1 findings

📋 History

✅ 1 findings

@ryancbahan ryancbahan changed the title update spec transform logic for extensions Update spec transform logic for extensions Feb 28, 2026
@ryancbahan ryancbahan changed the title Update spec transform logic for extensions Refactor createConfigExtensionSpecification to support direct deployConfig and transform params Mar 2, 2026
@ryancbahan ryancbahan marked this pull request as draft March 2, 2026 19:48
transformConfig?: TransformationConfig | CustomTransformationConfig
deployConfig?: ExtensionSpecification<TConfiguration>['deployConfig']
transformLocalToRemote?: ExtensionSpecification<TConfiguration>['transformLocalToRemote']
transformRemoteToLocal?: ExtensionSpecification<TConfiguration>['transformRemoteToLocal']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would we feel about making transformRemoteToLocal explicit? It's an override, so the default case should be not to implement it at all -- at which point you get the default behavior of using the contract for reverse transform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants