S264 Aot Final Validation Handoff
Documentation for Sessions
S264: AOT Final Validation Handoff
For: Co-work agent (final validation + AOT switch) From: S264.1-264.5 (architecture, implementation, bug fixes) Supersedes: S264-aot-testing-handoff.md (original handoff — S267 completed Phases A-D from that doc) Branch:
session-264-268Date: 2026-03-24
Status: What's Already Done
Completed by S264.1-264.2
- Self-hosted Angular runtime (21 ESM bundles, CDN eliminated)
- Compilation strategy pattern (JIT/AOT pluggable via injection token)
- Template compiler foundation (parser, Ivy codegen, decorator transformer)
- Wired into bundler pipeline
Completed by S267 (first test round)
- 43/43 unit tests passing (19 parser, 16 codegen, 8 transformer)
- Bug 1 fixed:
}}infinite loop in parseText() - Bug 2 fixed:
[(ngModel)]two-way binding parse order - Bug 3 fixed: array literal contextualization (
['/route']→ no ctx. prefix) - Bug 4 fixed:
ɵɵdefineComponent+ɵɵStandaloneFeature(were comments, not real calls) - Architectural finding: compiler CANNOT be eliminated (Angular library packages need JIT)
- AOT strategy updated to hybrid mode (always loads compiler for library code)
Completed by S264.4-264.5 (this round — fixes the S267 blockers)
- Bug 5 fixed:
dependenciesfield added toɵɵdefineComponent— the BLOCKING issue that caused blank renders - Bug 6 fixed:
@forparser paren-depth —items()signal syntax no longer breaks the parser - Bug 7 fixed:
@forloop variable scoping —item.namestaysitem.name, notctx.item.name changeDetectionextracted from @Component → injected into Ivy component definitionschemasextracted from @Component → injected into Ivy component definition (preserves NO_ERRORS_SCHEMA from enrichment)compilerEliminatedaccuracy — now scans for@Directive/@Pipe/@NgModule- All verified: 16/16 checks pass on realistic AnimalListComponent
What You Need To Do
Task 1: Browser Integration Test (THE critical validation)
This is the test that S267 attempted but couldn't pass due to Bugs 5-7 (now fixed).
Step 1: Confirm JIT baseline works
# Make sure dev server is running
yarn serve:space
# Open browser: http://localhost:6200
# Navigate to any space → universe → Studio → Live Preview
# Verify: preview renders, data loads, no red errors
# Console should show: "Angular is running in development mode"
# Console should show: "[useCrudList] Loaded N items"
Step 2: Switch to AOT
File: libs/libs-structural/platform-preview/src/lib/services/bundler.service.ts
Line ~15: Change the factory:
FROM: { factory: () => new JitCompilationStrategy() }
TO: { factory: () => new AotCompilationStrategy() }
Save the file. Wait for dev server to recompile (~3-5 seconds).
Step 3: Test AOT rendering
Reload the Live Preview tab (or navigate away and back).
Open browser DevTools → Console.
CHECK FOR:
✅ "[DecoratorTransformer] Transformed N component(s)" — AOT transform ran
✅ "Compiled (2 assets, Xms)" — rspack compilation succeeded
✅ "Angular is running in development mode" — Angular bootstrapped
✅ The preview renders UI (not blank)
✅ Data loads: "[useCrudList] Loaded N items"
✅ Click handlers work (click items in list, buttons)
✅ Navigation works (if multiple modules, click nav links)
CHECK AGAINST:
❌ "Preview Load Error" — AOT transform or rspack failed
❌ Blank white iframe with no errors — likely Bug 5 regression (missing dependencies)
❌ "ɵɵ... is not a function" — Ivy instruction mismatch
❌ "needs to be compiled using the JIT compiler" — decorator not transformed
Step 4: Compare JIT vs AOT
- Screenshot both. Does the rendered UI match?
- Are there any console errors in AOT that weren't in JIT?
- Does the compile time differ significantly?
Step 5: Revert to JIT
Change bundler.service.ts back to JitCompilationStrategy.
Save. This is important — don't leave AOT as default until validated.
Task 2: Edge Case Testing (if Task 1 passes)
Test these specific template patterns in AOT mode. Create a test component or find generated code that uses each:
| Pattern | What to check | Why it matters |
|---|---|---|
@for (item of items(); track item.id) |
Loop renders, scoping correct | Bug 6+7 fix validation |
[routerLink]="['/some-path']" |
No ctx. on array literal |
Bug 3 fix validation |
[(ngModel)]="value" |
Two-way binding works | Bug 2 fix validation |
{{title()}} signal interpolation |
Renders signal value | Core AOT functionality |
[class.active]="item.id === selectedId()" |
Mixed local+component scoping | Bug 7 fix validation |
(click)="select(item.id)" inside @for |
Handler uses loop var correctly | Scoping in handlers |
Nested @if inside @for |
Both control flow blocks render | Nested scope handling |
@switch with multiple @case |
Correct case renders | Switch codegen |
Component with provideHttpClient() |
HTTP calls work | Hybrid AOT+JIT for libraries |
Task 3: Performance Comparison (if Tasks 1-2 pass)
Measure in browser DevTools → Performance tab:
| Metric | How to measure | Target |
|---|---|---|
| Compilation time | Console: "Compiled (2 assets, Xms)" | AOT should be similar or faster |
| Bootstrap time | Time from "Compiled" to "Angular is running" | AOT should be faster (no template compilation) |
| Bundle size | DevTools → Network → look at main.js size | AOT should be smaller (no compiler import in main.js) |
Task 4: Flip the Switch (if ALL above pass)
If Tasks 1-3 validate successfully:
- Change default strategy to AOT in
bundler.service.ts:{ factory: () => new AotCompilationStrategy() } - Commit with message:
[S264.X] feat: switch default compilation strategy to AOT - Test one more time after commit to confirm
Architecture Reference (Quick)
Generated .ts files
→ BundlerFilesystemService.enrichForWorker() [enriches, injects schemas]
→ AotCompilationStrategy.transformSources() [decorator → Ivy instructions]
→ DecoratorTransformer.transformComponentFile()
→ TemplateParser.parse(template) → AST
→ IvyCodegen.generate(ast) → Ivy instructions
→ Replace @Component with static ɵcmp + ɵfac fields
→ Add dependencies, changeDetection, schemas
→ rspack Worker [bundles to main.js + styles.css]
→ PreviewServerService [wraps in HTML with prerequisites]
→ zone.js loaded first
→ @angular/compiler loaded (hybrid mode — library code needs JIT)
→ main.js parsed → Ivy instructions already present → bootstrap
Key files
| File | Role |
|---|---|
bundler.service.ts:15 |
THE SWITCH — JitCompilationStrategy vs AotCompilationStrategy |
decorator-transformer.ts |
@Component → Ivy static fields (dependencies, template fn, styles) |
template-parser.ts |
Angular template HTML → AST nodes |
ivy-codegen.ts |
AST → Ivy instruction code (CREATE + UPDATE blocks) |
aot-compilation.strategy.ts |
AOT strategy: transforms sources, loads compiler for library code |
jit-compilation.strategy.ts |
JIT strategy: no transforms, loads compiler for everything |
compilation-strategy.ts |
Interface both strategies implement |
All under: libs/libs-structural/platform-preview/src/lib/
Bug History (Complete)
| # | Bug | Severity | Found by | Fixed by | Status |
|---|---|---|---|---|---|
| 1 | }} infinite loop in parseText() |
CRITICAL | S267 | S267 | Fixed (1f1c8e6f6) |
| 2 | [(ngModel)] parse order |
HIGH | S267 | S267 | Fixed (1f1c8e6f6) |
| 3 | Array literal ['/route'] got ctx. prefix |
HIGH | S267 | S267 | Fixed (1f1c8e6f6) |
| 4 | ɵɵdefineComponent was a comment, not a call | HIGH | S267 | S267 | Fixed (1f1c8e6f6) |
| 5 | Missing dependencies → blank render |
BLOCKING | S267 | S264.4 | Fixed (d30bd8d33) |
| 6 | @for parser: items() broke on ) |
HIGH | S264.4 | S264.4 | Fixed (d30bd8d33) |
| 7 | item.name → ctx.item.name in @for |
HIGH | S264.4 | S264.5 | Fixed (005765588) |
Known Limitations (Acceptable for Ship)
Angular compiler still loaded in AOT mode — library packages (HttpClient, Router, Forms) ship as partially-compiled and need JIT. Our AOT only pre-compiles USER component templates. Benefit: faster bootstrap, not smaller bundle.
Pipe codegen is simplified — uses
ɵɵpipeBind(name, expr)pattern instead of proper slot-allocatedɵɵpipe()+ɵɵpipeBind1(). Low risk: generated code rarely uses pipes directly.Single @Component per file — transformer finds only the first @Component. Low risk: LLM generates one component per file.
No @Directive/@Pipe transformation — these pass through to JIT. The hybrid strategy handles this correctly (compiler is loaded).
String-based metadata extraction — works for predictable LLM output. Would break on creative formatting. Acceptable tradeoff vs. adding ts-morph dependency.
Success Criteria (Copy-Paste Checklist)
[ ] JIT baseline renders correctly (control)
[ ] AOT compilation succeeds (console: "Transformed N component(s)")
[ ] AOT preview renders (not blank, UI visible)
[ ] Data loads in AOT mode (console: "[useCrudList] Loaded N items")
[ ] Click handlers work in AOT mode
[ ] No new console errors vs JIT
[ ] @for loops render correctly with signal accessors
[ ] Mixed local/component expressions scope correctly
[ ] Performance: AOT bootstrap time ≤ JIT bootstrap time
[ ] Reverted to JIT works (no regression)
When ALL boxes are checked → flip the default to AOT and commit.