Rust + KID pivot — M3 Part 2: mora-cli end-to-end compile#45
Merged
Rust + KID pivot — M3 Part 2: mora-cli end-to-end compile#45
Conversation
Delivers the first real `mora compile`: clap subcommand, EspWorld open, KID INI discovery + parse, KidDistributor run, mora_patches.bin serialize and write. Adds load_order_hash to mora-esp (blake3 digest over canonical load-order representation, truncated to u64). 9 tasks across 5 phases. Explicit --data-dir + --plugins-txt flags; --output defaults to <data-dir>/SKSE/Plugins/mora_patches.bin; --dry-run modifier. End-to-end integration tests subprocess the built binary against synthetic Skyrim dirs + KID INIs.
Documents the mora compile subcommand: flags, exit codes, typical output format, and load_order_hash semantics. Future commands (check, info) flagged as deferred.
blake3-truncated-to-64-bit digest over a canonical load-order representation (plugin filenames + ESM/ESL flags + master lists, all lowercased and NUL-separated). Populates PatchFile::load_order_hash; runtime verifies on load and refuses on mismatch.
Cli { command: Commands::Compile(CompileArgs) }. CompileArgs exposes
--data-dir, --plugins-txt, --output, --dry-run, --verbose flags.
resolve_output() defaults to <data-dir>/SKSE/Plugins/mora_patches.bin.
Initializes an env-filter-aware subscriber with stderr output, no timestamps, no targets, no spans — clean log lines for user-facing output. Respects RUST_LOG; --verbose flips default to debug.
Opens EspWorld, computes load-order hash, discovers + parses KID INIs, runs KidDistributor, writes mora_patches.bin (or skips on --dry-run). anyhow::Context wraps every step with user-readable error messages.
Parses args via clap, initializes logging, dispatches to the requested subcommand. On error, prints the anyhow error chain to stderr and exits with code 1.
Fixture struct creates a tmpdir with synthetic plugins + plugins.txt, cleans up on Drop. build_plugin() composes minimal ESM bytes with optional KYWD/WEAP/ARMO groups. mora_bin() locates the built binary.
4 tests against a built mora binary + synthetic Skyrim dirs: compile_produces_valid_patch_file, compile_dry_run_does_not_write, compile_bad_data_dir_exits_nonzero, compile_empty_kid_ini_set. Exercises the full pipeline: argv parse -> EspWorld::open -> pipeline::compile -> PatchFile serialization -> on-disk bytes.
Whitespace normalization; no functional changes.
blake3's build script compiles C acceleration code when targeting Windows, which requires llvm-lib (ships in Ubuntu's llvm package). cargo-xwin installs only the Rust wrapper — the underlying LLVM toolchain must come separately. Fix for Plan 7 (which first added blake3 to mora-esp via the load_order_hash module).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Delivers the first real
mora compilecommand perdocs/superpowers/plans/2026-04-21-rust-kid-pivot-plan-7-mora-cli.md:morabinary withcompilesubcommand via clap--data-dir+--plugins-txtrequired flags;--outputoptional (defaults to<data-dir>/SKSE/Plugins/mora_patches.bin);--dry-run+--verbosemodifiers*_KID.inifiles, runs KidDistributor, writesmora_patches.bindocs/src/mora-cli-reference.md— CLI UX referenceTest plan
cargo test --workspace— 174 tests pass (167 prior + 7 new: 4 end-to-end integration + 1 fixture smoke + 2 load-order-hash)cargo clippy --all-targets -- -D warningscleancargo fmt --checkcleancargo xwin check --target x86_64-pc-windows-msvc --workspaceclean./target/debug/mora compile --helprenders correctlyIntegration testing
End-to-end integration tests subprocess the compiled
morabinary against synthetic Skyrim Data directories:compile_produces_valid_patch_file— builds a plugin withWeapMaterialIron+IronSword, writes aTest_KID.iniwithWeapMaterialIron = Weapon, runsmora compile, verifies outputPatchFilehas correctAddKeywordpatchcompile_dry_run_does_not_write—--dry-runproduces no on-disk filecompile_bad_data_dir_exits_nonzero— non-directory path exits with code 1 + "data-dir" in stderrcompile_empty_kid_ini_set_produces_empty_patches— Data dir with no*_KID.iniproduces an empty-but-well-formed patch fileScope discipline
mora compileonly.mora check+mora infodeferred.Next up
Plan 8: complete KID parity on Weapon + Armor — activate
ALL/ANYfilter buckets (currently parsed but skipped at evaluation), implement remaining trait predicates (anim type, armor type, DNAM fields on records), parse + honor ExclusiveGroups.At that point Weapon + Armor rules hit the full KID filter grammar. Other 18 record types then land per-type as Plan 9+ adds the mora-esp record accessors they need.