fix: detect app rename via auto-injected stable id#1118
fix: detect app rename via auto-injected stable id#1118
Conversation
Inject a stable `id: "app-<uuid>"` field into `defineConfig({...})` on
first apply/generate run, and stamp every managed resource with an
`sdk-app-id` metadata label. Ownership is now determined by the stable
id rather than the app name, so renaming the app (or its resources)
cleanly removes the old resources before creating the new ones.
Closes tailor-inc/platform-planning#1070
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: ac60416 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
⚡ pkg.pr.new@tailor-platform/sdk@tailor-platform/create-sdk
|
📖 Docs Consistency Check
|
| File | Issue | Suggested Fix |
|---|---|---|
packages/sdk/docs/cli/application.md |
The apply and generate commands modify tailor.config.ts on first run by injecting an id field, but this behavior is not documented in the CLI reference |
Add a note under both apply and generate command sections mentioning the config file modification |
Details
What the implementation does:
- On first
applyorgeneraterun, the SDK automatically injects anid: "app-<uuid>"field into thedefineConfig({...})call intailor.config.ts - This is implemented in
packages/sdk/src/cli/shared/config-id-injector.ts - This is called from both
packages/sdk/src/cli/commands/apply/apply.tsandpackages/sdk/src/cli/commands/generate/service.ts
What the documentation says:
- ✅
packages/sdk/docs/configuration.md(lines 22, 32): Properly documents that "On firstapply(orgenerate), the SDK injects anid: "app-<uuid>"field into yourdefineConfig({...})call" - ✅
packages/sdk/src/types/app-config.ts(lines 43-47): Proper JSDoc explaining the auto-managedidfield - ✅
example/tailor.config.ts(line 93): Shows the injectedidfield in practice - ❌
packages/sdk/docs/cli/application.md: Neither theapplynorgeneratecommand documentation mentions this file modification behavior
Why this matters:
- Users should be aware that running these commands will modify their config file on first run
- This is similar to how package managers document that they modify lock files
- The CLI docs already include manual sections for important behavioral details (e.g., "Migration Handling" and "Schema Check" under
apply) - Users should know to commit this change to version control
Recommended Actions
Add a note to both the apply and generate command sections in packages/sdk/docs/cli/application.md (similar to the existing "Migration Handling" and "Schema Check" sections under apply):
For generate command (after line 90):
**Config File Modification:**
On first run, `generate` automatically injects a stable `id: "app-<uuid>"` field into your `defineConfig({...})` call in `tailor.config.ts`. This identifier is used to track your application across renames. The generated id should be committed to version control. See [Configuration](../configuration.md#application-settings) for details.For apply command (after line 135, alongside "Migration Handling" and "Schema Check"):
**Config File Modification:**
On first run, `apply` automatically injects a stable `id: "app-<uuid>"` field into your `defineConfig({...})` call in `tailor.config.ts`. This identifier is used to track your application across renames. The generated id should be committed to version control. See [Configuration](../configuration.md#application-settings) for details.Note: Since application.md contains <!-- politty:... --> markers for auto-generated content, these notes should be added in the manual sections (outside the markers), similar to how "Migration Handling" is documented.
This comment has been minimized.
This comment has been minimized.
Move id auto-injection from `generate` (read-only) to `apply` only —
running `generate` against starter templates was mutating their
`tailor.config.ts`, breaking the cross-platform "Generate consistency"
CI check. Also validate any user-provided id against the metadata
label regex (`^[a-z][a-z0-9_-]{0,62}$`) so that an invalid id fails
loudly instead of producing an unusable label downstream.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
Add `AppConfigSchema` in `parser/app-config/schema.ts` and run it
inside `loadConfig()` so that an invalid config — most importantly an
`id` that does not match the metadata label regex
`^[a-z][a-z0-9_-]{0,62}$` — fails loudly before the SDK starts wiring
resources. Builder-bearing fields (`auth`, `idp`, `db`, ...) are
accepted as opaque values; their shapes are still validated by their
own factory functions and parser-level schemas.
Follows the existing "rich configure type / minimal parser shape"
pattern: `AppConfigParsed` is generated by zinfer from the new schema,
and `expectTypeOf<AppConfig>().toExtend<AppConfigParsed>()` enforces
that the hand-written `AppConfig` interface stays a structural
super-type of the validated shape.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
It is only consumed by `apply` now, so colocate it with the other apply-only modules instead of leaving it in `cli/shared/`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move the `app-` prefix out of the user-facing `id` value: configs now hold a plain UUID, and the SDK adds the label-compatible `app-` prefix when stamping `sdk-app-id` and stripping/comparing it in ownership checks. This keeps the user-facing format clean (just a UUID) while still satisfying the platform metadata label-value regex internally. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drop the `app-` prefix from the e2e test's sharedTestAppId so the injected `id` field passes the new UUID-only validation in ensureConfigId(). The metadata-side `app-` prefix is now added at the boundary (label.ts), so configs only need a plain UUID. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code Metrics Report (packages/sdk)
Details | | main (fa0de36) | #1118 (7b2aca6) | +/- |
|--------------------|----------------|-----------------|-------|
+ | Coverage | 60.7% | 60.9% | +0.2% |
| Files | 357 | 359 | +2 |
| Lines | 12120 | 12246 | +126 |
+ | Covered | 7359 | 7470 | +111 |
+ | Code to Test Ratio | 1:0.4 | 1:0.4 | +0.0 |
| Code | 79761 | 80514 | +753 |
+ | Test | 32753 | 33098 | +345 |Code coverage of files in pull request scope (65.3% → 66.2%)SDK Configure Bundle Size
Runtime Performance
Type Performance (instantiations)
Reported by octocov |
Summary
id: "app-<uuid>"intodefineConfig({...})on firstapply/generateand check it intotailor.config.ts.sdk-app-idmetadata label, alongside the existingsdk-name/sdk-versionlabels.apply.defineConfigfrom another file are skipped on the wrapper; the SDK injects into the file that actually contains the call.Closes tailor-inc/platform-planning#1070.
How the rename detection works
isOwnedByApp(labels, appName, appId)— strict id-precedence: if remote carriessdk-app-id, only an id match counts; otherwise fall back tosdk-name. This prevents an unrelated app that happens to share the samenamefrom being treated as ours.application.tsfetches metadata for every existing application in the workspace and detects "renamed-away" apps by id match — they are scheduled for deletion before the new app is created.isOwnedByAppfor both conflict checks and orphan deletion.Test plan
isOwnedByApp/hasMatchingSdkVersion(9 cases).ensureConfigId(8 cases: injection, idempotency, wrapper detection, malformedid, multipledefineConfigcalls).application.test.ts— 4 new rename scenarios (rename detected by id, same-id same-name no-op, unrelated apps untouched,forRemovalalso deletes id-matched renamed apps).e2e/apply.test.ts— uses a shared per-describeid so multi-config tests model a stable app across rewrites.pnpm test— 152 files / 2209 tests passing.pnpm exec turbo run check— 43/43 success.🤖 Generated with Claude Code