Fixed
- musl release binary now runs on NixOS and non-FHS distros. The bundled
runtime/libgcc_s.so.1was previously Ubuntu's glibc-linked copy, which
depends onld-linux-x86-64.so.2— absent on NixOS. The release workflow
now sourceslibgcc_s.so.1from Alpine Linux (a musl-based distro), so the
library depends onlibc.so(musl) instead. The zig/cargo-zigbuild approach
introduced to work around this is reverted; the build usesmusl-gccagain.
Changed
normalize context --helpnow shows comprehensive inline reference. The help output
includes frontmatter format,--matchdot-path syntax,--stdin/--prefixJSON injection,
--filestructured file loading, and examples. Previously the description was a one-liner.
Fixed
-
normalize context --helpno longer shows a duplicatecontextsubcommand.
The default action method was namedcontext(same as the parent service), causing
normalize context contextto appear in help output. The method is now hidden from
the subcommand list (#[cli(hidden)]);normalize contextcontinues to work as
the default action with all flags hoisted to the parent command. -
cargo xtask build-grammars --cc "zig cc -target x86_64-linux-musl"now works.
The--ccargument is split on whitespace so compound compilers likezig cc -target x86_64-linux-muslare correctly parsed into program + arguments. PreviouslyCommand::new
was called with the entire string as the binary name, causing "No such file or directory"
for every grammar. zig's lld linker also requires--allow-shlib-undefinedinstead of
--unresolved-symbols=ignore-in-shared-libs; the xtask now detects zig cc and emits the
correct flag. -
Grammar ABI mismatch after
normalize update.ensure_grammars_first_usenow
reads the.installed-versionstamp and compares it against the running binary's
version. If they differ (e.g. after a self-update), the stamp is deleted and grammars
are re-downloaded for the current binary before any command runs. Previously, an
existing stamp caused the check to short-circuit unconditionally, leaving stale 0.2.x
.sofiles loaded by a 0.3.x binary. -
normalize updatenow invalidates the grammar stamp immediately after replacing
the binary, so the next invocation triggers a grammar re-download even if the process
exits beforeensure_grammars_first_useruns. -
Friendly error for removed
[embeddings]config key. Loading.normalize/config.toml
now pre-checks for[embeddings](removed in 0.3.0) and exits with a clear migration
message instead of a generic parse error.
Added
-
normalize edit extract-function <file> --lines <start>-<end> --name <name> [--apply]command. Extracts a line range from a function into a new function using CFG liveness analysis. Infers parameters (variables live into the region from outside) and return values (variables defined inside the region and live after it) via backward-dataflow fixed-point over the facts index. Checkscfg_effectsfor async, generator, defer, and acquire/release semantics; emits warnings for defer crossing boundary, unbalanced resource lifetime, and escaping exception edges. Generates language-appropriate source for Rust, Python, Go, TypeScript/JavaScript, and Java. Default is dry-run;--applywrites the changes. Requiresnormalize structure rebuild. -
CFG Phase 4: type-refined exception flow.
Edgenow carriesexception_type: Option<String>forEdgeKind::Exceptionedges (None = conservative/unknown;Some("T")= typed). The CFG builder captures@cfg.exit.throw.typeand@cfg.try.catch.typefrom.cfg.scmqueries to emit typed exception edges. Exception edges inbuild_tryare emitted per catch type;ExitThrowedges carry the thrown type. -
Exception type captures in 5 languages. Java: thrown type from
object_creation_expression.type; catch type fromcatch_formal_parameter/catch_type/type_identifier(handles multi-catchIOException | SQLException). Python: thrown type fromraise_statement/call.function; catch type fromexcept_clause/identifier(single) andexcept_clause/as_pattern/tuple/identifier(multi). JavaScript/TypeScript/TSX: thrown type fromthrow_statement/new_expression.constructor; catch clauses are untyped (catches all). C++: thrown type fromthrow_statement/call_expression.function(identifierorqualified_identifier); catch type fromcatch_clause/parameter_list/parameter_declaration.type. C#: thrown type fromthrow_statement/object_creation_expression.type; catch type fromcatch_clause/catch_declaration.type. -
exception_typecolumn incfg_edgesSQLite table. Nullable TEXT column added tocfg_edges. Schema version bumped to 15. Bothrefresh_call_graphandreindex_filespaths updated. -
CfgEdgeFact.exception_typefield.cfg_edgeDatalog preamble relation extended to 7 fields:cfg_edge(file, func, func_line, from, to, kind, exception_type).liveness.dlupdated to use the 7-field form (_, _for the two new wildcards).relations.add_cfg_edgegains theexception_typeparameter. Bothfacts.rsandrunner.rsnow loadall_cfg_edges()from the index intoRelations. -
exception_flow.dlbuiltin Datalog rule. Derivesexception_reaches(file, func, func_line, throw_block, catch_block, type),unhandled_exception(file, func, func_line, throw_block, type), andcan_throw(file, func, func_line). Disabled by default; enable withnormalize rules enable exception_flow. -
Mermaid renderer shows exception type on edges. Exception edges now render as
b3 -->|"exception: IOException"| b5when a type is known, andb3 -->|"exception"| exitwhen conservative. -
normalize analyze exceptions <file> [--function <name>]command. Reports throw sites with their exception type and the catch clauses they route to. Flags unhandled throws (escaping to function exit). Shows catch clauses with types and handled-throw counts (including "dead catch?" annotation for clauses that handle 0 throws). Requiresnormalize structure rebuild. -
Cfg::throw_edges()helper. Returns an iterator over allEdgeKind::Exceptionedges in the CFG. -
CFG Phase 3: effects tracking.
BasicBlocknow carrieseffects: Vec<Effect>. NewEffectKindenum:Await,Defer,Yield,Acquire,Release,Send,Receive. NewBlockKindvariants:Deferred,Acquire,Release. NewEdgeKindvariants:Suspend,Resume. The builder collects@cfg.effect.*captures from.cfg.scmqueries and assigns them to the enclosing block. -
Effect queries for Rust, Python, TypeScript, JavaScript, Go.
@cfg.effect.awaitonawait_expression(Rust, TS, JS) and(await)(Python).@cfg.effect.yieldonyield_expression(TS, JS) and(yield)(Python).@cfg.effect.acquireonwith_statement(Python). Go effect queries:@cfg.effect.deferondefer_statement,@cfg.effect.sendongo_statementandsend_statement,@cfg.effect.receiveon unary<-expressions. -
cfg_effectsSQLite table. New table in the structural index:cfg_effects (file, function_qname, function_start_line, block_id, kind, byte_offset, line, label). Populated bynormalize structure rebuild. Schema version bumped to 14. -
CfgEffectFactDatalog relation. Newcfg_effect(file, func, func_line, block, kind, line, label)relation innormalize-facts-rules-api, exposed in the Datalog preamble and loaded in therules runandfactspipelines. -
effects.dlbuiltin rule. Derivesasync_function,defer_function,generator_function,resource_acquire,resource_leakfromcfg_effect. Disabled by default; enable withnormalize rules enable effects. -
normalize analyze effects <file> [--function <name>]command. Reports suspension points, deferred calls, yields, resource acquisitions, and channel operations for functions in a file. Requiresnormalize structure rebuild. -
normalize analyze liveness <file> --function <name>command. Computes live-in and live-out variable sets per basic block using standard backward-dataflow liveness analysis. Requires the structural index (normalize structure rebuild). Returns aLivenessReportwith per-blockBlockLivenessentries showing which variables are live at block entry and exit. -
CFG Phase 2: def/use captures.
BasicBlocknow carriesdefs: Vec<DefSite>anduses: Vec<UseSite>. The builder recognises@cfg.def/@cfg.def.nameand@cfg.use/@cfg.use.namecaptures from.cfg.scmqueries and assigns them to the enclosing block. Rust, Python, and Go.cfg.scmfiles updated with variable definition captures. -
CFG SQLite persistence. Four new tables in the structural index:
cfg_blocks,cfg_edges,cfg_defs,cfg_uses. Populated bynormalize structure rebuildfor all files whose language has a.cfg.scmquery. Schema version bumped to 13. -
Datalog CFG relations. Four new relations in
normalize-facts-rules-api:cfg_block(file, func, func_line, block, kind),cfg_edge(file, func, func_line, from, to, kind),cfg_def(file, func, func_line, block, name),cfg_use(file, func, func_line, block, name). Available in.dlrule files via the preamble. -
liveness.dlbuiltin rule. Registers standard backward-dataflow liveness (live_in/live_outderived relations) as a disabled-by-default built-in Datalog rule. Enable withnormalize rules enable liveness. -
normalize cfgcommand. Newnormalize-cfgcrate with a control flow graph builder and Mermaid renderer.normalize cfg <file> [-f <function>]builds a CFG from any file with a supported.cfg.scmquery and renders it as a Mermaidflowchart TD. Data model:Cfg,BasicBlock(withBlockKind: Entry, Exit, Statement, Branch, LoopHead, LoopBody, LoopExit, Catch, Unreachable),Edge(withEdgeKind: Fallthrough, ConditionalTrue, ConditionalFalse, BackEdge, Break, Continue, Return, Exception). Rust, Python, and Go have bundled queries. -
GrammarLoader::get_cfg(name)innormalize-languages. Parallel toget_complexity, loads.cfg.scmquery files from the grammar search path with fallback to bundled queries. -
Rust CFG query (
rust.cfg.scm). Capturesif_expression(branch + condition/then/else),match_expression(match + arms),while_expression/for_expression(loop + condition/body),loop_expression(unconditional loop + body),return_expression,break_expression,continue_expression, andpanic!/todo!/unreachable!macros (throw). Snapshot tests for 6 fixtures: linear, branch, loop, nested, early_return, match. -
Python CFG query (
python.cfg.scm). Capturesif_statement(branch + condition/then/else),match_statement/case_clause(Python 3.10+),for_statement/while_statement(loop + condition/body),try_statement/except_clause/finally_clause,return_statement,break_statement,continue_statement,raise_statement. Snapshot tests for 4 fixtures: linear, branch, loop, early_return. -
Go CFG query (
go.cfg.scm). Capturesif_statement(branch + then/else),expression_switch_statement/expression_case_clause(match),for_statement(loop + body; covers range, condition, and unconditional forms),return_statement,break_statement,continue_statement. Snapshot tests for 4 fixtures (skipped gracefully when Go grammar not installed). -
TypeScript and TSX CFG queries (
typescript.cfg.scm,tsx.cfg.scm). Capturesif_statement/else_clause(branch),switch_statement/switch_case/switch_default(match),for_statement/for_in_statement/while_statement/do_statement(loop),try_statement/catch_clause/finally_clause,return_statement,break_statement,continue_statement,throw_statement. Verified against arborium-typescript and arborium-tsx node-types.json. Snapshot tests for 6 fixtures: linear, branch, loop, early_return, try_catch, switch. -
JavaScript CFG query (
javascript.cfg.scm). Identical control flow grammar as TypeScript (shared arborium base). Same captures; verified against arborium-javascript node-types.json. Snapshot tests for 4 fixtures: linear, branch, loop, early_return. -
Java CFG query (
java.cfg.scm). Capturesif_statement(branch),switch_expression/switch_block_statement_group/switch_rule(match — covers both statement and expression form),for_statement/enhanced_for_statement/while_statement/do_statement(loop),try_statement/try_with_resources_statement/catch_clause/finally_clause,return_statement,break_statement,continue_statement,throw_statement. Labeled break/continue are captured as exits; label resolution deferred. Verified against arborium-java node-types.json. Snapshot tests for 5 fixtures (skipped gracefully when Java grammar not installed). -
CFG coverage matrix test. New
coverage_matrix.rstest innormalize-cfg/tests/classifies all registered languages asHAS_CFG(rust, python, go, typescript, tsx, javascript, java),NOT_APPLICABLE(data/markup/config formats: json, yaml, toml, xml, html, css, scss, graphql, sql, and others), orDEFERRED(languages with control flow but no query yet). Thecfg_has_cfg_languages_return_sometest asserts all HAS_CFG grammar names returnSomefromGrammarLoader::get_cfg. -
CFG Phase 1: batch queries for 69 additional languages. Added
.cfg.scmquery files for all DEFERRED languages: C-family (C, C++, ObjC, C#, Kotlin, Swift, Dart), JVM/functional (Scala, Groovy, VB, Haskell, OCaml, F#, Elixir, Erlang, Clojure, Gleam, ReScript, Idris, Agda, Lean, CommonLisp, Scheme, Elisp), scripting (Ruby, Lua, PHP, Perl, Bash, Fish, Awk, Zsh, PowerShell, Batch, Vim), systems/scientific (Zig, Ada, D, Prolog, R, Julia, MATLAB, GLSL, HLSL, Verilog, VHDL), and domain/config (Nix, HCL, Starlark, Elm, Jinja2, Svelte, Vue, CMake, Meson, TLA+, jq). Moveddockerfileandquery(tree-sitter query language) to NOT_APPLICABLE. Remaining DEFERRED: asm, x86asm (assembly — need grammar inspection), uiua (array language). Snapshot tests added for Lua and Jinja2 (grammars installed); all other tests skip gracefully when grammar not installed. Coverage matrixcfg_has_cfg_languages_return_somenow verifies 76 languages. -
ModuleResolverfor 7 additional languages. Elm (elm.jsonsource-directories, module name → file path), Nix (relative./path.nixresolution;<nixpkgs>→ NotFound), R (source("./file.R")relative load;library(pkg)→ NotFound), Julia (include("file.jl")relative include + workspaceProject.tomlpackage lookup), MATLAB (filename stem = function name; searches workspace root +src/+lib/), Prolog (relativeuse_module+ bare name search in workspace root;library(...)→ NotFound), D (dub.jsonsourcePaths,mypackage.utils→mypackage/utils.d). Resolver matrix test updated. -
ModuleResolverfor 20 additional languages. JVM languages: Java, Kotlin, Groovy, Scala (Maven/Gradlesrc/main/<lang>conventions). .NET languages: C#, VB, F# (namespace→file path mapping). Swift (SPMSources/<target>directory targets). Dart (pubspec.yamlpackage:import resolution). Zig (@importrelative path resolution). Elixir (Mixlib/CamelCase↔snake_case). Erlang (1:1 module=file). Haskell (Cabalhs-source-dirs). OCaml (capitalized stem convention). Lua (requiredot-path). PHP (composer.json PSR-4 autoload). Perl (lib/::path). Clojure (src/dot-namespace). Common Lisp (workspace stem). Scheme (R7RS.sld/.scm). Gleam (gleam.tomlsrc/). ReScript (bsconfig.json sources). -
Phase 0 scaffold: cross-file name resolution infrastructure. New Datalog predicates in
normalize-facts-rules-api:resolved_import,module,export,reexport,symbol_use,resolved_reference,resolved_call,module_search_path. NewModuleResolvertrait innormalize-languages::traitsfor per-language import resolution, with supporting typesImportSpec,ModuleId,Resolution,ResolverConfig. New cratenormalize-module-resolvere-exporting the trait and types. Newresolution.dlDatalog rules derivingresolved_referenceandresolved_call(disabled by default, requiresnormalize structure rebuild). -
Rust
ModuleResolver(RustModuleResolver). Resolvesuse/modimport specifiers to file paths within Cargo workspaces. Handlesworkspace_config(parsesCargo.tomlfor workspace members and crate names),module_of_file(derives canonical module path from file's position undersrc/), andresolve(mapscrate::module::nameto the.rsfile, handlessuper::/self::relative paths, returnsNotFoundfor stdlib and external crates). Tested with a 3-file fixture innormalize-refactor/tests/. -
TypeScript/TSX
ModuleResolver(TsModuleResolver). Resolves relative imports (./,../), tsconfig.jsoncompilerOptions.pathsaliases andbaseUrl, and.js→.tsextension elision. ReturnsNotApplicablefor non-TS/TSX files,NotFoundfor node_modules. -
JavaScript
ModuleResolver(JsModuleResolver). Resolves relative imports (.js,.mjs,/index.js), jsconfig.jsoncompilerOptions.pathsandbaseUrl. ReturnsNotFoundfor bare specifiers (node_modules). -
Python
ModuleResolver(PythonModuleResolver). Resolves relative imports (from . import,from ..pkg import), detectssrc/layout, searches workspace root for absolute imports. ReturnsNotFoundfor stdlib/third-party. -
Go
ModuleResolver(GoModuleResolver). Parsesgo.modto extract the module path; resolves import paths to directory targets within the module. ReturnsNotFoundfor stdlib and third-party packages. -
Ruby
ModuleResolver(RubyModuleResolver). Resolvesrequire_relativeto.rbfiles relative to the caller. ReturnsNotFoundfor barerequire(gems not modeled). -
ModuleResolverpass instructure rebuildpipeline. After the existingresolve_all_importspass,resolve_imports_via_module_resolver()runs as a second pass using per-language resolvers to populateresolved_filefor any imports still unresolved. Applies to full rebuild, incremental update, and single-fileupdate_file. All three rebuild paths now include this pass. -
find_referencesconfidence tagging.CallerRefandImportRefnow carry aconfidencefield ("resolved"|"heuristic"). Results are tagged"resolved"when the definition file's language has aModuleResolver, and"heuristic"otherwise. Downstream consumers (rename, safe-delete) can filter on this field.
Installation
curl -fsSL https://rhi.zone/normalize/install.sh | shirm https://rhi.zone/normalize/install.ps1 | iexManual download: pick the archive for your platform from the assets below and verify with SHA256SUMS.txt.