rime/weasel
【小狼毫】Rime for Windows
Single-maintainer risk — review before adopting
worst of 4 axescopyleft license (GPL-3.0) — review compatibility
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 2mo ago
- ✓12 active contributors
- ✓GPL-3.0 licensed
Show 4 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Single-maintainer risk — top contributor 84% of recent commits
- ⚠GPL-3.0 is copyleft — check downstream compatibility
What would change the summary?
- →Use as dependency Concerns → Mixed if: relicense under MIT/Apache-2.0 (rare for established libs)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/rime/weasel)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/rime/weasel on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: rime/weasel
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/rime/weasel shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Single-maintainer risk — review before adopting
- Last commit 2mo ago
- 12 active contributors
- GPL-3.0 licensed
- CI configured
- Tests present
- ⚠ Single-maintainer risk — top contributor 84% of recent commits
- ⚠ GPL-3.0 is copyleft — check downstream compatibility
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live rime/weasel
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/rime/weasel.
What it runs against: a local clone of rime/weasel — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in rime/weasel | Confirms the artifact applies here, not a fork |
| 2 | License is still GPL-3.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 94 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of rime/weasel. If you don't
# have one yet, run these first:
#
# git clone https://github.com/rime/weasel.git
# cd weasel
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of rime/weasel and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "rime/weasel(\\.git)?\\b" \\
&& ok "origin remote is rime/weasel" \\
|| miss "origin remote is not rime/weasel (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(GPL-3\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"GPL-3\\.0\"" package.json 2>/dev/null) \\
&& ok "license is GPL-3.0" \\
|| miss "license drift — was GPL-3.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "WeaselServer/WeaselServerApp.cpp" \\
&& ok "WeaselServer/WeaselServerApp.cpp" \\
|| miss "missing critical file: WeaselServer/WeaselServerApp.cpp"
test -f "WeaselIPC/WeaselClientImpl.cpp" \\
&& ok "WeaselIPC/WeaselClientImpl.cpp" \\
|| miss "missing critical file: WeaselIPC/WeaselClientImpl.cpp"
test -f "WeaselIPCServer/WeaselServerImpl.cpp" \\
&& ok "WeaselIPCServer/WeaselServerImpl.cpp" \\
|| miss "missing critical file: WeaselIPCServer/WeaselServerImpl.cpp"
test -f "WeaselTSF/Composition.cpp" \\
&& ok "WeaselTSF/Composition.cpp" \\
|| miss "missing critical file: WeaselTSF/Composition.cpp"
test -f "WeaselDeployer/WeaselDeployer.cpp" \\
&& ok "WeaselDeployer/WeaselDeployer.cpp" \\
|| miss "missing critical file: WeaselDeployer/WeaselDeployer.cpp"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 94 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~64d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/rime/weasel"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Weasel (小狼毫) is the Windows implementation of Rime, an open-source input method engine that enables Chinese character input via multiple schemes (Pinyin, Zhuyin, Cangjie, etc.). It provides a modern IME (Input Method Editor) for Windows 8.1–11 with real-time candidate selection, user dictionary management, and customizable UI themes. Multi-component C++ monorepo: RimeWithWeasel/ is the core IME service that interacts with librime; WeaselDeployer/ is the configuration UI (Qt-style dialogs); WeaselIPC/ handles inter-process communication with the input context via named pipes; each component compiles as a separate .exe with shared utilities. Build system uses xmake and NSIS for packaging.
👥Who it's for
Chinese language users on Windows who need a free, open-source, highly customizable input method; power users and linguists who want to tweak input schemes and dictionaries via YAML configuration; developers contributing to the Rime ecosystem who need a Windows-native reference implementation.
🌱Maturity & risk
Production-ready. The project has active CI/CD pipelines (GitHub Actions), follows semantic versioning with tagged releases, includes comprehensive unit tests via Google Test, and maintains regular commits. It is a stable, feature-complete IME used by thousands of Chinese-speaking Windows users.
Low risk for typical users, moderate risk for heavy customization: the codebase depends on librime and external libraries (Boost, OpenCC, LevelDB) whose compatibility may shift; the Windows-specific IME integration code (WeaselIPC, message pipes) requires careful testing across Windows versions; single maintainer @lotem may create delays, but the community is active on GitHub issues.
Active areas of work
Active maintenance includes CI workflow updates (update-appcast.yml), bug fixes reported via GitHub Issues, and ongoing Windows compatibility testing. Recent work appears focused on stability and Windows 11 support; WeaselDeployer UI refinements and IPC protocol robustness are typical ongoing areas.
🚀Get running
git clone https://github.com/rime/weasel.git
cd weasel
git submodule update --init --recursive
# Install xmake (https://xmake.io/)
# Install MSVC 2022 or later via Visual Studio
xmake build
# Output binaries in build/windows/x64/release/
Daily commands:
Development build: xmake build --mode=debug. Release build: xmake build --mode=release. Install locally: Run the generated NSIS installer (.exe) or use xmake's install target. Run service: WeaselServer.exe (started by installer/launcher); WeaselDeployer.exe for config UI. No external services needed; reads config from %AppData%\Rime.
🗺️Map of the codebase
WeaselServer/WeaselServerApp.cpp— Entry point for the Weasel server; orchestrates initialization of IPC, TSF, and system integration—every contributor must understand the server lifecycle.WeaselIPC/WeaselClientImpl.cpp— Core client-side IPC implementation that bridges TSF/input context with the Rime engine—essential for understanding request/response flow.WeaselIPCServer/WeaselServerImpl.cpp— Server-side IPC handler that processes input and returns composition/candidate data—critical for debugging IPC communication.WeaselTSF/Composition.cpp— Text Services Framework integration layer for composing and editing text—necessary for understanding Windows IME integration.WeaselDeployer/WeaselDeployer.cpp— Configuration UI and deployment tool; manages Rime engine startup and user settings persistence—needed to understand configuration flow.WeaselSetup/WeaselSetup.cpp— Installation and setup logic for registering Weasel as a Windows IME—required for understanding installation, registry integration, and upgrade paths.WeaselIPC/Configurator.cpp— Loads and parses Rime configuration; bridges user config files with runtime IME behavior—essential for feature customization.
🛠️How to make changes
Add a new input method or schema option
- Add schema definition to Rime user data folder (outside this repo, but referenced by WeaselIPC/Configurator.cpp). (
WeaselIPC/Configurator.cpp) - Update the switcher configuration in WeaselDeployer to expose schema selection. (
WeaselDeployer/SwitcherSettingsDialog.cpp) - Test by deploying and selecting the new schema in WeaselDeployer UI. (
WeaselDeployer/WeaselDeployer.cpp)
Add a new visual theme or candidate list style
- Define theme colors and fonts in theme YAML (user data folder), loaded by WeaselIPC/Configurator.cpp. (
WeaselIPC/Configurator.cpp) - Update WeaselIPC/Styler.cpp to parse and apply the new theme properties to candidate rendering. (
WeaselIPC/Styler.cpp) - Add theme preview/selection UI in WeaselDeployer/UIStyleSettingsDialog.cpp. (
WeaselDeployer/UIStyleSettingsDialog.cpp) - Verify rendering in WeaselTSF/CandidateList.cpp by building and testing live input. (
WeaselTSF/CandidateList.cpp)
Add a new IPC request or response type
- Define the request/response message structure and add serialization in WeaselIPC/Deserializer.cpp and WeaselIPCServer/WeaselServerImpl.cpp. (
WeaselIPCServer/WeaselServerImpl.cpp) - Implement the request handler in WeaselIPCServer/WeaselServerImpl.cpp to delegate to Rime engine. (
WeaselIPCServer/WeaselServerImpl.cpp) - Update WeaselIPC/WeaselClientImpl.cpp to send the new request type and parse the response. (
WeaselIPC/WeaselClientImpl.cpp) - Update WeaselIPC/ContextUpdater.cpp or WeaselTSF/Composition.cpp to consume the new response. (
WeaselIPC/ContextUpdater.cpp)
🔧Why these technologies
- Windows Text Services Framework (TSF) — Provides modern, Unicode-aware text input integration with Windows 8.1+; required for per-application context isolation and Unicode composition.
- Named pipes (WeaselIPC/PipeChannel.cpp) — Low-latency, secure local IPC between client-side TSF and background server; avoids network overhead and enables per-user DACL isolation.
- Rime input method engine (embedded) — Pluggable, rule-based composition engine supporting phonetic, shape-based, and mixed-mode input; allows offline operation and user customization via YAML.
- Visual C++ MFC & Win32 API — Direct Windows integration for TSF, system tray, and installer UI; required for low-latency rendering and system-level IME registration.
⚖️Trade-offs already made
-
Separate client (TSF) and server (Rime) processes via IPC
- Why: Isolates Rime engine crash from user input; allows shared Rime process across multiple applications; enables graceful restart without session loss.
- Consequence: Adds ~20–50ms latency per keystroke for IPC round-trip and serialization; requires careful pipe buffering and DACL management.
-
Stateful IPC protocol with per-session context
- Why: Rime engine maintains composition state; TSF client must track the same state to render candidates accurately and validate commits.
- Consequence: Client and server must stay synchronized; rollback/error recovery requires careful state reconciliation (see ContextUpdater.cpp).
-
User config files in %AppData%\Rime (YAML + Rime data)
- Why: Allows offline customization without reinstalling; users can version control and share configs; decouples application updates from user preferences.
- Consequence: Config parsing is non-trivial; invalid YAML or missing dictionary files can silently degrade input quality; requires 'Deploy' action to hot-reload.
-
System-wide server (one process per user session)
- Why: Reduces memory footprint and Rime engine startup cost vs. per-application servers; enables status icon and global hotkey switching.
- Consequence: Server crash affects all input fields; service isolation relies on Windows session boundaries; privilege escalation or COM deserialization bugs are high-impact.
🚫Non-goals (don't propose these)
- Cross-platform support (Windows 8.1+ only; use ibus-rime, fcitx5-rime, or 鼠鬚管 for other OS).
- Real-time collaborative editing or cloud-based dictionary sync.
- Graphical dictionary editor or corpus management UI (users edit YAML files directly).
- GPU-accelerated rendering (CPU-based GDI+ drawing is sufficient for 50–200 candidates).
- Speech input or handwriting recognition.
🪤Traps & gotchas
Windows version compatibility: IME code uses Win32 APIs that changed between Windows 8.1 and 11; test matrix must include multiple target versions. IPC protocol fragility: Pipe serialization format is tightly coupled between WeaselServer and UI clients; version mismatches cause silent failures. librime dependency: Weasel does not bundle librime binaries; build requires pre-built librime DLLs or custom librime compilation in parallel. User data path: Hardcoded to %AppData%\Rime; portable/roaming scenarios not well-supported. NSIS installer quirks: Requires admin privileges; registration to Windows language settings is fiddly. No built-in tests for UI dialogs: WeaselDeployer changes are manual-test-only.
🏗️Architecture
💡Concepts to learn
- Input Method Editor (IME) / Text Services Framework (TSF) — Weasel must integrate with Windows' TSF API to intercept keypresses and deliver candidates; core to understanding RimeWithWeasel.cpp
- Named Pipes (IPC) — Weasel uses Windows named pipes (PipeChannel.cpp) for client-server communication between IME service and UI; understanding serialization format is critical for extending protocol
- YAML-driven Configuration — User customization in Weasel is entirely YAML-based (parsed by yaml-cpp in Configurator.cpp); understanding RIME YAML schema is essential for feature additions
- Lexicon Trie (MARISA-trie) — Rime uses MARISA-trie for fast dictionary lookups; Weasel delegates this to librime but understanding the data structure helps optimize user dictionary updates
- Key-Value Store (LevelDB) — User dictionary and input history stored in LevelDB; Weasel interacts via librime's wrapper; affects persistence and performance tuning
- Serialization Protocol (Custom Binary Format) — WeaselIPC defines a binary protocol for context updates (candidates, preedit, cursor) serialized in Deserializer.cpp; protocol versioning issues cause obscure bugs
- OpenCC (Chinese Character Conversion) — Supports conversion between Simplified/Traditional Chinese on-the-fly; critical for users switching between locales; invoked during commit
🔗Related repos
rime/librime— Core Rime engine library that Weasel depends on; contains the input method logic and dictionary managementrime/home— Central Rime project hub with customization guides (Wiki) and community documentation referenced in Weasel READMErime/plum— Rime package manager for downloading and installing input schemes beyond those pre-packaged in Weaselrime/squirrel— Rime for macOS; parallel reference implementation for feature parity and cross-platform architectural decisions
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add unit tests for WeaselIPC serialization/deserialization pipeline
The WeaselIPC module contains critical serialization components (Deserializer.cpp, ResponseParser.cpp, Styler.cpp, ContextUpdater.cpp, ActionLoader.cpp) that handle inter-process communication. These are stability-critical but have no visible test coverage. Adding tests would catch regressions in IPC data marshaling and prevent subtle bugs in input method state synchronization.
- [ ] Create new test directory: WeaselIPC/tests/
- [ ] Add unit tests for Deserializer.cpp covering edge cases in response parsing
- [ ] Add unit tests for ResponseParser.cpp validating protocol message handling
- [ ] Add tests for Styler.cpp ensuring UI styling data is correctly serialized
- [ ] Reference existing .github/workflows/ci.yml to integrate tests into CI pipeline
Add missing documentation for WeaselDeployer configuration and UI customization
WeaselDeployer contains multiple configuration dialog components (UIStyleSettingsDialog.h, SwitcherSettingsDialog.h, DictManagementDialog.h, Configurator.h) but there is no developer documentation explaining the configuration schema, dialog workflows, or how to extend them. This blocks contributors from safely modifying or adding configuration options.
- [ ] Create WeaselDeployer/DEVELOPMENT.md documenting the Configurator architecture
- [ ] Document the configuration file format handled by Configurator.cpp with examples
- [ ] Add API documentation for UIStyleSettings.h explaining theme customization workflow
- [ ] Document the SwitcherSettingsDialog flow and state management
- [ ] Reference relevant sections in main README.md for discoverability
Refactor RimeWithWeasel.cpp module responsibilities into separate utility modules
RimeWithWeasel.cpp appears to be the main integration point between Rime engine and Weasel UI, but is likely handling multiple concerns (engine initialization, input handling, UI updates). Splitting this into focused modules (RimeEngineAdapter.cpp, InputProcessor.cpp, UIBridge.cpp) would improve testability and reduce maintenance burden. This refactoring opportunity is evident from WeaselUtility.cpp being a separate file, suggesting utilities are already being extracted.
- [ ] Analyze RimeWithWeasel.cpp to identify distinct concerns (engine lifecycle, key handling, context updates)
- [ ] Create RimeWithWeasel/RimeEngineAdapter.cpp to isolate Rime engine initialization and state management
- [ ] Extract input key processing logic into RimeWithWeasel/InputProcessor.cpp
- [ ] Move UI synchronization logic to RimeWithWeasel/UIBridge.cpp, coordinating with WeaselIPC
- [ ] Update RimeWithWeasel.vcxproj.filters to reflect new module structure
- [ ] Add corresponding unit tests for each new module in tests/ directory
🌿Good first issues
- Add unit tests for WeaselDeployer/Configurator.cpp's YAML parsing—currently untested; would catch schema migration bugs early
- Document the IPC protocol in PROTOCOL.md with examples of message serialization/deserialization (WeaselIPC/Deserializer.cpp)—no spec exists; makes contribution harder
- Add logging/telemetry to WeaselIPC/ActionLoader.cpp to track which input actions are most used—helps prioritize performance optimization
⭐Top contributors
Click to expand
Top contributors
- @fxliang — 84 commits
- @lotem — 5 commits
- @wzv5 — 2 commits
- @hwf1324 — 1 commits
- @caramel1205zh — 1 commits
📝Recent commits
Click to expand
Recent commits
93eec2d— fix(WeaselTFS): Always update UIElements. (hwf1324)022f749— fix(WeaselUI): hover will be triggered, when panel is first created with mouse on some candidate, event mouse not moved, (fxliang)9b2943c— fix(WeaselUI): candies.size() might larger then MAX_CANDIDATE_COUNT (fxliang)5ffbae1— fix(WeaselUI): fix clipboard memory issue (fxliang)b3d161e— fix(WeaselUI): hover is triggered when mouse not moved (fxliang)cbe3aab— perf(WeaselUI): GdiplusBlur with openmp (fxliang)193a9ad— fix(WeaselUI): mark metrics of fullscreenlayout (fxliang)7da19f2— ci: merge ci ymls (fxliang)bccdff6— fix(WeaselServer): WinSparkle will always open ReleaseNoteUrl in default browser, bundled it with patch (fxliang)74eb272— refactor(RimeWithWeasel): remove useless code for ime (fxliang)
🔒Security observations
- High · IPC Communication Security —
WeaselIPCServer/SecurityAttribute.cpp, WeaselIPCServer/SecurityAttribute.h, WeaselIPC/PipeChannel.cpp. WeaselIPC components handle inter-process communication between client and server. The SecurityAttribute.cpp file suggests custom security handling, but without reviewing the actual implementation, there's a risk of privilege escalation or unauthorized access between processes. Named pipes in Windows require proper DACL configuration. Fix: Verify that SecurityAttribute properly implements restrictive DACLs. Ensure only authorized users can connect to named pipes. Review PipeChannel.cpp for proper handle validation and impersonation checks. - High · Input Validation in Deployer Configuration —
WeaselDeployer/Configurator.cpp, WeaselDeployer/UIStyleSettings.cpp, WeaselDeployer/DictManagementDialog.cpp. WeaselDeployer handles user input for configuration settings (UIStyleSettings, SwitcherSettingsDialog, DictManagementDialog). Without visible input validation, there's risk of malicious configuration injection that could affect IME behavior or system stability. Fix: Implement strict input validation for all user-configurable parameters. Validate file paths, style settings, and dictionary data before processing. Use whitelisting for acceptable values. - High · Deserializer Untrusted Data Processing —
WeaselIPC/Deserializer.cpp, WeaselIPC/ResponseParser.cpp. WeaselIPC/Deserializer.cpp processes data from IPC channels without clearly visible sanitization. Deserialization of untrusted data can lead to arbitrary code execution or data corruption. Fix: Implement safe deserialization practices. Validate data structure and size limits before processing. Use a robust serialization format with schema validation. Consider using Protocol Buffers or similar typed serialization. - Medium · Dependency Management Clarity —
Root directory - missing dependency manifest. No visible package dependency file (package.json, requirements.txt, vcpkg.json, etc.) was found. The project depends on librime and other components, but dependency versions and security patch status are unclear. Fix: Create and maintain a dependency manifest file. Pin specific versions of all external dependencies. Implement automated dependency scanning for known vulnerabilities. Document all submodule dependencies in .gitmodules with version tags. - Medium · File Path Handling in Configuration —
WeaselDeployer/Configurator.cpp, WeaselIPC/Configurator.cpp, WeaselServer/WeaselServer.cpp. Configuration files stored in %AppData%\Rime are referenced throughout the codebase. Without proper path validation, there's potential for directory traversal attacks if user-supplied paths aren't sanitized. Fix: Implement path canonicalization and validation. Restrict configuration file access to the intended Rime directory. Use secure APIs for path operations. Validate all paths to prevent directory traversal. - Medium · DLL Loading and Module Import Risks —
RimeWithWeasel/, WeaselServer/, WeaselDeployer/. Windows applications loading DLLs are susceptible to DLL hijacking. No visible DLL search path hardening or manifest configuration found in the project structure. Fix: Use SetDllDirectory() to restrict DLL search paths. Load DLLs with full paths. Add proper manifests specifying required components. Implement DLL verification before loading. - Medium · Registry Access in Windows Environment —
WeaselDeployer/WeaselDeployer.cpp, WeaselServer/WeaselServerApp.cpp. WeaselDeployer and WeaselServer interact with Windows registry for configuration. Improper registry handling could allow unauthorized system configuration changes. Fix: Use least-privilege registry key access. Validate all registry values before use. Document all registry locations accessed. Implement proper error handling for registry operations. - Low · Build Configuration Security —
RimeWithWeasel/RimeWithWeasel.vcxproj, WeaselDeployer/WeaselDe. Multiple .vcxproj files present. Without visible security compiler flags (SafeSEH, DEP, CFG), the compiled binaries may lack modern exploit mitigations. Fix: undefined
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.