Skip to content

m3 alias resolution and regex rules

Kazushi Kamegawa edited this page Jul 30, 2026 · 3 revisions

M3: Alias resolution and regex rules

Status: Planned, reviewed. Tracked by issue #5 and sub-issues #37 through #43. A pre-implementation review found two blockers and several should-fix items, folded into the sections below and into comments on the affected sub-issues.

Scope

This plan implements every M3 item that does not depend on another milestone:

  • #37: load and validate rules JSON
  • #38: embed ordered default alias rules
  • #39: resolve aliases by the accepted regex-to-raw priority
  • #41: test alias and rule behavior
  • #42: select explicit, user, or embedded rules
  • #43: finalize canonical documentation

#40 remained open at the time of this plan because an executable test-rule command requires #53 for command-line parsing and #56 for process dispatch. Those dependencies were recorded as structured blocked-by relationships. Consequently, parent issue #5 also remained open after this plan was complete.

Resolved after M6 merged. #56 delivered the presentation itself: runTestRule() in src/cli/Dispatch.cpp prints file name → matched rule name → alias, which is what #40 asked for. #40 was reviewed against the merged code and closed as already implemented, so no separate M3 change is outstanding. docs/TODO.md M3's test-rule checkbox is still unticked and is corrected by M8's #64.

Design decisions

  • ADR-0009 is authoritative: the winget COM API does not expose portable command-alias metadata. Alias resolution therefore starts with regex rules and falls back to the raw executable filename.
  • The permanently unused PackageExe::metadataAlias field is removed.
  • JSON is parsed with winrt::Windows::Data::Json, using the existing ComApartment. No third-party dependency is introduced.
  • Regexes use std::wregex with ECMAScript grammar, full-filename matching, ordered first-match semantics, numbered captures, and the optional ignorecase flag.
  • Resolved aliases must be non-empty .exe filenames without path separators or dot path components. Reject (not normalize) an empty stem, a replacement that does not end in .exe, and cover extension case-folding explicitly in tests.
  • winrt::Windows::Data::Json parses rules.json (per ADR-0005; no header-only parser is introduced), but this requires an initialized apartment at runtime — the known tension already flagged in adr.md ADR-0005 and the cpp-msbuild skill. #37 resolves this tension with a new ADR entry rather than leaving it implicit, and gives RuleSet's own tests a ComApartment fixture. The translation unit that calls into winrt::Windows::Data::Json carries its own #pragma comment(lib, "runtimeobject.lib") — the existing one in WingetComSource.cpp only propagates into links that pull in WingetComSource.obj, which a RuleSet-only test link need not do.
  • Matching uses std::regex_match (whole-string), matching the "full-filename matching" design intent, not std::regex_search.
  • Malformed user rules file behavior (resolves the ambiguity in the prior draft of this page): an absent user rules file (%LOCALAPPDATA%\syncwingetlink\rules.json) falls through to the embedded defaults — that is the normal, unconfigured state. A present but malformed/invalid user rules file is an error, exit code 3, identical to a broken --rules <path> file. It never silently falls back to embedded defaults, since that would hide a config mistake the user made themselves.

Delivery sequence

Each sub-issue is delivered as a separate branch and pull request, based on main after the preceding dependency is merged:

  1. #37 adds rules/RuleSet, typed configuration errors, JSON parsing, and validation. Parsing is string-based (parse(text) -> rules); file I/O is left to #42's source selection so these tests stay filesystem-free. Also adds the ADR entry for the WinRT-Json/apartment decision above, the ComApartment test fixture, and the runtimeobject.lib pragma in the JSON-parsing translation unit.
  2. #38 adds embedded Rust-target-triple and version/architecture rules, plus at least one negative test proving the broad strip-version-and-arch default does not over-match a file name it should leave alone.
  3. #39 adds core/AliasResolver, removes the dead metadata field, and records the M3 architecture decision. Removing PackageExe::metadataAlias also requires removing or rewriting tests/ExecutableScannerTests.cpp's metadataAliasIsNotSuppliedByTheFilesystem, which asserts on that field directly and would otherwise fail to compile under /W4 /WX.
  4. #42 adds source selection in the order explicit rules, user rules, then embedded rules. An explicitly selected (--rules <path>) file that is missing, unreadable, or invalid is an error (exit 3) and never silently falls through. The auto-discovered user rules file follows the absent/malformed distinction above.
  5. #41 adds the cross-component regression matrix, including the alias-validation edge cases and rule over-matching tests called out above.
  6. #43 corrects docs/rules.md, the design, the TODO, agent guidance, and the work log: the currently documented "COM metadata" tier-1 alias priority describes an API that does not exist (ADR-0009) and must be dropped, not merely synced — the priority list is regex rules, then raw file name.

Every added source file is registered in both the core or test project and its filters file. Core logic stays in the static library.

Test plan

  • Validate accepted and rejected version 1 JSON documents, required field types, unique non-empty rule names, flags, malformed regexes, and UTF-8 input.
  • Verify rule ordering, case behavior, numbered replacement captures, invalid alias rejection, and raw-filename fallback.
  • Verify the Codex x64, ARM64, and GNU target suffixes plus a representative version/architecture suffix.
  • Verify explicit rules beat user rules, user rules beat embedded rules, an absent user rules file selects embedded rules, and a present-but-malformed user rules file errors (exit 3) rather than falling back to embedded rules.
  • Verify alias-validation edge cases: an empty capture that would yield a bare .exe, a replacement not ending in .exe, and extension case-folding.
  • Confirm a RuleSet parsing test fails clearly if the ComApartment fixture is absent, and that a tests-only link exercising RuleSet without WingetComSource succeeds.
  • Build core and tests in Debug and Release for x64 and ARM64 at the warnings-as-errors setting.
  • Run the x64 Debug and Release test DLLs and record actual results. Record ARM64 as cross-built rather than run unless execution occurs on an ARM64 host.

The full solution continues to have the known unresolved executable entry point until #56 supplies main.cpp; targeted core and test builds are the completion evidence for this milestone.

Completion criteria

  • #37, #38, #39, #41, #42, and #43 are merged and closed.
  • #40 was open and structurally blocked by #53 and #56 at the time of this plan; it was later closed as already implemented by #56's runTestRule() (see above).
  • Issue #5, docs/TODO.md, docs/task.md, and this Wiki page report the same state.
  • All targeted builds and x64 tests pass, with no new warning or dependency.
  • Canonical documentation and code comments are English. Localized *_ja.md files are not read or changed.

Clone this wiki locally