You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Purpose: Identify the cdktf-named surface areas, the tensions and user-impact each carries, and the known mitigation patterns. This issue does not decide any of these items - each area is its own design problem with its own discussion or follow-up issue. Time horizon: Multi-release, gradual. Not scoped to a single release.
Background
v0.22 (the first cdktn release) intentionally preserved the entire cdktf-named configuration and identity surface to keep the upgrade cost near zero. The cdktn CLI today reads cdktf.json, writes to cdktf.out/, honors CDKTF_* env vars, caches under ~/.cdktf, emits a cdktfVersion context key on synthesized stacks, and marks class instances with Symbol.for("cdktf/*") keys. That commitment is explicit in upgrade-guide-v0-22:
The cdktn cli should be fully compatible with existing CDK for Terraform projects for at least v0.22.
The rename achieved minimal friction — friction was low enough that no cdktn migrate command was created.
Since then the project has stabilized (CVEs cleared, EOL runtimes updated, unmaintained deps replaced) and contributor velocity is up. Incoming opportunities is expected to bring in new users. The friction is now inverted: the split identity is creating real bugs and unclear guidance for new contributors:
cdktn-io/cdktn-provider-project deprecated the cdktf package.json metadata key in favor of cdktn, and the CLI's prebuilt-providers.ts:219 reads cdktn-first (packageJson.cdktn?.provider ?? packageJson.cdktf?.provider). One surface is cdktn-first.
The rest of the surface is still cdktf-only with no cdktn alternative.
New users see cdktn synth write to cdktf.out/ with a cdktfVersion metadata key. New contributors adding env vars or config keys have no clear rule for which name to use.
Unclear guidance is producing real bugs in downstream tooling.
This issue inventories the surface so it can be addressed area-by-area across multiple releases.
Scope
In scope: identifying and tracking the cdktf-named surface areas that will need attention as the project moves toward a fully cdktn identity, gradually, across multiple releases. Highlighting tensions and user-impact for each.
Out of scope: Terraform-vs-OpenTofu behavioral divergence — selection of the underlying binary already works today via the TERRAFORM_BINARY_NAME env var (packages/cdktn/src/util.ts:10, packages/@cdktn/commons/src/terraform.ts:6, packages/@cdktn/provider-schema/src/provider-schema.ts:24). The unsolved problem is that terraform and tofu don't behave identically — the known provider codegen failure when TERRAFORM_BINARY_NAME=tofu is one manifestation. That belongs in its own tracking issue, not here.
Deciding the specific implementation for any one area below should be done by RFC(s) tracked against this parent issue
Preferred mitigation pattern: feature flags
Where surface changes affect serialized state, deployed stacks, or cross-package runtime behavior, feature flags (the existing FUTURE_FLAGS mechanism in packages/cdktn/src/features.ts) should be the preferred mitigation. They let new projects (bootstrapped via cdktn init) opt into the cdktn-named surface by default while letting existing projects keep current behavior until they explicitly opt in.
For pure read paths with no state implications (config-file lookup, env var reads, output dir defaults), a simple cdktn-first/cdktf-fallback may be sufficient without a flag. The reference implementation already exists at packages/@cdktn/cli-core/src/lib/dependencies/prebuilt-providers.ts:219.
Problem areas
Each area below is described with its current state, the tension it carries, the user-impact of changing it, and any known mitigation. No area is being decided here.
1. Config file: cdktf.json
Current: packages/@cdktn/commons/src/config.ts:31 — const CONFIG_FILE = "cdktf.json". Only this name is read.
Tension: New users expect cdktn.json. Existing users have cdktf.json checked into thousands of projects.
User impact of a change: Adding cdktn.json as a primary lookup with cdktf.json fallback is low risk (pure read path). Removing cdktf.json support is high risk.
Tension: New users expect cdktn.out/. Existing CI/CD pipelines, .gitignore files, deploy scripts, and IDE configs hard-code cdktf.out/.
User impact of a change: Flipping the default is potentially breaking for any pipeline that assumes the path. Decoupling the change from the config-file change vs. coupling them is itself an open question.
3. Environment variables: CDKTF_*
Eight variables, no CDKTN_* aliases:
Variable
Purpose
CDKTF_OUTDIR
Output dir override
CDKTF_TARGET_STACK_ID
Single-stack synthesis filter
CDKTF_CONTEXT_JSON
Context values as JSON
CDKTF_LOG_LEVEL
Log verbosity
CDKTF_LOG_FILE_DIRECTORY
Log file output path
CDKTF_HOME
Cache/config home
CDKTF_DISABLE_PLUGIN_CACHE_ENV
Disable plugin cache
CDKTF_CONTINUE_SYNTH_ON_ERROR_ANNOTATIONS
Don't fail on annotations
CDKTF_EXPERIMENTAL_PROVIDER_SCHEMA_CACHE_PATH
Provider schema cache
Tension: Users set these in shell profiles, .envrc, CI secrets. New contributors don't know whether to add CDKTF_* or CDKTN_* for a new var. See feat(lib): allow disabling creation stacks #215
User impact of a change: Pure read paths; dual-read is low risk. Removing legacy is shell-profile-breaking.
4. Cache / home directory: ~/.cdktf
Current: packages/@cdktn/commons/src/checkpoint.ts:19 — hardcoded ~/.cdktf (overridable via CDKTN_HOME / CDKTF_HOME).
Tension: Cached provider schemas, project-tracking IDs, telemetry data live there. Moving it leaves stale data behind.
User impact of a change: Either silently migrate cached data, leave both, or accept duplication. All have trade-offs.
Current: packages/cdktn/src/app.ts:120 sets node.setContext("cdktfVersion", version). packages/cdktn/src/terraform-stack.ts:88,220,350 reads it and serializes version: this.cdktfVersion into the synthesized output.
Tension: This value lands in cdk.tf.json. Terraform itself ignores it, but downstream tooling and snapshot tests may read it.
User impact of a change: Renaming the key changes synthesized output (snapshot-test diffs). Aliasing (emit both, read either) avoids that.
6. Provider metadata key in package.json — reference example, already cdktn-first
Current: packages/@cdktn/cli-core/src/lib/dependencies/prebuilt-providers.ts:219 — packageJson.cdktn?.provider ?? packageJson.cdktf?.provider. Upstream cdktn-io/cdktn-provider-project has deprecated the cdktf key.
Tension: None — this is the reference pattern. Listed here to make it discoverable and document it as the canonical example for other surface changes.
7. Logger appender name: "cdktf"
Current: packages/@cdktn/commons/src/logging.ts:83 — log4js appender literally named "cdktf".
Tension: Internal-only; not user-visible unless someone configures their own log4js consumer that filters by appender name.
User impact of a change: Negligible.
8. Runtime symbols and codegen logical IDs (highest-impact area)
This is the area with the largest cross-package compatibility implications. Investigation summary (see appendix below):
18 Symbol.for("cdktf/*") keys marking class identity for tree-walking type guards (e.g. TerraformStack.isStack(x), isTerraformResource(x)). Pattern is uniform: constructor writes the symbol, static method reads via SYMBOL in x.
5 Symbol.for("@cdktf/core.TokenMap.*") keys used as both marker AND cache key for token caching.
Logical ID prefixes: __cdktf_module_asset (terraform-module-asset.ts:81), glob.__cdktfTokenMap (tokens/token-map.ts:40).
The cross-version concern:
An older @cdktn/provider-* package published when cdktn-cli wrote Symbol.for("cdktf/X") may not be recognized by a newer cdktn framework release that only writes/reads Symbol.for("cdktn/X"). Type guards return false, the construct tree walk misses the provider's resources, and synthesis silently produces wrong output. This is what would break if symbols flip without compatibility.
Known mitigation — dual-symbol pattern (feature-flag gated):
For the 11 class-identity symbols, the pattern is mechanically simple and bidirectionally compatible:
constOLD=Symbol.for("cdktf/TerraformElement");constNEW=Symbol.for("cdktn/TerraformElement");// Constructor: write bothObject.defineProperty(this,NEW,{value: true});Object.defineProperty(this,OLD,{value: true});// Type guard: read eitherreturnNEWinx||OLDinx;
This means:
Old provider (writes cdktf/X only) + new core (checks both) → works
New provider (writes both) + old core (checks cdktf/X only) → works
For the 5 TokenMap symbols the same idea works but is more involved because the symbol is the data key, not just a marker — dual-writing the cached value at both keys, dual-reading on lookup. Solvable, but adds plumbing.
Logical ID prefixes (__cdktf_*) are different: they appear in synthesized output, so flipping them affects Terraform state. This is the strongest argument for a feature flag (new projects opt in; existing deployed stacks keep current IDs until the user runs a state rewrite).
Reference precedent: AWS CDK's constructs library uses Symbol.for("constructs.Construct") with the same duck-typing-across-realms pattern. Their evolution history is worth reviewing before settling on an approach.
Note: lessons learned from dual-dependency (cdktf + cdktn) design, which we wish to avoid going forward
9. Synthesized output filename: cdk.tf.json
Current: Synthesized files at <outdir>/stacks/<name>/cdk.tf.json. The prefix is cdk, not cdktf — it just means "produced by a CDK".
Tension: Likely none. cdk is generic; Terraform loads any *.tf.json in a directory regardless of prefix; the name doesn't carry the cdktf brand.
User impact of a change: Renaming to cdktn.tf.json would still synthesize correctly but would churn snapshots, downstream tooling that greps for cdk.tf.json, and documentation/examples — for no clear gain.
Surfaced for completeness; current expectation is no action.
10. Already at cdktn (no action)
For completeness:
CLI binary name (cdktn)
Telemetry product name (packages/@cdktn/commons/src/errors.ts:11, checkpoint.ts:84)
Debug output reports both cdktn and cdktf versions (debug.ts:393-399)
Cross-cutting open questions
These apply across multiple areas above and don't belong to any single sub-issue:
Cadence: Each area can move independently across releases. Is there a desired ordering (e.g. read-path-only items first, state-affecting items last)?
Deprecation timeline: When does cdktf-named fallback start emitting warnings? When (if ever) is it removed? Per-area or one timeline?
Warning ergonomics: Print once per session vs. every invocation when legacy is detected.
Versioning: Does a default flip warrant a major bump? Per area, or batched into one release?
Contributor guidance: What is the rule today for someone adding a new env var or config key? (No current guidance — the absence is part of the bug.)
State-rewrite UX: For items that change synthesized output (logical IDs in §8), what does the user experience look like for an existing deployed stack — print a terraform state mv plan? Require explicit opt-in? Refuse without backup confirmation?
Prior art posture: SDD - 001-cdktn-package-rename/ (FR-008..FR-013 and research.md) explicitly preserves this surface. spec driven folders are left as frozen v0.22 record (ADR)
Investigation of all Symbol.for("cdktf/*") and Symbol.for("@cdktf/core.TokenMap.*") sites in packages/cdktn/src/, focused on whether a dual-symbol (write both, read either) backward-compat pattern is viable.
Class-identity symbols (11 sites, uniform pattern):
Every one is a class identity marker. Constructor writes Object.defineProperty(this, SYMBOL, { value: true }); a static type guard reads SYMBOL in x. Used for cross-realm/cross-package duck-typing (the cross-package equivalent of instanceof) in tree-walk predicates like findAll(TerraformProvider.isTerraformProvider).
terraform-module-asset.ts:14,79,83 — Symbol.for("cdktf.TerraformModuleAsset") (used as data key on stack, not just marker — closer to the TokenMap pattern below)
Dual-symbol viability for class-identity sites: High. The change is mechanical — dual-write in the constructor, dual-read in the type guard. Symbol lookups are O(1). Bidirectional compatibility (old provider + new core, new provider + old core).
TokenMap symbols (5 sites, different pattern): packages/cdktn/src/tokens/private/token-map.ts:20-105. The symbol is both marker AND property key for the cached value — cachedValue(x, sym, prod) reads from and writes to the same symbol slot on x.
@cdktf/core.TokenMap.STRING
@cdktf/core.TokenMap.LIST
@cdktf/core.TokenMap.NUMBER
@cdktf/core.TokenMap.NUMBER_LIST
@cdktf/core.TokenMap.MAP
Dual-symbol viability for TokenMap: Medium. Solvable by dual-writing the cached value under both keys and trying both on read. Adds plumbing, threading a legacy-symbol arg through cachedValue and each register* call. Failure mode without the fix: cache miss → token re-registered → potential plan-mismatch with duplicate token keys.
DependableTrait symbol (tokens/private/dependency.ts:54,94): Symbol.for("@aws-cdk/core.DependableTrait") — used for trait storage. Same pattern as TokenMap.
Logical ID prefixes (separate from symbols, but related concern):
These land in synthesized output and therefore in Terraform state. Flipping them is a state-affecting change. Strongest case for feature-flag gating among all the items here.
Conclusion: A dual-symbol pattern is viable as a known mitigation for class-identity symbols (the bulk of the 18). TokenMap requires more care. Logical ID prefixes warrant a feature flag. None of this is decided here — surfacing for design discussion in the appropriate sub-issue.
Tracking: cdktn Configuration & Identity Surface
Background
v0.22 (the first
cdktnrelease) intentionally preserved the entirecdktf-named configuration and identity surface to keep the upgrade cost near zero. ThecdktnCLI today readscdktf.json, writes tocdktf.out/, honorsCDKTF_*env vars, caches under~/.cdktf, emits acdktfVersioncontext key on synthesized stacks, and marks class instances withSymbol.for("cdktf/*")keys. That commitment is explicit inupgrade-guide-v0-22:The rename achieved minimal friction — friction was low enough that no
cdktn migratecommand was created.Since then the project has stabilized (CVEs cleared, EOL runtimes updated, unmaintained deps replaced) and contributor velocity is up. Incoming opportunities is expected to bring in new users. The friction is now inverted: the split identity is creating real bugs and unclear guidance for new contributors:
cdktn-io/cdktn-provider-projectdeprecated thecdktfpackage.json metadata key in favor ofcdktn, and the CLI'sprebuilt-providers.ts:219reads cdktn-first (packageJson.cdktn?.provider ?? packageJson.cdktf?.provider). One surface is cdktn-first.cdktn synthwrite tocdktf.out/with acdktfVersionmetadata key. New contributors adding env vars or config keys have no clear rule for which name to use.This issue inventories the surface so it can be addressed area-by-area across multiple releases.
Scope
In scope: identifying and tracking the cdktf-named surface areas that will need attention as the project moves toward a fully
cdktnidentity, gradually, across multiple releases. Highlighting tensions and user-impact for each.Out of scope: Terraform-vs-OpenTofu behavioral divergence — selection of the underlying binary already works today via the
TERRAFORM_BINARY_NAMEenv var (packages/cdktn/src/util.ts:10,packages/@cdktn/commons/src/terraform.ts:6,packages/@cdktn/provider-schema/src/provider-schema.ts:24). The unsolved problem is thatterraformandtofudon't behave identically — the known provider codegen failure whenTERRAFORM_BINARY_NAME=tofuis one manifestation. That belongs in its own tracking issue, not here.Preferred mitigation pattern: feature flags
Where surface changes affect serialized state, deployed stacks, or cross-package runtime behavior, feature flags (the existing
FUTURE_FLAGSmechanism inpackages/cdktn/src/features.ts) should be the preferred mitigation. They let new projects (bootstrapped viacdktn init) opt into thecdktn-named surface by default while letting existing projects keep current behavior until they explicitly opt in.For pure read paths with no state implications (config-file lookup, env var reads, output dir defaults), a simple cdktn-first/cdktf-fallback may be sufficient without a flag. The reference implementation already exists at
packages/@cdktn/cli-core/src/lib/dependencies/prebuilt-providers.ts:219.Problem areas
Each area below is described with its current state, the tension it carries, the user-impact of changing it, and any known mitigation. No area is being decided here.
1. Config file:
cdktf.jsonpackages/@cdktn/commons/src/config.ts:31—const CONFIG_FILE = "cdktf.json". Only this name is read.cdktn.json. Existing users havecdktf.jsonchecked into thousands of projects.cdktn.jsonas a primary lookup withcdktf.jsonfallback is low risk (pure read path). Removingcdktf.jsonsupport is high risk.2. Output directory:
cdktf.out/packages/@cdktn/commons/src/config.ts:56(CONFIG_DEFAULTS.output = "cdktf.out") andpackages/cdktn/src/app.ts:101(this.outdir = config.outdir ?? process.env.CDKTF_OUTDIR ?? "cdktf.out").cdktn.out/. Existing CI/CD pipelines,.gitignorefiles, deploy scripts, and IDE configs hard-codecdktf.out/.3. Environment variables:
CDKTF_*Eight variables, no
CDKTN_*aliases:CDKTF_OUTDIRCDKTF_TARGET_STACK_IDCDKTF_CONTEXT_JSONCDKTF_LOG_LEVELCDKTF_LOG_FILE_DIRECTORYCDKTF_HOMECDKTF_DISABLE_PLUGIN_CACHE_ENVCDKTF_CONTINUE_SYNTH_ON_ERROR_ANNOTATIONSCDKTF_EXPERIMENTAL_PROVIDER_SCHEMA_CACHE_PATH.envrc, CI secrets. New contributors don't know whether to addCDKTF_*orCDKTN_*for a new var. See feat(lib): allow disabling creation stacks #2154. Cache / home directory:
~/.cdktfpackages/@cdktn/commons/src/checkpoint.ts:19— hardcoded~/.cdktf(overridable viaCDKTN_HOME/CDKTF_HOME).5. Synthesized stack metadata:
cdktfVersioncontext keypackages/cdktn/src/app.ts:120setsnode.setContext("cdktfVersion", version).packages/cdktn/src/terraform-stack.ts:88,220,350reads it and serializesversion: this.cdktfVersioninto the synthesized output.cdk.tf.json. Terraform itself ignores it, but downstream tooling and snapshot tests may read it.6. Provider metadata key in
package.json— reference example, already cdktn-firstpackages/@cdktn/cli-core/src/lib/dependencies/prebuilt-providers.ts:219—packageJson.cdktn?.provider ?? packageJson.cdktf?.provider. Upstreamcdktn-io/cdktn-provider-projecthas deprecated thecdktfkey.7. Logger appender name:
"cdktf"packages/@cdktn/commons/src/logging.ts:83— log4js appender literally named"cdktf".8. Runtime symbols and codegen logical IDs (highest-impact area)
This is the area with the largest cross-package compatibility implications. Investigation summary (see appendix below):
Symbol.for("cdktf/*")keys marking class identity for tree-walking type guards (e.g.TerraformStack.isStack(x),isTerraformResource(x)). Pattern is uniform: constructor writes the symbol, static method reads viaSYMBOL in x.Symbol.for("@cdktf/core.TokenMap.*")keys used as both marker AND cache key for token caching.__cdktf_module_asset(terraform-module-asset.ts:81),glob.__cdktfTokenMap(tokens/token-map.ts:40).The cross-version concern:
An older
@cdktn/provider-*package published when cdktn-cli wroteSymbol.for("cdktf/X")may not be recognized by a newer cdktn framework release that only writes/readsSymbol.for("cdktn/X"). Type guards returnfalse, the construct tree walk misses the provider's resources, and synthesis silently produces wrong output. This is what would break if symbols flip without compatibility.Known mitigation — dual-symbol pattern (feature-flag gated):
For the 11 class-identity symbols, the pattern is mechanically simple and bidirectionally compatible:
This means:
cdktf/Xonly) + new core (checks both) → workscdktf/Xonly) → worksFor the 5 TokenMap symbols the same idea works but is more involved because the symbol is the data key, not just a marker — dual-writing the cached value at both keys, dual-reading on lookup. Solvable, but adds plumbing.
Logical ID prefixes (
__cdktf_*) are different: they appear in synthesized output, so flipping them affects Terraform state. This is the strongest argument for a feature flag (new projects opt in; existing deployed stacks keep current IDs until the user runs a state rewrite).Reference precedent: AWS CDK's
constructslibrary usesSymbol.for("constructs.Construct")with the same duck-typing-across-realms pattern. Their evolution history is worth reviewing before settling on an approach.9. Synthesized output filename:
cdk.tf.json<outdir>/stacks/<name>/cdk.tf.json. The prefix iscdk, notcdktf— it just means "produced by a CDK".cdkis generic; Terraform loads any*.tf.jsonin a directory regardless of prefix; the name doesn't carry thecdktfbrand.cdktn.tf.jsonwould still synthesize correctly but would churn snapshots, downstream tooling that greps forcdk.tf.json, and documentation/examples — for no clear gain.10. Already at
cdktn(no action)For completeness:
cdktn)packages/@cdktn/commons/src/errors.ts:11,checkpoint.ts:84)cdktnandcdktfversions (debug.ts:393-399)Cross-cutting open questions
These apply across multiple areas above and don't belong to any single sub-issue:
cdktf-named fallback start emitting warnings? When (if ever) is it removed? Per-area or one timeline?configuration-file.mdx,environment-variables.mdx, and the next upgrade guide.terraform state mvplan? Require explicit opt-in? Refuse without backup confirmation?001-cdktn-package-rename/(FR-008..FR-013 andresearch.md) explicitly preserves this surface. spec driven folders are left as frozen v0.22 record (ADR)References
../cdk-terrain-docs/content/release/upgrade-guide-v0-22.mdx)RFCs/RENAME.md— strategic renaming protocolRFCs/RENAME-PLAN.md— Release 1 implementation roadmapspecledger/001-cdktn-package-rename/spec.md— Release 1 spec (FR-008..FR-013 preserve cdktf surface)specledger/001-cdktn-package-rename/research.md— Symbol/JSII inventorypackages/@cdktn/cli-core/src/lib/dependencies/prebuilt-providers.ts:219packages/cdktn/src/features.ts(FUTURE_FLAGS)Appendix: Symbol.for() investigation
Investigation of all
Symbol.for("cdktf/*")andSymbol.for("@cdktf/core.TokenMap.*")sites inpackages/cdktn/src/, focused on whether a dual-symbol (write both, read either) backward-compat pattern is viable.Class-identity symbols (11 sites, uniform pattern):
Every one is a class identity marker. Constructor writes
Object.defineProperty(this, SYMBOL, { value: true }); a static type guard readsSYMBOL in x. Used for cross-realm/cross-package duck-typing (the cross-package equivalent ofinstanceof) in tree-walk predicates likefindAll(TerraformProvider.isTerraformProvider).app.ts:17,99—Symbol.for("cdktf/App")terraform-element.ts:10,37,52—Symbol.for("cdktf/TerraformElement")terraform-stack.ts:17,95,100—Symbol.for("cdktf/TerraformStack")terraform-resource.ts:40,157,174—Symbol.for("cdktf/TerraformResource")terraform-provider.ts:14,30,39—Symbol.for("cdktf/TerraformProvider")terraform-output.ts:12,46,50—Symbol.for("cdktf/TerraformOutput")terraform-count.ts:5,15,19—Symbol.for("cdktf/TerraformCount")terraform-dynamic-block.ts:9,23,70—Symbol.for("cdktf/TerraformDynamicBlock")terraform-dynamic-expression.ts:8,28,31—Symbol.for("cdktf/TerraformDynamicExpression")terraform-backend.ts:8,18,22—Symbol.for("cdktf/TerraformBackend")terraform-module-asset.ts:14,79,83—Symbol.for("cdktf.TerraformModuleAsset")(used as data key on stack, not just marker — closer to the TokenMap pattern below)Dual-symbol viability for class-identity sites: High. The change is mechanical — dual-write in the constructor, dual-read in the type guard. Symbol lookups are O(1). Bidirectional compatibility (old provider + new core, new provider + old core).
TokenMap symbols (5 sites, different pattern):
packages/cdktn/src/tokens/private/token-map.ts:20-105. The symbol is both marker AND property key for the cached value —cachedValue(x, sym, prod)reads from and writes to the same symbol slot onx.@cdktf/core.TokenMap.STRING@cdktf/core.TokenMap.LIST@cdktf/core.TokenMap.NUMBER@cdktf/core.TokenMap.NUMBER_LIST@cdktf/core.TokenMap.MAPDual-symbol viability for TokenMap: Medium. Solvable by dual-writing the cached value under both keys and trying both on read. Adds plumbing, threading a legacy-symbol arg through
cachedValueand eachregister*call. Failure mode without the fix: cache miss → token re-registered → potential plan-mismatch with duplicate token keys.DependableTrait symbol (
tokens/private/dependency.ts:54,94):Symbol.for("@aws-cdk/core.DependableTrait")— used for trait storage. Same pattern as TokenMap.Logical ID prefixes (separate from symbols, but related concern):
__cdktf_module_asset(terraform-module-asset.ts:81)glob.__cdktfTokenMap(tokens/token-map.ts:40-41)These land in synthesized output and therefore in Terraform state. Flipping them is a state-affecting change. Strongest case for feature-flag gating among all the items here.
Conclusion: A dual-symbol pattern is viable as a known mitigation for class-identity symbols (the bulk of the 18). TokenMap requires more care. Logical ID prefixes warrant a feature flag. None of this is decided here — surfacing for design discussion in the appropriate sub-issue.