---
name: compiler-codegen
description: "Rust compiler pipeline, template codegen (VDOM/IDE), CodeTransform, cached directives, strict slots, IDE error recovery, style preprocessing, CompileTarget, compiler authority/policy/demand/admission"
---

# Compiler & Codegen

Compiler authority, policy, demand, and admission: **normative** text is
the `compiler-architecture.md` contract, a DAG asset owned by the TAMA controller (not a file in this repository).
The skill file
[`references/authority-policy-demand.md`](references/authority-policy-demand.md)
is an operational pointer only. The combined carrier-compiler registry remains
a live migration seam, not the ratified owner. `DefaultCompilationContractId`
is 1:1 with live `ProductKind` (no `facts` dump). `compile_bundle` is a
combined product pass, not a third bus. Cheap Default facts are
`FrameworkSemanticAuthority` over admitted parse only.

## Rust Compiler Architecture

AST-based pipeline. `compile()` orchestrator drives a linear 5-phase pipeline:

```
Vue SFC Source
    |
[Tokenizer]  byte-level SFC tokenization (tokenizer/byte.rs)
    |
[Parser]     builds arena-based template AST + extracts script/style blocks (parser/)
    |
[Style]      typed Vue/Svelte rewrite planning over StyleSyntaxIr
    |
[Script]     macro expansion, binding extraction, component wrapper (script/)
    |
[Template]   render function codegen -- VDOM or Vapor backends (template/)
    |
[Compile]    orchestrates the above, applies CodeTransform, emits output (compile.rs)
```

**Module overview:**

```
compile.rs                # Pipeline orchestrator, options, result types
tokenizer/
+-- byte.rs               # Zero-copy byte-level SFC tokenizer (production)
+-- helpers.rs            # Tokenizer utility functions
+-- types.rs              # Event, QuoteType
parser/
+-- mod.rs                # Syntax state machine (tokenizer events -> AST)
+-- types.rs              # RootNodeScript, RootNodeStyle, RootNodeTemplate
ast/
+-- mod.rs                # TemplateAst (flat arena with O(1) navigation)
+-- builder.rs            # TemplateAstBuilder (incremental AST construction)
+-- types.rs              # AstNode, ElementNode, NodeId, pre-computed flags
script/
+-- mod.rs                # generate_script() entry point
+-- process.rs            # Script setup processing, companion script merging
+-- macros.rs             # defineProps/Emits/Model/Slots/Expose/Options
+-- css_vars.rs           # _useCssVars() injection for v-bind() in styles
template/
+-- oxc/                  # OXC expression parsing for template bindings
|   +-- mod.rs            # parse_template_expressions()
|   +-- scope.rs          # LexicalScopes frames, LexicalScopeId handles, ActiveScope
|   +-- types.rs          # OxcParsedAst, OxcParsedElement, OxcParsedExpression
+-- code_gen/             # Render function codegen
    +-- mod.rs            # generate_template() entry point
    +-- walker.rs         # DFS tree walker (shared by all backends)
    +-- types.rs          # TemplateCodeGen trait, CodeGenOutput
    +-- binding.rs        # BindingResolver (_ctx./$setup. prefix resolution)
    +-- shared/           # Shared codegen helpers
    +-- vdom/             # VDOM render function output (_createElementVNode, etc.)
    +-- vapor/            # Vapor mode output (_template, _renderEffect, etc.)
ide/                      # IDE codegen: TSX or JSX+JSDoc (for LSP/TSGO type checking)
+-- mod.rs                # generate_ide_template() -- Vue template -> valid JSX; IdeScriptOptions, IdeTemplateOptions
+-- script.rs             # generate_ide_script() -- script block -> TS or JS+JSDoc wrapper
+-- script_recover.rs     # Token scanner for macro binding recovery from broken script tails
+-- condition.rs          # v-if/v-else-if/v-else condition chain codegen
+-- template/
    +-- mod.rs            # walk_element/walk_node, cached directive removal, ref conversion
    +-- directives.rs     # v-if -> ternary, v-for -> .map(), v-show -> style
    +-- props.rs          # :prop -> prop={}, @event -> onEvent={}, v-bind spread
style_planner.rs          # Vue authored-v-bind, CSS Modules, and plain-CSS scoping stages
css/
+-- mod.rs                # legacy processStyle/CSS-modules compatibility surface
+-- prepass.rs            # retained until the NAPI/provider cutover
+-- scoped.rs             # retained until the NAPI/provider cutover
+-- modules.rs            # CSS Modules: hash class names
+-- walk.rs               # String-level CSS selector walking
+-- types.rs              # ProcessStyleOptions, ProcessStyleResult
code_transform/
+-- code_transform.rs     # Chunk-based deferred mutation engine (MagicString equivalent)
+-- chunk.rs              # Chunk types (Original, Overwritten, Inserted, InsertedMapped)
+-- source_map.rs         # Source map generation from chunk positions
utils/
+-- oxc/                  # OXC parser utilities
|   +-- bindings/         # Expression binding extraction
|   +-- vue/              # Vue-specific OXC helpers (macro syntax, v-for, v-slot)
+-- vue/                  # Vue runtime helpers (tag detection, patch flags)
```

## Arena-Based Template AST

Parser builds a flat `Vec<AstNode>` arena with O(1) navigation:

```rust
pub struct TemplateAst {
    nodes: Vec<AstNode>,        // flat arena
    root: RootNodeTemplate,
}

pub struct AstNode {
    kind: AstNodeKind,          // Element | Text | Comment | Interpolation
    parent: Option<NodeId>,     // O(1) parent lookup
    index_in_parent: usize,     // O(1) sibling lookup
}
```

`ElementNode` pre-computes metadata during parsing to avoid re-scanning in codegen:

- `tag_type`: Element / Component / SlotOutlet / Template
- `prop_flag`: Bitset of prop characteristics (has class, style, spread, etc.)
- `children_flag`: Bitset of children characteristics (has text, elements, v-if, etc.)
- `children_mode`: Enum for codegen branching (Empty, TextOnly, SingleElement, Mixed, etc.)
- Cached directives: `v_condition`, `v_for`, `v_slot`, `v_once`, `v_ref`

## Template Lexical Scopes (`template/oxc/scope.rs`)

`parse_template_expressions` is the ONE producer of template lexical scope, built
in its existing forward pass over the node arena:

- Each `v-for` alias list and each `v-slot` parameter list opens one persistent
  frame in `OxcParsedAst::scopes` (`LexicalScopes`) holding ONLY the names that
  list declares, linked to its enclosing frame. A list that declares nothing
  opens no frame.
- Every node gets a `LexicalScopeId` handle: `OxcParsedAst::children_scope(id)`
  is what the node's children see; `scope_of(id, ast)` (the parent's children
  scope) is what the node itself sits in; `OxcParsedElement::props_scope` is the
  element's props / dynamic slot name / `v-slot` value scope (own `v-for` aliases
  visible, own slot params NOT). `ide_recovery_scope` on a broken IDE expression
  is a handle too.
- While parsing, expressions resolve names through `ActiveScope`, a multiset the
  pass moves between frames incrementally (leave/enter only the frames that
  differ). `BindingContext::within` and the `v-for` / `v-slot` binding helpers
  read it in place through `verter_parser`'s `EnclosingScope` trait.

Receiving rules: query names through a handle (`LexicalScopes::declares`,
`declares_completion_of`, `own_names`) or the shared `EnclosingScope`; never
flatten a handle's inherited names into a per-element / per-expression list, never
copy a parent's aliases into a nested `v-for` scope, and never walk ancestors to
rediscover a node's scope. Exact lexical identity and order are the frames'
(source order within a frame, innermost frame first along a chain).

## CodeTransform (Deferred Mutations)

All codegen phases use `CodeTransform` -- a chunk-based deferred mutation engine:

```rust
let mut ct = CodeTransform::new(input, &allocator);
ct.overwrite(start, end, replacement);  // deferred
ct.prepend_left(pos, content);          // deferred
let output = ct.build_string();         // single-pass concatenation
```

Key features:

- `cursor_hint`: Accelerates forward-progressing access patterns to amortized O(1)
- `output_delta`: Incremental length tracking avoids full scan
- Pre-allocated chunk capacity: `source_len / 13` (empirically tuned)

## CodeTransform Is the Single Source of Truth (CRITICAL)

**All modifications to generated code MUST go through `CodeTransform` operations** (`overwrite`, `prepend_left`, `append_left`, `move_with_suffix`, etc.). Never apply string replacements, regex transforms, or manual splicing to the output of `build_string()` or to content produced by a `CodeTransform`.

Post-hoc string manipulation breaks sourcemap accuracy: `CodeTransform` generates source maps by tracking chunks (Original, Inserted, Moved, Overwritten). Modifying the string after the transform makes byte offsets in the source map no longer match the content, causing position mismatches in the LSP (e.g. hover landing on the wrong token, go-to-definition jumping to wrong locations).

**Correct:** Use `ct.prepend_left(pos, ".ts")` to insert text at a known position -- chunk list and source map stay consistent.

**Wrong:** Call `content.replace(".vue'", ".vue.ts'")` on the built string -- the source map still reflects the pre-replace byte offsets.

The rule has NO scoped exceptions: the Svelte scoped-CSS renderer (`crates/verter_compiler/src/svelte/runtime/css/render.rs`) edits the original component source through the shared `CodeTransform`'s checked (`try_*`) operations -- whose insertion-affinity chunk model carries the `magic-string` semantics the official `svelte@5.56.10` `render_stylesheet` depends on (content-only `try_update` preserving the replaced range's first-chunk boundary insertions, left/right insertion affinity with per-affinity stacking, `try_remove` clearing interior insertions; pinned by `code_transform/edit_semantics_tests.rs`) -- and generates the css source map (`css.map`) from the SAME transform that built `css.code`. The guard `svelte_css_renderer_uses_code_transform` (`crates/verter_compiler/tests/`) asserts the renderer stays on the shared transform and bans any private edit buffer from the css matcher/render tree.

## Compile artifact schema

`assembly::CompileArtifactSet` owns the immutable terminal schema for
already-produced compiler artifacts. Its constructor validates facts only;
it does not parse, compile, assemble, or publish. Artifact rows keep the
caller-supplied contribution/plan order (identity bytes are not an ordering
key); terminal JSON still sorts artifacts by id so the wire stays
identity-canonical. `CompileArtifact` inputs are mutable builders, while a
validated set exposes immutable accessors and is the only schema container
implementing `Serialize`.

Artifact lineage is the canonical tuple `(SourceUnitId, ProductKind, LanguageId,
producer-owned slot name)`. Revision, source content, output availability, and
provenance are neighbouring facts. Each artifact names its primary source unit,
an `InputBasisId`, a producer `ResultContractId`, and all contributing units.
Typed artifact relations must target another artifact in the same set. Duplicate
artifact/source identities, missing references, and inconsistent revisions of
one source fail closed. Empty available text differs from unavailable content.

`ArtifactSourceUnit` binds each input to its registered SFC-absolute byte `Span`;
external inputs carry their own source identity and absolute extent.
`QualifiedArtifactMap` distinguishes source-projection and runtime-source-map
families, names its generated artifact and nonempty input-space set, and carries
the exact generated `ContentId` and observed `InputBasisId` so maps from older
output bytes or inputs fail closed even when artifact lineage is unchanged.
It carries typed generated ranges separately from authored `Span`s. Map sources
must be part of artifact provenance; ranges must fit their spaces, generated endpoints
must be UTF-8 boundaries, and ambiguous overlapping generated mappings fail.
Unmapped generated text has no authored segment. An absent family is not an
implicit identity map. These mappings describe authored anchors and do not
assert byte-for-byte fidelity or interpolate between differently sized ranges.

Terminal JSON uses schema version 1, explicit `utf8-bytes` coordinates,
hex-encoded full canonical identities, sorted objects/sets and generated-order
segments. JSON/V3/LSP adapters own UTF-16 conversion using source documents.
The schema does not replace the live `ArtifactSet` publication boundary or
`VerterCompileResult` routes.

Vue custom-block parsing (`compile/mod.rs`, `extract_block_ranges` /
the `unknown_nodes()` walk) retains one `VerterCustomBlock` per source
block: `block_type`, `content`, `attrs` (exact authored order — attrs
are never sorted or deduped, since `CustomBlockDescriptorId::mint`
hashes them in order), `region` (SFC-absolute; a self-closing tag with
no content span anchors at `tag_open.end`, a zero-length region),
`source_order`, `lang`, `src` (matched by exact attribute name, as Vue's
`createBlock` does), and `source_content` — the `ContentId` of the bytes
the facts were read from, hashed once per compile and only when a custom
block exists. The facts travel opaquely on
`RuntimeCompileOutput.custom_block_facts` to the Vue bridge, which mints
`CustomBlockDescriptor`s from them directly — no re-parse, no re-scan of
source text — and refuses facts whose `source_content` differs from the
registered carrier bytes.

`assembly::StagedCompileArtifacts` is the request's ONE handoff from a
framework host-integration backend into session lifecycle/publication code:
a validated `CompileArtifactSet`, the `ArtifactId` of the runtime-module
artifact inside it, the `FragmentDialect` those bytes are written in, and
the runtime source map produced with them. `stage` is the only mint site
and refuses a root the set does not contain (`UnknownRootArtifact`) or one
that produced no content (`UnavailableRootArtifact`); an empty map payload
stages as absent. `vue_main_compile_artifacts` and
`svelte_main_compile_artifacts` return it, and it is what
`RuntimeCompileOutput.main` carries — the module bytes live ON the staged
root artifact, so a published body without its typed set is not
representable and no consumer re-derives the module's language or locates
its artifact by name.

## Multi-unit carrier lowering

Runtime and IDE output blocks carry a `RuntimeOutputDescriptor` naming the generated destination space, emitted content artifact, declared input spaces, raw map, and honest `Exact`/`Approximate` fidelity. A separately lowered template receives `TemplateBindingMetadata` from its script pass (bindings, `has_script`, const props, and ref-bindable imports), matching Vue's official `bindingMetadata` mechanism.

When an output genuinely merges spaces, each source is parsed and lowered independently. The compiler exposes typed generated-template holes/chunks, and `framework_common/generated_chunk.rs` assembles those generated bytes while rebuilding a multi-source V3 map. Do not concatenate authored inputs, synthesize carrier markup, or reparse a fabricated whole carrier. A missing chunk boundary or uncomposable map is a typed unavailable result.

Generated-hole geometry is registered through `CodeTransform` chunk identity and resolved during the authoritative chunk walk. Never locate a generated hole by scanning built output for marker text; authored source may contain identical text.

Current Vue capability gates:

- Style analysis publishes carrier-absolute CSS/v-bind spans only for native carrier content. External or validated-supplied style bytes keep their availability/provenance but publish `css: None` and no located rows until LSP consumers understand the declared source space. Native SCSS/Sass/Less/Stylus does not mint an optional supplied-output request.
- Runtime lowering supports external/validated-supplied templates with transferred carrier script metadata, projected setup script with a carrier template, and the validated supplied-inline template topology. Projected plain script and simultaneous projected plain+setup remain `BlockContentRuntimeUnavailable`.
- IDE lowering supports native carriers and external/validated-supplied templates with composed maps when the carrier has no script, a setup script, or both plain and setup scripts. An external/validated-supplied template with only a carrier plain script remains `BlockContentIdeUnavailable` because its registered template hole is mid-module. Any projected plain or setup script also remains `BlockContentIdeUnavailable`, including the simultaneous case.
- Every lifted IDE class must pass the real TypeScript syntax gate; the gate must prove tsc analyzed the emitted file and may tolerate only missing-module/intrinsic-environment diagnostics TS2307 and TS7026. Every lifted runtime class must pass `node --check`, with both gates carrying invalid-output and non-execution controls. A compiler `Ok` result alone is not proof of capability.

### Vue component resolution projection

`VueProjectionBackend::component_resolution` combines the admitted `ProjectionPlan`, component-use witnesses, `ScriptProjectionFacts` binding inventory and the parse-selected custom-element prefixes. Configured custom elements do not enter component resolution. `ComponentResolutionObservation` distinguishes local/namespace, filename-recursive, global and dynamic uses without answering their types in Rust. Global names use the existing script fallback emitter, including its navigation probe; missing PascalCase members remain `unknown`. Non-Pascal authored tags retain the existing `GlobalComponentKebabType` route, and any Pascal-authored occurrence upgrades the shared fallback to Pascal intent.

`DynamicComponentUseContract` checks a pure `:is="choice.component"` plus `v-bind="choice.props"` pair over each arm of `choice`. A branch containing a constructor union must satisfy every possible constructor; a mismatched or `never` arm is refused. Other dynamic expressions keep the ordinary component-use witness. Resolution specialization includes the plan snapshot and binding route alongside the witness key, so source shifts and binding changes invalidate the observation. The product is dormant until the Vue checking composer adopts it; TypeScript remains the type authority.

### Svelte IDE structural projection

Svelte block projectors consume parser-owned structural spans. In particular, `{#snippet ...}` uses `SvelteBlock.head_span`, whose end is the grammar-balanced outer brace; downstream code must not rediscover the head with a first-`}` scan because destructured/defaulted parameters can contain nested braces. Element-owned snippets lower into a lexical IIFE so same-name snippets in sibling elements do not collide and forward, mutual, and recursive references remain valid. Unchanged snippet names, parameter lists, and bodies move as original `CodeTransform` chunks; only punctuation, annotations, or parameter text that genuinely requires a store/await-default rewrite is synthetic.

Private component-call checks may map only byte-identical authored tokens. Synthetic scaffolding, quoted/escaped property spellings, rewritten spreads, and transformed directive names stay unmapped. Legacy intrinsic `on:event|modifier={handler}` projects to the lowercase Svelte DOM attribute (`onevent={handler}`); modifiers are runtime listener behavior and never survive as TSX attribute syntax.

The Svelte IDE carrier's public facade inlines the syntactic `$props()` annotation directly into `Component<Props, Exports, Bindings>`. Facade scaffolding stays unmapped; a byte-identical annotation is emitted as one mapped insertion per authored line because V3 source-map state does not carry across generated newlines. This lets tsgo definitions on a public prop land on the authored annotation member without assigning provenance to synthetic facade text. The higher-layer Svelte public-API projector prefers the resolved semantic contract when available (captured syntax is the fallback) and records prop-name anchors from typed local declaration origins, so tsserver targets in `.svelte.verter.ts` map through that surface's own source map to the same authored member.

Vue IDE self-instance declarations reference the public API as `InstanceType<typeof import('./Foo.vue.verter')['default']>` (and the JSDoc equivalent). The relative specifier is basename-only and omits the physical `.ts` extension, avoiding `allowImportingTsExtensions` diagnostics while resolving the exact virtual `.verter.ts` surface. Do not use `InstanceType<import(...)['default']>`: `import(...)` there is not a value query and forces the old `@ts-ignore` workaround.

## Binding Metadata Flow

1. `script/process.rs` parses `<script setup>` -> walks AST -> classifies bindings as `BindingType` (SetupConst, SetupRef, Props, etc.)
2. Bindings passed to `template/code_gen/` via `generate_template()` parameter
3. `BindingResolver` determines correct accessor prefix (`_ctx.`, `$setup.`, `__props.`) and suffix (`.value` for refs)
4. Binding patches accumulated in `CodeGenOutput`, batch-applied to `CodeTransform`

## Vue Macro Semantic Boundary (CRITICAL)

The compiler owns Vue macro syntax and code emission, not typed macro
resolution. Parser macro facts are limited to authored spans, runtime
object/array constructors, defaults-object shape, model names/options, and
other syntax needed to preserve the source. Typed `defineProps`,
`defineEmits`, and `defineModel` surfaces arrive from TypeInfo through the
explicit `VueMacroSemanticInput` compile argument, staged once at entry on
the sealed `CompileAttempt` (`stage_vue_macro_semantics` / read via
`vue_macro_semantics()`) — the transaction's staged handoff is the
projection's SOLE carrier; no route threads a bundle on
`VueExecutionInputs`/`VueRuntimeInputs` beside it. Staging is one-shot:
a second `stage_vue_macro_semantics` on the same transaction panics
(misuse-loud); an explicitly staged `Unavailable` is a valid single stage:

- `Unavailable`
- `Runtime(Arc<MacroRuntimeBundle>)`
- `Tsc(Arc<MacroTscBundle>)`
- `RuntimeAndTsc { runtime, tsc }`

Runtime and TSC are independent demands. Bundler script emission consumes only
`MacroRuntimeBundle`; declaration emission consumes only `MacroTscBundle`.
Entries join macro syntax by stable `syntax_index`. Runtime entries contain
the normalized props/emits/model shapes. TSC entries contain terminal splice
text and are emitted directly; the compiler does not parse or reinterpret the
splice. A property-form emits tuple remains one terminal rest-tuple parameter
(`...args: [value: T]`) in both `TscEmitRow.emit_parameters` and
`handler_parameters`; flattening it to `value: T` loses the authored tuple
shape and is forbidden.

Profile-aware public-API projection treats a script/content block override as
an immutable one-file session overlay. The batch fixed view, TypeInfo macro
producer (`SessionResolverContext`), syntax extraction, and whole-hash revision
fence must all observe that exact overlay source. Override extraction is
request-local and must not populate the raw-source `cached_tsc_extract` slot.

Resolved invalid roots cross this boundary only as closed
`MacroInvalidReason` facts. The compiler renders their public diagnostic once,
using the typed reason plus the parser-owned macro role and authored type span;
authored type text is presentation data and must never be used to reclassify
the semantic outcome. Runtime and TSC invalid outcomes share this renderer.

Local declaration carriers preserve TypeInfo refusal detail through
`TscDependencyDeclaration.declaration_failure`: structural inference budgets
remain the closed depth/work variants, while deterministic unsupported and
unresolved declaration shapes remain distinct. The compiler forwards that
typed detail in `TscDeclarationShapeReason`; it never collapses the carrier to
a generic semantic-inference failure or a diagnostic string.

The compiler must never resolve a typed macro parameter, build a companion
type environment, accept a compiler-owned external-type map, or merge
host-resolved types into parser state. `PreparedScript` parses setup and
companion blocks once for syntax reuse only. Typed prop bindings are registered
from the runtime DTO; runtime-form object/array bindings remain parser-owned
syntax facts.

A target that encounters a typed macro without its required bundle, with a
degraded entry, or with a projection for the wrong macro role fails closed at
the authored macro/type anchor using `XMissingMacroSemanticBundle` or
`XUnavailableMacroSemanticResult`. Before runtime codegen, the compiler
structurally validates the whole bundle: syntax/effective macro identities,
roles, `withDefaults` association, public names, authored-member ordinals, and
synthesized model-row anchors must all match parser-owned syntax. Any invalid
row suppresses the entire runtime bundle; a `Complete` row with a degraded
member remains usable, emits `type: null`, and reports the typed reason/detail
at the exact authored key (or model-name/type) span.

Parser model-name facts carry both an OXC-decoded semantic value and the exact
authored literal span. Runtime/TSC joins compare the decoded value, retain the
span only for mappings and diagnostics, and serialize typed emit/model public
names with the canonical JavaScript string escaper.

`withDefaults` syntax remains parser-owned. A statically eligible object
(supported keys, no spread) is folded into each DTO-derived prop row, preserving
the first duplicate and method/default expression syntax. Dynamic, spread, or
unsupported-key defaults preserve the whole authored expression and emit
exactly one `_mergeDefaults`. Runtime prop rendering follows three independent
profiles: development emits `type`, `required: true|false`, `skipCheck`, then a
static default; production retains only Vue-required Boolean/Function types and
defaults; production custom-element mode retains every `type` field, including
`type: null`. `CodegenOptions.custom_element` selects the script policy and is
independent of template tag matching in `custom_elements`. Model props use
Vue's separate model policy (no synthesized `required`; custom-element mode
does not widen production model types).

Guards: `vmrs_boundary_missing_runtime_semantic_bundle_fails_closed`,
`vmrs_runtime_bundle_is_the_only_type_based_props_authority`,
`vmrs_invalid_macro_shapes_render_role_specific_diagnostics_on_both_rails`,
`vmrs_runtime_failures_preserve_typed_reason_detail_and_absolute_anchor`,
`vmrs_tsc_unavailable_diagnostics_preserve_exact_outcome_reason_and_detail`,
`pinned_vue_macro_oracle_carries_provenance_and_discriminating_runtime_facts`.

## IDE Prefixed-Expression Emit Substrate (`ide/template/emit.rs`)

IDE template codegen emits a Vue binding value as JSX through the typed `EmitOp` vocabulary so the user expression keeps an exact source-map mapping while synthetic JSX scaffolding stays unmapped. `EmitText` (`Static`/`Borrowed`/`Owned`) is the text payload; `EmitOp` variants: `InsertUnmapped` (order-preserving unmapped insert, lowers via `prepend_ordered_unmapped`), `InsertMapped` (`InsertedMapped` chunk, mapped at `source_start`+`content_offset`), `PreserveOriginal` (pure no-op — bytes stay an `Original` 1:1 chunk), `OverwriteSyntheticBoundary` (delete + unmapped insert; NEVER a mapped `out.overwrite`), `MoveOriginal`. `emit_op` is the single lowering point. `emit_jsx_binding_value` emits a `JsxBindingValue` (`source_expr`/`prefix`/`suffix`/`occurrences`/`bindings`) `occurrences` times for RELOCATED emission (native `v-model` emits the expression 2-3x); in-place sites (v-html, v-text, `:[key]`, `.foo=`, `v-bind="obj"`, static `:prop`) preserve the bytes and emit `OverwriteSyntheticBoundary` + `collect_binding_patches` around them. A function-typed `:prop` under a v-if scope (e.g. `<div v-if="ok" :onX="() => handle()">`) gets a type-narrowing guard: `compute_function_guard_injection` (props.rs) locates the injection point in SOURCE coordinates from the OXC AST (arrow-EXPRESSION body start → ternary `!((cond))?undefined:`; arrow-BLOCK / `function` body `{`+1 → block `if(!((cond))) return;`), then the value is kept IN PLACE (boundary split + `collect_binding_patches`) and the guard is an UNMAPPED `prepend_alloc` spliced into the middle — emitted BEFORE `collect_binding_patches` so an arrow-expr body identifier at the injection offset stable-sorts as `<guard><accessor-prefix><identifier>`. The guard is never baked into a mapped overwrite. The v-on inline-handler guard (von.rs) is likewise a synthetic PREFIX inside `out.overwrite(prop.start, trimmed_vs, …)` with the handler body preserved in place — it never bakes the resolved value, so it is not migrated.

Bug this replaces: baking `prefix + identifier` into one `out.overwrite(prop.start, prop_end, &format!(...))` produced a `Chunk::Overwritten` mapping the whole run back to the prop start (identifier hover/go-to-definition landed on the prop name). The flat-string IDE producers `resolve_prefixed_expr`/`resolve_prefixed_dynamic_arg` were deleted; wrapped/transformed flat-string consumers (v-on spreads, dynamic event-name keys, v-show) call the shared `build_prefixed_expr` directly. Guard: `crates/verter_compiler/tests/cases/ide_no_baked_prefix_overwrite.rs` — scans `ide/template/**` for both the INLINE bake (`out.overwrite(.., &format!(..<resolver-var>..))`) and the `let`-INDIRECTION (`let v = …format!(..<resolver-var>..)… / build_prefixed_expr(..) / resolve_simple_expr(..); out.overwrite(.., &v)`), EXCLUDING self-anchored overwrites (`out.overwrite(base + node.start, base + node.end, &v)` replaces one node's own span → navigable; partial-interpolation recovery path is the canonical example). The allowlist is EMPTY.

## Template Codegen Backends

Three backends implement the `TemplateCodeGen` trait, called by `walker::walk_template()` in DFS order:

- **VDOM** (`vdom/`): In-place source overwrites producing `_createElementVNode()` calls
- **Vapor** (`vapor/`): Replaces entire template block with direct DOM manipulation code

## Two Template Codegen Paths (CRITICAL)

The Rust compiler has **two separate template codegen paths**. Modifying one does NOT affect the other:

| Path           | Module                    | Purpose                                     | Output                           |
| -------------- | ------------------------- | ------------------------------------------- | -------------------------------- |
| **VDOM/Vapor** | `template/code_gen/vdom/` | Runtime render functions for bundler output | `_createElementVNode(...)` calls |
| **IDE**        | `ide/template/`           | Valid JSX/TSX for LSP/TSGO type checking    | `<div prop={expr}>` JSX elements |

The **LSP uses the IDE path** via `host.ensure_compiled()` with `CompileTarget::IDE`. TSGO type-checks this output. Changes to VDOM codegen do NOT affect LSP hover/completions. IDE codegen auto-detects the script language: TS SFCs produce `.tsx` (TypeScript + JSX); JS SFCs (no `lang` or `lang="js"`) produce `.jsx` (JavaScript + JSDoc annotations).

Guards: the `compile_audit_sourcemap` suite (`crates/verter_session/tests/cases/g_compile/compile_audit_sourcemap.rs`) plus the compile-output snapshots. An IDE-side regression caused by a VDOM change (or the reverse) surfaces as a snapshot diff or a sourcemap byte-offset mismatch.

## Compiled-Output Conformance (CRITICAL)

Official-framework compiler conformance is behavioral plus structural/helper-topology parity, not raw-byte identity. For Vue VDOM/Vapor, Svelte `svelte/internal/*`, SSR/client, and future runtime backends, compare emitted output by observable behavior plus parsed/token-normalized structure: imports, helper families, helper call sequence where order is semantic, memoization/reactivity/effect topology, DOM/hydration template topology, class/style/attribute normalization, prop/property routing, event delegation, and diagnostic/reject ordering.

Cosmetic JS carrier formatting is not a finding: indentation, line breaks, non-semantic comments, intra-expression whitespace outside literals, and behavior-preserving redundant parentheses may differ from the official compiler. Directive, pragma, license/preserve, source-map/sourceURL, TS-directive, JSDoc, and other tool-consumed or framework-significant comments remain in contract. Generated local identifier spellings are waived only when the backend oracle implements scope-aware alpha-equivalence for private, non-observable bindings; otherwise identifiers are structural. Literal payload bytes, static HTML/CSS/SSR strings, public/exported or source-authored names, sourcemap mappings, diagnostic text/codes/order, and any framework-defined observable format remain in contract.

Do not build or route production compiled-output emission through JS printers, re-printers, redundant-paren canonicalizers, or any machinery whose role includes mimicking the official compiler's cosmetic JS carrier formatting. Direct-emission helpers may emit syntax-required tokens, including required parentheses for valid JavaScript expression/statement shape, but they must be scoped to semantic/syntactic correctness and covered by behavioral/structural tests rather than official cosmetic byte parity. Emit correct code directly and make conformance oracles structural for cosmetic categories: a cosmetic-only diff passes; a behavioral or structural divergence fails.

Byte-equality tests remain valid only where bytes are the actual contract, such as generated binding freshness, source-map exactness, or self-characterization during a refactor; they are not official-compiler conformance oracles.

Tracked guard gap: the positive structural-discriminator guard currently covers Svelte client only. Add backend-owned positive structural conformance oracles for Vue VDOM/Vapor and SSR/client outputs before those backends are considered fully guard-covered by this rule; the re-printer guard is cross-backend negative coverage.

### Deliberate documented deviations (Svelte client)

Default is parity with official's observable-correct behavior. A deviation is a DELIBERATE final-state choice to differ from official's correct behavior, recorded with a deviation record, durable code comment, and landed note; silent divergence is never a deviation. The native Svelte client backend currently has no deliberate deviations. This does not mean zero divergences: known structural/helper-topology divergences, behavior-equivalent topology differences, and unconverged SSR/unimplemented surfaces remain tracked in `crates/verter_compiler/src/svelte/runtime/diff_oracle_divergences.rs` or their owning tests and must be converged or kept fail-closed before promotion. `<svelte:head>` attributes fail closed matching official's `svelte_head_illegal_attribute`; that is reject-parity, not a deviation.

Guards: `svelte_structural_conformance_discriminates_cosmetic_from_behavioral_diffs`, `no_compiled_output_cosmetic_reprinter_path`.

### Svelte client text interpolation

Template text expressions are classified from the canonical retained OXC AST. Supported roots are identifier, member/optional-member, call/optional-call, binary, logical, conditional, template, `new`, and primitive literals. Rewriting, call memoization, binding impurity, D-14 constant evaluation, and nullish-coalescing analysis must consume that retained carrier rather than reparsing or scanning source text. Exact static runs use `textContent` (sole element child) or `nodeValue` (reached sibling text node) without an effect; mixed static/live chunks share one text update, and call-bearing values use the official deps-array `$.template_effect` topology. Each/await aliases retain their signal-root rewrite. Unsupported nested constructs preserve their precise typed refusal.

## Svelte Compile-Options Resolver

`resolve_svelte_compile_options(source, parsed, opts) -> Result<ResolvedSvelteCompileOptions, UnsupportedSvelteRuntimeSurface>` (`svelte/runtime/compile_options.rs`) is the SINGLE fold point for Svelte compile options. It runs ONCE per compile request from the single guarded call site at the top of `compile_client` (`svelte/runtime/client_compile.rs`) — every downstream consumer reads the resolved struct, never the raw `SvelteRuntimeOptions`.

**The fold** — compile-option side (`SvelteRuntimeOptions`) ∪ the inline `<svelte:options>` attributes, INLINE WINS per admitted key (matching `svelte@5.56.10` precedence). Inline values are read through the typed AST via the shared parser value authority (`options_namespace_value` / `options_boolean_value`), never a raw rescan. Only the keys the inline syntax admits (`namespace`, `preserveWhitespace`) fold; the resolver runs AFTER the official-reject gate, so it only ever sees official-accepted `<svelte:options>` shapes. The folded `namespace` is used ONLY to fail closed (see below) — the backend emits HTML-namespace roots ONLY, so no namespace value is threaded to codegen.

**Resolved struct** `ResolvedSvelteCompileOptions { fragments: SvelteFragments{Html,Tree}, preserve_whitespace: bool (default false), preserve_comments: bool (default false), disclose_version: bool (default true) }` — HTML-only, four fields. There is NO resolved `namespace` field (svg/mathml fail closed, so the emitted root is always html-namespaced), NO `component_name` field, and NO `css_hash_override` field: the component name is derived during LOWERING (`derive_component_name` in `naming.rs`, reading `opts.name` ?? filename, then `Scope.generate` sanitization + deconfliction against the canonical `ComponentScopeFacts` binder — `component_scope_facts.rs`, `source_declarations ∪ free_references` from one lexical pass over the module/instance scripts plus the template's authored declarations and stored expression references; the single scope authority, replacing the earlier selective `all_declared_names` + reparse approximations) and fed onto `ComponentIr`, and the `cssHash` override rides the carrier channel into the single style-plan scope point (see `/host-session` for the cache-identity seam).

**Namespace fail-close (html-only).** A `namespace: 'svg' | 'mathml'` selection (compile-option OR inline) fails closed at the resolver with a typed `UnsupportedSvelteRuntimeSurface::NamespaceUnsupported { namespace: SvelteNamespace, origin: CompileOptionOrigin{CompileProfile,Inline}, span: Option<Span> }` (stable code `svelte-runtime-unsupported-namespace`) → NO runtime module; an inline `namespace="html"` masks a compile-option `svg`/`mathml` (inline wins). svg/mathml ELEMENT emission (the `$.from_svg` / `$.from_mathml` root-helper family, the `TEMPLATE_USE_SVG` / `TEMPLATE_USE_MATHML` flag bits) is a separate deferred element-emission surface — see the svelte-native-compiler-plan D-62 row. There is NO ns×fragments matrix: every supported root, in every fragments mode, is html-namespaced.

**Per-option codegen consumers** (all read the resolved struct):

- `fragments` → the root template factory. `emit_root_hoist` (`client_module_frame.rs`) picks `$.from_html` (the backtick clone) or `$.from_tree` (the array-literal objectifier) under `fragments: 'tree'`; the root is always html-namespaced.
- `preserve_whitespace` → seeds the root `CleanContext { preserve_ws }` threaded through region synthesis.
- `preserve_comments` → a drop-set gate on retained comments, which serialize as `<!--data-->` (bare `<!>` for empty) in `template_serialize.rs` with the node-path shift applied.
- `disclose_version` → `ImportPlan.disclose_version` (`helpers.rs`), toggling the `import 'svelte/internal/disclose-version'` side-effect import.

**Fail-closed unsupported carrier.** Four officially-accepted options this backend does not support — `compatibility.componentApi` (any explicit value other than `5`), `hmr`, `accessors`, `immutable` — are demoted out of the essential surface. Any EXPLICIT presence (including a `false` / default-equivalent value, from EITHER the compile-option origin OR an inline `<svelte:options>` origin, even a value later masked by inline) fails closed with a typed `UnsupportedSvelteRuntimeSurface::CompileOptionUnsupported { option: UnsupportedSvelteCompileOption, origin: CompileOptionOrigin{CompileProfile,Inline}, span: Option<Span> }` (distinct stable codes `svelte-runtime-unsupported-{compatibility-component-api,hmr,accessors,immutable}`) → NO runtime module. This is a FEATURE refusal, NOT an official compile-error. The deprecated inline `tag` key stays the parser-first `svelte_options_deprecated_tag` HARD error, with a defensive unreachable resolver arm. `runes` is NOT folded here — it flows through the existing mode-inference plumbing (`forced_runes_option` + `opts.runes`); `css` / `customElement` / `dev` / `generate` / `experimental.async` stay delegated to their owners.

## Svelte Conformance Trace (`conformance-trace` feature)

`verter_compiler`'s `conformance-trace` Cargo feature (default OFF) enables the typed conformance-observability side channel `verter_compiler::svelte::runtime::conformance_trace` — CONFORMANCE-TOOLING-ONLY, consumed by the `verter_svelte_conformance` crate (which dev-deps `verter_compiler` with the feature on). It is not a production API surface: the default build compiles the module, its producer hooks, and every trace collection site out entirely, and production IR structs carry no trace state under either setting.

**API surface** (feature-gated): `compile_client_with_conformance_trace(...)` runs the production `compile_client` pipeline under a capture and returns the compile outcome together with the trace (a refused/rejected fixture still returns what was observed up to the failure); `capture(f)` installs a thread-local trace around a closure (captures nest, unwind-safe restore); `ConformanceTrace { static_attrs, style_matches }` carries static-attribute lexical provenance (quoting + HTML entity source representation, folded from the attribute-lowering producer boundary's single decode pass — never a second source scan) plus per-`<style>` matcher facts (per-selector tri-state certainty rows, used/scoped selector spans, scoped element identities); `MatchCertainty` is re-exported.

**`MatchCertainty` tri-state** (`svelte/runtime/css/match.rs`, always-on — NOT feature-gated): `No < Maybe < Yes`, `and` = min, `or` = max. Production projects through `might_match()`: `Yes | Maybe ⇒ true`, `No ⇒ false` — byte-identical to the pre-tri-state boolean matcher (`Maybe` was `true`; it is never treated as `No`). The per-selector certainty rows on the match sink exist only under `cfg(any(test, feature = "conformance-trace"))`.

**Zero cost when off**: by `#[cfg]` gating plus a monomorphized no-op entity-decode observer that compiles away in the default path. Guarded by `crates/verter_compiler/tests/svelte_conformance_trace_zero_cost_guard.rs` (prod-IR trace-mention ban, feature-gated module declaration, closed `AttrIr`/match-sink field inventories, decoder-mention ban, manifest keeps the default build feature-off with no dev-dependency re-enable channel) and an isolated feature-off CI gate (`cargo build`/`cargo test -p verter_compiler --lib` with no conformance crate in the dependency graph, so workspace feature unification cannot mask the default build).

## Generated IDE Imports And Authored Bindings

Every authored import is hoisted to module scope beside the generated helper
preamble, so a generated import must never rebind a name an authored import
already binds. `unbound_builtin_components` drops a Vue built-in
(`Teleport`, `Suspense`, `KeepAlive`, …) from the generated `vue` import when
either script block already imports that local name; an authored ALIAS
(`Suspense as Pending`) leaves the built-in's own name unbound, so it is still
imported.

A setup binding whose WHOLE initializer is a proven call to Vue's runtime
`defineAsyncComponent` (a non-type-only named `vue` import under any local
alias, or that member read off a runtime `vue` namespace import — see
`proven_vue_async_component_bindings`) is read into the template through
`asyncComponent(name)` instead of `name as unknown as typeof name`. Vue types
the call as whatever the loader resolves to, so a loader resolving to a raw
options object has no construct/call signature and direct JSX rejects a valid
tag. `AsyncComponent<T>` returns constructors and functional components
EXACTLY (generics included) and turns a raw options object into a constructor
over the contract its own `props`/`emits` declare. The authored declaration,
the destructured template local and the JSX tag identifier are unchanged. The
helper import is emitted only by files that use it, so no other file's
preamble moves. A same-name local function, a non-Vue import and a type-only
import prove nothing and keep the ordinary entry. The declaration lives in all
four shipped surfaces (`packages/types/src/components/components.ts`,
`packages/types/index.d.ts`, `crates/verter_lsp/src/verter_types_stub.d.ts`,
`packages/typescript-plugin/src/helpers/verterTypesStub.ts`).

## Strict Slot Children Type Checking (Experimental)

Scoped slot inference captures a component instance in the parent's lexical scope
using `new (componentConstructor(Component))(props)`. Preserve generic constructor
identity until the authored props are applied; extracting a generic return type
first erases its type parameters. `ide/template/slot_inference.rs` emits the
unmapped inference inputs using the same bindings as JSX props. Capture before
slot parameters and named-slot loops can shadow those inputs, including paired
components with an empty slot body. Incomplete/self-closing recovery must not
reference an instance whose capture wrapper was never emitted.

When `strict_slots: true` (VS Code: `verter.experimental.strictSlots`), the IDE template codegen emits `strictRenderSlot` calls after the JSX tree, enforcing that slot children match the parent component's `defineSlots()` type signature ([RFC #733](https://github.com/vuejs/rfcs/discussions/733)).

**Generated pattern** (inside the block scope, after JSX):

```tsx
___VERTER___strictRenderSlot({} as NonNullable<ReturnType<typeof ___VERTER___Comp{offset}>['$slots']['{slot}']>, [TabItem, {} as HTMLElementTagNameMap["input"], "" as string]);
```

**Child type references**: Component -> constructor name, HTML element -> `HTMLElementTagNameMap["tag"]`, text/interpolation -> `"" as string`. Each child is a sourcemapped `InsertedMapped` chunk pointing to its template position.

**Skipped cases**: self-closing components (no children), `is_jsx` mode, `<component :is>` (deferred), whitespace-only text, comments.

**Key files**: `ide/template/mod.rs` (`StrictSlotEntry`, `collect_strict_slot_children`, `emit_strict_slot_checks`), `ide/script.rs` (ambient `strictRenderSlot` type declarations).

## Cached Directive Fields on ElementNode

Parser extracts structural directives from `el.props` via `prop.take()` and caches them as dedicated fields on `ElementNode` (`ast/types.rs`):

| Field         | Directive                     | In `el.props`? | Notes                                            |
| ------------- | ----------------------------- | -------------- | ------------------------------------------------ |
| `v_condition` | `v-if`, `v-else-if`, `v-else` | **No** (taken) | Contains `ElementNodeCondition` with kind + prop |
| `v_for`       | `v-for`                       | **No** (taken) | Contains the full `NodeProp`                     |
| `v_slot`      | `v-slot`, `#name`             | **No** (taken) | Contains the full `NodeProp`                     |
| `v_once`      | `v-once`                      | **No** (taken) | Contains the full `NodeProp`                     |
| `v_ref`       | `ref`, `:ref`                 | **No** (taken) | Contains the full `NodeProp`                     |

**Consequence**: Code iterating `el.props` will **never see** these directives. Both codegen paths must handle them explicitly. The IDE module removes `v-if/v-for/v-slot/v-once` attributes (they become JSX wrappers/removals) and converts `ref` to JSX expression syntax (`ref={"name"}`).

## GlobalComponents Fallback Consts + Kebab Tag Rewrite (IDE surface)

Globally-registered components (registered only through a `GlobalComponents` augmentation, never imported) type in the template via per-tag **fallback consts** emitted into every script arm (`ide/script/wrapper.rs`):

- Collection (`collect_global_component_fallbacks`) walks the template once per arm: every non-builtin, non-member-expression, non-`<component>` component tag that is NOT already bound and NOT a configured custom element yields one `GlobalComponentFallback { pascal, authored_non_pascal }`, deduplicated by Pascal name in first-seen order. A `<component is="Name">` STATIC target contributes too. The same list feeds the emitted consts AND the `TemplateComponentBindings` inventory (tag rewrite, `@event` spread payloads, simple-handler param inference) so one component types identically everywhere.
- Emission is **authoring-form-sensitive** (the custom-element/fail-open contract):
  - Pascal-authored anywhere → fail-closed `const Pascal = {} as ___VERTER___GlobalComponentType<'Pascal'>;` — an unregistered name types `unknown` and produces a real TS2604 at the tag (never silent `any`).
  - Kebab/lowercase-authored ONLY → fail-open `const Pascal = {} as ___VERTER___GlobalComponentKebabType<'Pascal', 'authored-tag'>;` — a registered member (Pascal key, then the authored key) resolves the component type; an UNREGISTERED tag degrades to a function component over `JSX.IntrinsicElements['authored-tag']` (a user's web-component `IntrinsicElements` augmentation keeps typing it; Vue's `[name: string]: any` index otherwise yields `any`) — never a false TS2604 on a web-component tag.
  - Each TS const carries a go-to-definition **NAV PROBE** (`void ___VERTER___globalComponentsNav().Pascal;`); `global_component_nav_probe_offset` byte-verifies BOTH emission shapes and fails closed on any mismatch.
- **Configured custom elements** (`CompileOptions::custom_elements` prefix match, threaded as `IdeScriptOptions::custom_elements` / `IdeTemplateOptions::custom_elements`, shared predicate `ide::matches_custom_element`) are native by contract: excluded from collection AND from the kebab rewrite — the tag stays authored even when a same-name local binding exists.
- **Kebab tag rewrite** (`ide/template/mod.rs`): a dashed component tag with an inventory/local resolution rewrites to its Pascal identifier via `emit_mapped_kebab_pascal_rewrite` — PER-SEGMENT mapped edits (delete each `-`, overwrite only case-changing segment heads; every other byte stays an `Original` chunk). A whole-name overwrite mapped only up to the generated (Pascal) length, leaving the authored tag TAIL unmapped (dead hover/definition/rename); per-segment keeps every LETTER column mapped, including the last — only the removed `-` separators stay unmapped. Composition mismatch falls back to the whole-span mapped overwrite.

The conditional types live in `@verter/types` — five synchronized copies: `packages/types/index.d.ts`, `packages/types/src/components/components.ts`, `packages/typescript-plugin/src/helpers/verterTypesStub.ts`, `crates/verter_lsp/src/verter_types_stub.d.ts`, and both constants in `crates/verter_compiler/src/ide/script/type_constructs.rs` (`VERTER_TYPES_AMBIENT_MODULE` + `VERTER_TYPES_STANDALONE_DTS`). The shipped empty `declare module "vue" { interface GlobalComponents {} }` augmentation guarantees the surface exists on every Vue version (introduce-on-absence + user-augmentation merge proven by `verterTypesStub.spec.ts`'s ≤3.4 leg and its discrimination control).

**Declaration-surface parity.** Hand-maintained copies drift: the `runCustomDirective` fix that carried the directive's `Arg` type parameter (`Directive<HostElement, Value, Modifiers, Arg>`) into `arg` landed in the two `type_constructs.rs` constants and stayed missing from the three copies a real editor actually serves. `verterTypesStub.spec.ts` → `declaration-surface parity` now compiles ONE contract against every copy a TypeScript test can read as a whole artifact (`VERTER_TYPES_STUB`, `crates/verter_lsp/src/verter_types_stub.d.ts`, `packages/types/index.d.ts`) plus a deliberately reverted pre-fix copy, all in a single program: the TypeScript compiler is the oracle, so formatting and `Directive` vs `import("vue").Directive` spelling differences are invisible and only BEHAVIOURAL divergence fails. The published `@verter/types` source is covered by its own type test (`packages/types/src/directives/directives.spec.ts`); the two `type_constructs.rs` constants keep `verter_types_surface_carries_the_directive_arg_type_parameter`. Generating all copies from one source of truth would delete the drift class outright and remains the durable fix.

Design + deferred-debt rows: the LSO5 charter in the TAMA controller DAG.

## IDE Script Error Recovery

OXC parses the original `<script setup>` content exactly ONCE (`ide/script/setup.rs`). There is a single recovery surface — no truncate-and-reparse, no clean-prefix reparse authority, no file-scope error mode.

- **Clean parse** → the full codegen path runs unchanged (import/type-decl hoisting, binding extraction, macro lowering, the `___VERTER___TemplateBindingFN` wrapper).
- **Genuine syntax error** (both the TSX parse AND a TS-mode parse fail — a TSX-only failure is an angle-bracket assertion already handled by `rewrite_ts_type_assertions`) → a single token scan of the REAL source produces a `ScriptSetupRecoveryPlan` (`ide/script_recover.rs`, `ScriptTokenScanner::recover_plan`). The plan carries **top-level (bracket depth 0)** original-span `imports` / `macros` / `functions` / `variables` (reused for hoisting + binding registration) plus OUTPUT-ONLY recovery chunks (detected over the WHOLE source at any depth):
  - **member holes** — a dangling `a.` / `a?.` gets a universal member placeholder (`valueOf`) right after the operator so the dot cannot absorb the following token;
  - **expression holes** — a trailing operator / assignment RHS / conditional arm / arrow body gets an operand placeholder (`(undefined)`);
  - **scope closers** + a statement terminator at the recovery boundary (the `</script>` overwrite) — close the brackets the user left open so the generated scaffolding starts cleanly. A delimiter that requires a non-empty body but was left empty (a grouping/arrow-body paren `const x = (`, a computed-member bracket `foo[`) gets a placeholder operand BEFORE its closer (`undefined)`, `undefined]`); call args `foo()`, array literals `[]`, and blocks/objects `{}` are valid empty and get a bare closer.

**Top-level fact gate.** Recovered facts are gated to bracket depth 0, mirroring the clean top-level parser's `block_depth == 0` rule. A block-local declaration (`function f(){ const inner = 1; }`) is NEVER recovered as a setup binding/import; only the whole-source holes/closers fire inside nested scopes.

**Recovered macro = clean-lowering parity.** A recovered `defineProps`/`withDefaults` binding is registered `Props` AND emits the same `const __props = <binding>;` alias as clean macro lowering, so a template `props.x` (lowered to `__props.x`) resolves instead of dangling against a `__props` that was never declared.

The user's body STAYS inside the `___VERTER___TemplateBindingFN` wrapper in both cases; the broken-tail member access (`count.`) keeps hover/completion/go-to-definition working for declarations above the cursor.

**No synthesize-then-reparse.** Synthetic recovery chunks are output-only and unmapped; they are NEVER bindings, macros, imports, or any other source fact. Recovery metadata comes only from the original clean OXC AST or from original-span token recovery over the real source — a reparsed synthetic view is never an authority.

Guard: `crates/verter_compiler/tests/cases/ide_script_recovery_guard.rs` (scans `ide/script/setup.rs` for the deleted dual-recovery identifiers and the synthesize-then-reparse anti-pattern), plus `crates/verter_compiler/tests/cases/repro_member_access_ide_codegen.rs` (recovery shapes + clean-path preservation) and the negative-metadata tests in `script_recover.rs`.

## Style Rewrite Stages

Framework style rewrites use `verter_css_syntax::StyleSyntaxIr` and are deliberately split. Public bump-backed CSS syntax nodes (`StyleBlock`, `StyleRule`, `SelectorCompound`, …) are borrowed from that IR and are not independently `Clone`/`Copy`; clone the IR (`Arc`) when an owned handle is needed.

1. `transform_vue_v_bind(AuthoredStyleInput)` is the production v-bind-only entry: it runs on authored CSS, SCSS, indented Sass, Less, or Stylus and returns the same dialect. It never preprocesses or evaluates.
2. CSS Modules hashing and the original-to-hashed mapping from typed selector spans are a cascade stage over the shared IR, not a public parse/materialize entry.
3. Scoped-selector rewriting runs only on plain CSS after preprocessing. `PlainCssInput::try_new` typed-refuses every non-CSS dialect before that cascade stage is admitted. There is no shipped isolated parse/materialize entry for this stage.
4. Svelte owns a separate plain-CSS consumer for `:global`, selector matching/pruning, scope insertion, and keyframe rewriting. Authored preprocessor dialects are typed-refused unless the host supplied a completed external preprocessing result for the block: the host hands it over once as a `SvelteSuppliedStyle` (produced bytes, producer, the host-minted source-space/artifact identity of those bytes, the host's parse of them, and the content identity of the authored bytes it validated — all read off the projection the block-content capture fence stamped, never re-derived from the live carrier), `bind_svelte_style_continuations` (`svelte/carrier.rs`) binds it to its block through `ExternalStyleContinuation::admit`, and the admitted continuation's produced bytes are then the block's only body for every stage (official-reject css probe, analysis, matcher, render) via `AdmittedStyleIrs`. A result that cannot be bound (stale basis, missing basis, unknown authored dialect, no block at its slot, boundary refusal) refuses the compile with `svelte-runtime-style-continuation-refused`; it never falls back to the authored block. Svelte's external css artifact publishes as a stage-qualified `QualifiedRuntimeStyle` (`RuntimeCompileOutput.qualified_styles` / `DirectCompileOutput.qualified_styles`), declared over the byte space the render consumed: the carrier source for an authored block, and for a continued block the HOST-minted space/artifact pair carried on `BoundStyleContinuation` — never a carrier space minted over the produced bytes, which would name an identity the host's own produced-to-authored map cannot be joined to. That host map (`SvelteSuppliedStyle.source_map` → `BoundStyleContinuation.source_map`) is transported with the continuation, and a continued block's css map is published chained through it (`chain_generated_map_json`, as for a supplied Vue block) — or absent when the host holds no map or the chain cannot be built — never the bare render map, whose produced-space positions would be labelled with the carrier file. The diagnostics the producing tool reported (`SvelteSuppliedStyle.diagnostics`, projected from the admitted override at the severity the tool stated, addressing the authored stage with no fabricated position) ride the continuation's result and then the published style's result in production order; they are never replaced by an empty list at the boundary.

Only complete trusted nodes publish edits. Rewrite uncertainty fails closed; style-liveness uncertainty fails open. Every emitted edit and its source map comes from the same `CodeTransform`. Supplied or external inputs retain their host-minted source-space/artifact identity in `RuntimeOutputDescriptor`; carrier-absolute positions are never fabricated.

The sealed `applyBlockOverrides()` handoff remains the input channel for caller-preprocessed content. External/supplied semantic analysis continues to publish `css: None` until its source-space-aware analysis consumer lands; compiler rewrite code must not route around that gate.

### Stage-Qualified Style Identity

`verter_css_syntax::stage` owns the vocabulary every style consumer shares: `StyleStage` (`Authored` / `Preprocessed` / `FrameworkRewritten`), `StyleProducer` (`Verter` / `External(ExternalStyleProducer)` / `ExternalAnonymous`), `StyleDiagnostic` (a message plus a span qualified by the stage whose space it addresses), `StyleDependency` (the parse-minted inclusion inventory), and `QualifiedStyleResult` (bytes + stage + dialect + producer + diagnostics).

A diagnostic's `stage` always names the space its span is IN, never the authority that reported it. There is deliberately no authority or severity axis on `StyleDiagnostic`: every style diagnostic a live route produces is a rewrite refusing, and a refusal is an error. A closed taxonomy whose other members name routes that do not exist claims a generality the pipeline does not have — the route that needs one adds it together with its producer.

- `VueStyleCascadeOutcome.result` is that carrier, and `outcome.code()` is the only way to the bytes. A run that changed nothing stays at its input stage; a run that produced output is `FrameworkRewritten` and keeps its input dialect (only preprocessing fixes the dialect to CSS).
- **A refusal's stage is the cascade input space, and there is no per-stage space to record.** Shared planning hands every compatible stage the same input IR and merges their plans into one terminal `CodeTransform`, so no stage ever inspects bytes another stage rewrote. `finish_vue_style_cascade` therefore projects every diagnostic at `input.stage()` — a single answer, not a per-stage table. Later Vue rewrite refusals are `Authored` (or admitted `Preprocessed`) and token-precise; they are not `FrameworkRewritten` and do not use a rewritten-space span. A refusing plain-CSS-only stage still clears the output after reporting.
- **A cleared output is a refusal, not a rewrite that emitted nothing.** The runner's output state is the three-way `CascadeOutput` (`Passthrough` / `Rewritten` / `ClearedByRefusal`), and a wiped output mints `QualifiedStyleResult::refused(...)` — `is_refused()` true, no bytes, and no claim that a rewrite produced them. Deriving "was this rewritten?" from "are there owned bytes?" labelled those zero bytes `FrameworkRewritten` + `StyleProducer::Verter`. `cascade_output_is_publishable` reads `result.is_refused()`, not `code().is_empty()` and not the failing stage's identity. An authored-`v-bind()` rewrite failure that left the input intact is not a refusal. **A parse miss is recorded once, by the parse that ran, and clears only a request whose meaning depended on a rewrite it could not plan**: with `module` or `scoped` set, unrewritten bytes would ship unhashed class names or component styles applied document-wide, so the output is cleared; with neither, the only work was `v-bind()` lowering, nothing was rewritten, and the authored bytes publish beside the diagnostic. That second case is byte-for-byte the answer `run_vue_style_authored_only` gives, and it is the same answer by construction rather than by agreement: `RuntimeStyleProcessing::AuthoredOnly` and a `Complete` request with neither attribute are the same request, so `run_vue_style_authored_only` IS `run_vue_style_cascade(input, scope_id, false, false, …)` and holds no route of its own. It used to, and the two diverged on exactly one recorded fact — whether a parse that completed but whose `v-bind()` planning refused had surveyed the block's inclusions — which no signature could have caught. Every dialect now takes one route: the plain-CSS gate (`CssStageRequest::gated`, whose sole predicate is `PlainCssInput::try_new`) drops the CSS-only stages for a non-CSS `<style module>`/`<style scoped>` and records the refusal that dropped them, so "which stages ran" and "was a refusal recorded" are one value and cannot disagree. `run_vue_style_cascade_verified` passes `CssStageRequest::admitted`, because `VerifiedPlainCss` already carries the proof the gate exists to establish.
- **Style diagnostics have ONE publication route: `outcome.result.diagnostics()`.** `facts.refusals` and `stage_failures` are each authority's own record of what it reported; the carrier is where those records are projected into the shared vocabulary exactly once. `StyleRewriteFailure::to_diagnostic` is crate-private, which removes the correctly-spaced projection (and its `space` argument) from the outside vocabulary so no consumer accidentally re-derives one and reports the same refusal twice. Read what that buys exactly: it is a convention backed by where the correct answer lives, NOT a structural bar — `StyleDiagnostic::new` and `Display` are public and `facts.refusals`/`stage_failures` stay `pub`, so a consumer that sets out to mint its own shape can. `verter_napi`'s `transform_vue_style` therefore reads `result.diagnostics()` rather than re-formatting `facts.refusals`.
- Consumers pick their map FROM that stage. `compile::push_style_diagnostic` shifts a span by the block start only for an `Authored` span; a later-space span (`Preprocessed`) anchors on the block, because no later-space → SFC map exists at that boundary. Shared Vue rewrite refusals stay `Authored`, so they take the token-precise arm. The VS Code client's `preprocessorDiagnostics` applies the same rule.
- **The cascade never infers its input's provenance.** `run_vue_style_cascade_verified` / `transform_vue_style` take a `CascadeInput` (`Authored`, or `Preprocessed(PreprocessorIdentity)`): preprocessed plain CSS and authored plain CSS are the same bytes, so only the caller knows. `standalone::apply_selected_runtime_styles` reads it from `RuntimeBlockContentInput.producer`, which the host fills from the supplied artifact's processor identity — never from a dialect comparison, which missed every tool that ran over an already-plain-CSS block. The authored-`v-bind()` stage still runs on preprocessed bytes: a preprocessor leaves `v-bind()` in its output for exactly that stage.
- `prepare_supplied_style(PreprocessedStyle<'_>)` is the sole admission point for caller-preprocessed bytes. The witness borrows the bytes and carries `PreprocessorIdentity`, which can name only an external tool or explicit anonymity. It is minted by `QualifiedStyleResult::as_preprocessed` or `PreprocessedStyle::admitted`. `as_preprocessed` gates on stage `Preprocessed`, an external producer, and not-a-refusal. What the signature enforces is that bytes cannot arrive without a stated space and external producer; `&str` is rejected by `prepare_supplied_style_rejects_unqualified_bytes.rs`. It cannot prove the bytes left SCSS behind because plain CSS is a subset of every supported dialect. That assertion belongs to the admitting boundary; richer tool provenance stays on `SuppliedContentArtifact`.
- `CssDialect::from_lang` is the single authority for exact `lang="…"` spelling → `CssDialect` identity, and `CssDialect::requires_external_preprocessing()` answers "is this already CSS". `LANG_SPELLINGS` is byte-exact for `scss`/`sass`/`less`/`stylus`/`styl`; `lang="SCSS"` therefore does not become SCSS through case folding. This lookup does not decide framework fallback: Vue's reference compiler passes a missing processor-table entry through as plain CSS. `css` is matched case-insensitively because it already names that fallback grammar. Every route reaches the identity owner with authored bytes; other block roles retain their owner-specific normalized spelling. `carrier_style_dialect` is the matching carrier-level owner shared by Vue and Svelte. The carrier parser's universe is deliberately wider because it also names `postcss`, which the editor serves as CSS-shaped content while the rewrite pipeline has no native dialect for it.
- `StyleSyntaxIr::dependencies()` replaces the former `imports_unresolved` boolean, and `StyleSyntaxIr::dependency_pulls_in_unparsed_bytes` is the same owner's answer for ONE inclusion. `StyleDependencyKind` is closed over every inclusion keyword the five dialects spell — `@import`/`@use`/`@forward`/`@plugin`/`@require` — plus Stylus's bare `require 'x'` / `import 'x'` statement form, which carries no at-keyword and is recognised at the IR sink instead. A missing member is a wrong-complete defect, not a cosmetic gap: an unrecognised inclusion keyword records nothing, so the block publishes an exhaustive `v-bind()` surface while its bindings sit in the included sheet. The list lives on the IR alone — `QualifiedStyleResult` carries no copy, because a `StyleDependency`'s spans address the minting parse's space, reading a specifier needs that same IR, and an empty list there could not distinguish "no inclusions" from "nothing ever parsed". Sass's NON-EMITTING built-in modules bring in nothing (function libraries: no rules, no classes, no `v-bind()`), and that exemption is the exact closed set `SASS_NON_EMITTING_BUILTIN_MODULES`, not the `sass:` prefix — `sass:meta` emits another module's CSS through `load-css()`, so it answers like any other sheet. Everything else does too, including an inclusion whose target the parse could not address exactly.
- **Whole-surface completeness is `StyleSyntaxIr::pulls_in_unparsed_bytes()`, NOT a fold over `dependencies()`.** A recover-mode parse returns a usable IR for a sheet it could not read end to end, and its inclusion list is then a LOWER BOUND: an `@import` inside an unterminated block never reaches the at-rule frame and never enters the list. Folding over what the parse DID record therefore answers "nothing foreign here" exactly where it saw least — the wrong-complete direction, publishing an exhaustive `v-bind()` liveness surface for a block whose bindings live in a sheet nothing parsed. The whole-sheet answer adds the parse's own recovery record (`RecoveryKind::discarded_input`, an exhaustive match so a new recovery strategy must state its side) to the per-inclusion answers. It keys on recovery, not on diagnostic kinds or on "are there diagnostics": an ambiguity reported without dropping anything still parsed and still inventoried the surrounding structure. `extract_style_v_bind_usage_*` marks its result incomplete on that one fact and callers fail open. Every entry that parses the input records the completeness answer through the one `record_input_dependencies` recorder, so it does not depend on which entry the caller took. The recorded state is tri-state (`Option<bool>`, private) and read through `VueStyleFacts::pulls_in_unparsed_bytes()`, which **fails closed on the unrecorded state**: `false` is the STRONG claim ("nothing outside these bytes contributes to this block's surface") and no parse has earned it until one has run. The reachable unsurveyed state is a cascade whose parse of the input never completed, with neither modules nor scoping requested to clear the output; a derived `bool` default answers "exhaustive" there, which is the wrong-complete direction. The answer is recorded from the parse BEFORE any stage plans an edit, so a stage that then refuses to plan (an untrusted `v-bind()` target, an indented-layout mutation) does not un-survey a parse that completed, and a recovered parse still fails closed through the owner's own `discarded_input` check rather than through an absent recording. Pinned by `a_cascade_that_surveyed_nothing_never_claims_an_exhaustive_surface` (both entry points, with its surveyed control) and `a_refusing_stage_does_not_un_survey_a_completed_parse` (both entry points, completed and recovered parses).

Later Vue rewrite refusals remain in the cascade input space, so `compile::push_style_diagnostic` applies authored-block arithmetic and consumers receive token-precise ranges. The former whole-block `FrameworkRewritten` bound does not apply to this planner.

Tests: `crates/verter_compiler/tests/cases/style_stage_identity.rs`, `crates/verter_compiler/src/direct_result_tests/style_planner.rs`, `crates/verter_css_syntax/tests/cases/dialects.rs`, `crates/verter_session/tests/cases/style_native_analysis_preprocessor_boundary.rs`.

## Style Preprocessing in Bundler Mode

Style blocks with `lang="scss"`, `lang="sass"`, `lang="less"`, or `lang="stylus"` require caller-owned preprocessing before the plain-CSS module/scoping stages.

**Vite mode:** the unplugin caches raw authored style content and preserves its authored `lang` in the style request. Its Vue `RuntimeRender` Main request selects the typed `RuntimeStyleProcessing::AuthoredOnly` plan so authored `v-bind()` facts still drive runtime codegen without claiming modules/scoping before CSS exists. Vite's CSS pipeline performs preprocessing; Vue-specific post-processing then consumes the resulting plain CSS. Host-backed, non-Vite, and direct compiles retain the default `Complete` plan and therefore remain fail-closed for scoped non-CSS without supplied preprocessing.

**Non-Vite mode:** `preprocessBlock()` / `preprocessStyle()` use Vite's `preprocessCSS()` when configured. The result returns through the sealed `applyBlockOverrides()` channel with validated artifact, revision, source-space, and content hashes before the compiler runs the plain-CSS stages.

`vue/compiler-sfc` is resolved once per plugin instance from the project root and is shared by SFC parsing and bundler-side style post-processing. The relevant owners are `packages/unplugin/src/index.ts`, `packages/unplugin/src/core/preprocessor.ts`, `crates/verter_session/src/block_content.rs`, and `crates/verter_session/src/host_resolve/virtual_file_pipeline.rs`.

## Vue Style Planner

Compatible Vue rewrite stages share one `StyleSyntaxIr` and one terminal `CodeTransform`. Later-stage refusals stay in the cascade input space (`Authored` or admitted `Preprocessed`) and keep the refused token's span.

```
authored or admitted-preprocessed bytes
    | one StyleSyntaxIr
    | v-bind + CSS Modules + scoped plans (authored coordinates)
    | one terminal CodeTransform
rewritten CSS, passthrough, or ClearedByRefusal
```

A non-CSS `<style module>` / `<style scoped>` request refuses at `PlainCssInput` without parsing the bytes as CSS.

Isolated CSS-Modules / scoped parse-and-materialize entries are forbidden on the shipped crate: they would be a second CSS parse path. The crate-test comparison instruments (`transform_vue_css_modules`, `transform_vue_scoped_css`) exist only under `#[cfg(test)]`. They must never be compiled through the `test-support` Cargo feature: that feature is an ordinary library feature and can compile in a non-test build. Compiling either instrument into any non-test library build reintroduces the second CSS parse and the staged coordinate spaces the shared plan removed. Per-stage allocation and source-map benches measure the shared cascade with the unused stage flags off. `transform_vue_v_bind` remains a real production entry for the v-bind-only surface. Giving either isolated instrument a production caller, or compiling it outside `#[cfg(test)]`, is forbidden; route new work through `run_vue_style_cascade`.

Each stage's plan is merged into the previous one in authored coordinates (`merge_shared_stage_edits`): a later edit strictly inside an earlier overwrite targets bytes the earlier stage already replaced and is discarded; a later deletion may subsume earlier work inside the bytes it removes; every other intersection refuses the whole plan rather than materialize a half-ordered rewrite.

**Order and pairwise disjointness are one type invariant, `PlanDisjointEdits`, not a precondition on the merge.** The merge's `Insert` arm inspects only the nearest earlier edit by start offset, and that single candidate is the unique possible container exactly when the stream it scans is disjoint. Carrying that in the type is what stops a future caller from handing the arm an overlapping stream it would silently mis-read rather than refuse. The invariant is established once, over the first stage's own edits (`from_planned` sorts then checks), and re-established over each merge's composition (`from_ordered` — `later` carries no invariant of its own, and two disjoint streams can compose into an overlapping one). There is no terminal disjointness gate on the finished plan: holding the witness IS the proof. `build_transform_output` keeps its own independent sort-and-check as the backstop for every route, including a test-only staged comparison that builds no shared plan.

That merge is equivalent to running the stages one after the other only while no stage's REPLACEMENT bytes contain something a later stage would have acted on — nothing structurally enforces it, so `a_shared_plan_matches_running_the_stages_one_after_the_other` compares the two models across the construct corpus. The merge itself catches only the SAME-SPAN case, where two stages emit coincident non-empty overwrites and it refuses. The likelier hazard is DIFFERENT-SPAN and silent: if a stage ever renamed an `@keyframes` name, the matching `animation-name:` rewrite a later stage owes lives in another span entirely, the merge sees no intersection and refuses nothing, and the shared model emits an animation reference pointing at the old name where the staged model was correct — wrong-complete output, not a refusal.

**This one is a test-and-review mitigation, not a guard, and it is the planner's live residual risk.** The mitigation is the corpus sweep (both models over every fixture, with its own non-vacuity legs so a mutual refusal cannot pass for agreement) plus the `shared_families == 11` size pin. The pin detects a change in the shared generator family's COUNT, not new planner behavior omitted from that corpus: a stage that starts emitting a new replacement construct while the family list stays at 11 will not trip it. Any change to a stage's replacement vocabulary MUST extend `a_shared_plan_matches_running_the_stages_one_after_the_other` in the same change. Read the corpus honestly: the shared generators produce TOP-LEVEL rules only, and they cannot be extended cheaply because that same category list is the allocation ceiling's universe of recaptured legacy counts. The nested and overlap-prone shapes — a class rule inside an `@media` body, `@keyframes` renaming beside a class hash, a later rewrite landing inside a `v-bind()` replacement — are therefore carried as that same test's own fixtures.

## CompileTarget (Selective Pipeline)

`CompileTarget` (bitflags in `verter_compiler::compile::types`) controls which compilation steps run:

| Flag            | Controls                                             | Used By           |
| --------------- | ---------------------------------------------------- | ----------------- |
| `STYLE`         | Style codegen (CSS scoping, modules, v-bind)         | Bundler           |
| `SCRIPT`        | Script codegen (macro expansion, binding extraction) | Bundler, Analysis |
| `TEMPLATE`      | Template VDOM/Vapor render function codegen          | Bundler           |
| `TSX`           | TSX template codegen for type checking               | LSP/IDE           |
| `TSC`           | TSC declaration file generation                      | TSC               |
| `TEMPLATE_DATA` | Template data extraction (binding occurrences)       | LSP, Analysis     |

**Presets:**

| Preset     | Flags                         | Consumer                    |
| ---------- | ----------------------------- | --------------------------- |
| `BUNDLER`  | `STYLE \| SCRIPT \| TEMPLATE` | `@verter/unplugin`, default |
| `IDE`      | `TSX`                         | LSP, TSGO                   |
| `ANALYSIS` | `SCRIPT \| TEMPLATE_DATA`     | MCP analysis                |

**Key API**: `VerterHost::ensure_compiled(canonical_id, profile)` compiles with the given profile's target. Used by LSP and MCP to populate the cache. `get_virtual_file()` still exists for retrieving specific virtual file outputs.

**Empty SFC = valid empty component.** A completely block-less `.vue` file (0 bytes / whitespace / comments only) compiles to a minimal synthetic shell — `defineComponent({ __name: "<Filename>" })` + `export default` — through a dedicated synthetic-script branch (`empty_sfc_script_block` in `compile/helpers.rs`) adjacent to the scoped-style/vapor/SSR one, so the host assembles a `Main` virtual node instead of erroring `MissingVirtualNode`, and the imported public surface is empty (`$props: {}`, no slots). Zero-block files also count the whole input as one inter-block gap (`remove_inter_block_gaps`), so stray top-level comments never leak into generated module output. Template-only SFCs keep their existing no-synthetic-script shape.

## TypeExpr Lowering To The Semantic Graph (session boundary)

The OXC worker and the semantic-lowering surface produce owned `TypeExpr` IR (and worker-local OXC AST) ONLY — they never emit a session semantic-graph node (`SemanticNodeData` / `SemanticNodeId` / `HotTypeRef`); that crate barrier (`verter_semantic` never depends on `verter_session`) is locked from the worker side by the `oxc_worker_emits_no_session_graph_node` guard. Downstream, an engine-owned, query-free **structural lowerer** (`crates/verter_type_engine/src/structural_carrier_producer/macro_arg_producer.rs`, entry `lower_type_expr_structural`) consumes that owned `TypeExpr` and emits the dormant semantic-graph carriers (`BareRef` / `ImportType` / `RawFallback` / `SyntheticBinding`, with a construct-signature type lowered to `Signature { kind: Construct }` and tuple rest preserved on `TupleElement.rest`) plus the structural shells, NodeScopeId-rooted, performing NO name / import / type resolution: `Foo<Arg>` becomes a `BareRef` whose `type_args` are structurally lowered (never an `InstantiationRef`), and `keyof` / indexed-access / conditional / mapped / `typeof` stay deferred shells even where the eager path would reduce them. It is intern-only — it makes no host / dispatch query (`session_graph_lowerer_makes_no_query`) and never materializes a carrier back to `TypeExpr` during emission (`unresolved_carriers_not_materialized_during_emission`). It stays dormant / demand-time (never pulled into publish or indexing). Carrier RESOLUTION is a separate demand-time engine — see the type-resolution skill.

## Key Files

| File | Purpose |
| --- | --- |
| `crates/verter_compiler/src/compile.rs` | Pipeline orchestrator (tokenize -> parse -> style -> script -> template) |
| `crates/verter_compiler/src/parser/mod.rs` | SFC parser: tokenizer events -> root nodes + template AST |
| `crates/verter_compiler/src/ast/types.rs` | AstNode, ElementNode, NodeId, PropFlags |
| `crates/verter_compiler/src/script/macros.rs` | defineProps/Emits/Model/Slots/Expose/Options |
| `crates/verter_compiler/src/script/process.rs` | Script setup processing, companion script merging |
| `crates/verter_compiler/src/template/code_gen/mod.rs` | Template codegen entry point |
| `crates/verter_compiler/src/template/code_gen/walker.rs` | DFS tree walker (shared by VDOM/Vapor backends) |
| `crates/verter_compiler/src/template/code_gen/binding.rs` | BindingResolver (\_ctx./$setup. prefix resolution) |
| `crates/verter_compiler/src/template/code_gen/vdom/` | VDOM render function codegen |
| `crates/verter_compiler/src/template/code_gen/vapor/` | Vapor mode codegen |
| `crates/verter_compiler/src/ide/mod.rs` | IDE codegen entry: TSX (TS SFCs) or JSX+JSDoc (JS SFCs) |
| `crates/verter_compiler/src/ide/script.rs` | IDE script codegen: TS annotations or JSDoc equivalents |
| `crates/verter_compiler/src/ide/script_recover.rs` | Token scanner for macro binding recovery from broken tails |
| `crates/verter_compiler/src/ide/condition.rs` | v-if/v-else-if/v-else condition chain codegen |
| `crates/verter_compiler/src/ide/template/mod.rs` | IDE template codegen: Vue -> JSX, StrictSlotEntry, emit_strict_slot_checks |
| `crates/verter_compiler/src/ide/template/directives.rs` | IDE: v-if -> ternary, v-for -> .map(), v-show -> style |
| `crates/verter_compiler/src/ide/template/props.rs` | IDE: :prop -> prop={}, @event -> onEvent={} |
| `crates/verter_compiler/src/ide/template/emit.rs` | IDE typed prefixed-expression emit substrate (`EmitOp`, `emit_jsx_binding_value`) |
| `crates/verter_compiler/src/style_planner.rs` | Typed Vue stage-1/stage-2 planners over shared style IR |
| `crates/verter_compiler/src/css/mod.rs` | Legacy NAPI/CSS-modules compatibility owner pending provider cutover |
| `crates/verter_compiler/src/css/modules.rs` | CSS Modules: hash class names |
| `crates/verter_compiler/src/code_transform/code_transform.rs` | Chunk-based deferred mutation engine |
| `crates/verter_compiler/src/code_transform/chunk.rs` | Chunk types (Original, Overwritten, Inserted, InsertedMapped) |
| `crates/verter_compiler/src/code_transform/source_map.rs` | Source map generation from chunk positions |
| `crates/verter_compiler/src/framework_common/generated_chunk.rs` | Generated-chunk assembly and multi-source map composition |
