Saltar a contenido

Auditoría post-Fase 5b — deudas y mejoras

Documento generado tras cerrar Fase 5b (codegen a binario nativo). Identifica deudas técnicas, gaps de docs, mejoras de calidad/UX. No ejecuta fixes — es input para decidir qué atacar y en qué orden.

🟢 Body form-urlencoded → tipo declarado en fitz run (CERRADO v0.50.0, 2026-08-20)

El gap (clase FITZ-14, paridad runbuild): un handler @post fn login(creds: Credentials) que recibía un body application/x-www-form-urlencoded deserializaba al type declarado en fitz build (confirmado sobre el binario, A5 refutado), pero en fitz run el body llegaba como un Value::Map crudo en vez de una Instance → un creds.user explotaba con "field access .user on a value of type Map" (500). El path JSON sí coercía; el form-urlencoded no. Bloqueaba el login zero-JS (<form method=POST>, el caso canónico de FITZ-05 + MatHelp) en el intérprete.

El fix (src/http.rs, parse_urlencoded_body): en vez de armar un Value::Map directo, ahora construye un serde_json::Value::Object all-string (paralelo bit-a-bit al __parse_urlencoded del codegen) y reusa el MISMO path de coerción que el body JSON (parse_body): json_to_instance(&obj, t) cuando hay tipo declarado, json_to_value (free Map<Str, Str>) cuando no. El codegen ya hacía esto (__parse_urlencoded__from_fitz_json), así que el fix es puro alineamiento del intérprete → paridad bit-a-bit. Defaults/nullables aplicados igual que en JSON; body vacío con tipo declarado → defaults o "missing field" claro. Los 3 tests urlencoded existentes (body libre, declared_type: None) quedan idénticos (json_to_value da el mismo Map, preserve_order mantiene el orden). 2 tests nuevos en http::tests (e2e_fitz05_form_urlencoded_*: typed body + defaults/nullables). Smoke real: login POST form-urlencoded → 2 Set-Cookie idénticos en fitz run y en el binario.

🟢 Deuda A — fitz check valida los módulos .fitz importados (CERRADO v0.43.0, 2026-08-18)

El gap: fitz check sobre un .fitz clásico chequeaba solo el entry. Los módulos importados nunca se parseaban ni type-checkeaban — solo se pre-escaneaban señales (auth provider W12 / background B10 / live components §9.bb). Un error de sintaxis (o de tipos) en un módulo importado pasaba fitz check limpio y solo explotaba en fitz run (parse) o fitz build (type-check del loader). El repro que lo destapó: un Err(_) => return, bare (return como valor de arm) en un empleados.fitz importado.

El fix (src/main.rs, ~180 LoC, sin tocar el checker ni el codegen): el entry rutea por check_file_with_deps (dep-aware) que, tras chequear el entry, llama check_imported_fitz_modules — un walker que recorre el grafo de imports (transitivo) y corre lexer + parser + check_program_with_context sobre cada módulo local .fitz. Decisiones: - Walker dedicado, NO reuso del codegen::ModuleLoader — el loader corre codegen por módulo (emite .rs), lo que agrega superficie de fallo y peores diagnósticos (solo el primer error, sin span). El walker reusa las piezas que importan (resolve_import_file_path, los pre-scans, el checker) con cero acoplamiento al codegen. - Contexto del proyecto heredado (ProjectCheckContext): el provider / bg fns / live components pre-escaneados de los imports DIRECTOS del entry se pasan como fallback a cada módulo (paralelo a los main_imported_* del loader). Sin esto, un handler @authenticated en comments.fitz (que no importa auth.fitz, lo cablea main.fitz — patrón B12) daría un falso positivo. El módulo propio gana; lo que no declara cae al contexto del proyecto. - .fitzv se saltean en el walk (cubiertos por el sweep a2 + el pipeline de view). Deps externas de fitz.toml se saltean (build las valida). - Ciclos + dedup por visited set de paths canónicos. - Errores agregados (no aborta al primero); el entry sigue siendo dueño de su propio reporte. check_file (wrapper viejo) se eliminó — todo pasa por check_file_with_deps.

7 tests cli_e2e (deuda_a_check_*). Smoke verde sobre api-orm-full / api-orm-full-fullstack / api-fullstack-postgres (multi-módulo con B12 + subcarpetas). Actualiza el comment de Commands::Check (que decía "does NOT recurse into imported modules").

Deudas residuales derivadas (NO bloquean, refinables si aparece demanda): - Un nivel de profundidad del project context: el provider/bg/live heredado se pre-escanea de los imports DIRECTOS del entry (paralelo al loader). Si el provider viviera en un módulo importado transitivamente (no directo del entry), un módulo hermano podría dar falso positivo. El patrón canónico (main importa todos los módulos) lo cubre. - Un .fitz importado desde un .fitzv no lo alcanza el walker (skipeamos el .fitzv); lo cubre el pipeline de view del .fitzv. - import_root fallback parcial: el walker resuelve contra el dir del módulo importador, con fallback al dir del entry — no replica el import_root exacto del loader del codegen para todos los layouts anidados raros (el loader tiene su propia lógica). Los layouts probados (subcarpetas src/types/, src/data/) resuelven bien.

🟢 Paso 0 SSR isomórfico — to_json en fitz build sin HTTP (CERRADO 2026-08-07)

Fix aplicado (2026-08-07): se extrajo el core de serialización JSON (axum-free) de HTTP_RUNTIME_PRELUDE a un JSON_SERIALIZE_PRELUDE independiente en src/codegen.rs — los dos traits __ToFitzJson / __FromFitzJson + los impls de primitivos/contenedores (Option, Vec, Arc<Mutex>, Vec<(K,V)>, Result) + __MapKey/__MapKeyFromStr + __json_shape. HTTP_RUNTIME_PRELUDE conserva solo los tipos de dominio HTTP (Request/Response/File) + el glue axum (__apply_cors_and_respond) + los parsers urlencoded/multipart. El JSON prelude se emite cuando has_http || uses_to_json, antes del HTTP prelude (los impls de dominio dependen de los traits; Rust resuelve impls sin importar orden, así que el output HTTP es funcionalmente idéntico). Nuevo detector program_uses_to_json (walk del AST, call de ident pelado — paralelo a program_uses_smtp), campo CodegenCtx.uses_to_json + transitividad por módulo (LoadedModule) + mirror lado-módulo (use crate::{__ToFitzJson, __FromFitzJson}), gating de los per-type impls (gen_type_http_impls + emit_helpers_for_imported_types) por has_http || uses_to_json, y serde_json (con preserve_order) al Cargo.toml generado cuando uses_to_json y ningún otro branch ya lo trajo (sin duplicar). Un fitz build de un CLI puro con to_json ahora solo linkea serde_json (sin axum ni tokio) y produce output bit-a-bit idéntico a fitz run (validado a mano + E2E to_json_in_pure_cli_build_matches_interpreter en tests/compile_e2e.rs). Tests: +5 unit codegen (to_json_in_pure_cli_*, program_uses_to_json_*, cargo_toml_*_serde_json*) + 1 E2E. 4024 lib (default) + 4185 (lsp) verdes; fmt + clippy -D warnings (default + lsp) limpios. Divergencias run/build menores siguen (Float NaN/Inf, Bytes, Map con clave nominal) — ver detalle histórico abajo.

Detalle histórico (deuda original, 2026-08-03) El builtin nuevo `to_json(x) -> Str` (Paso 0 del render-a-string isomórfico SSR; alimenta el `