Local-first desktop tool for renaming selected documents. Erynva supports PDF, JPEG, PNG, and multipage TIFF files while preserving each file's original format. macOS is distributed from GitHub as a signed and notarized installer. Native Windows x64 and ARM64 builds are being prepared for Microsoft Store certification.
The tool reads each selected document, extracts useful fields, and renames the file safely. PDF text layers are used when available; PDFs and images otherwise use local OCR. Multipage TIFF files use the same bounded first/last/second-page recovery policy as multipage PDFs.
Date - Subject - Document type.pdf
Examples:
16-05-26 - John Smith - Service agreement.pdf
John Smith - Unknown.pdf
unknown - Invoice.pdf
This tool is designed to run locally.
- OCR is performed locally by the host operating system (Apple Vision on macOS and Windows OCR in the Windows package).
- Document extraction uses local Ollama.
- The configured local model is downloaded on first use; document content is not sent to Microsoft, GitHub, OpenAI, or another cloud AI service.
- No cloud APIs are used by the renamer.
- Real customer/company documents, debug files, OCR dumps, and build artifacts are ignored by git.
Regression tests must use fictional people, companies, and document details only.
Model choice, architecture boundaries, and longer-term cross-platform notes are
documented in docs/model_strategy.md and docs/architecture.md.
The normal workflow is through a Finder Automator Quick Action:
- Install
Erynva-Installer-Apple-Silicon-Intel.pkg. - Enable the Quick Action once:
- Right-click any supported document in Finder.
- Choose
Quick Actions > Customize…. - Tick
Erynva.
- Select one or more PDF, JPEG, PNG, or TIFF files in Finder.
- Run
Quick Actions > Erynva. - Wait for the Erynva progress window, then choose
Done. - If files need review, choose
Review unknownsto correct them immediately.
macOS commonly requires the one-time Customize… step for Automator Quick
Actions installed by a package rather than created manually in Automator.
Automator may still show its own small “Running Shell Script” status item, but
the app's native progress window is the reliable progress indicator.
Opening Erynva.app directly from /Applications shows a small
interface with a document drop zone. Drop supported files into the window or
choose them with the file picker; either way, the app renames the original
files in place and preserves their extensions.
On the first ordinary app launch, Erynva also shows a short Getting Started
tour covering document selection, automatic versus reviewed renaming, the
review screen, and the one-time Finder Quick Action setup. Choose
Getting Started… in the main window to replay the tour later. Finder Quick
Action launches keep processing immediately and never interrupt a batch with
the tour.
The app UI can also change which filename fields are used. It offers generic
presets such as General documents, Correspondence, Finance, Legal / case,
Property / site, Logistics, and Full detail. These presets are built from
industry-neutral fields:
- date
- sender
- recipient
- subject
- reference
- document type
- amount
- location
- status
The app uses the same renaming engine as the Finder Quick Action.
Erynva runs one rename or review batch at a time. If Erynva is used again while a batch is still active, the second request stops without changing its files and asks you to wait for the current batch to finish before trying again. It does not silently queue work. Files that would receive the same name are kept safe with a numbered suffix rather than replacing one another.
Review Before Renaming… is transactional: Erynva stages every correction,
shows the complete old-to-new filename batch, and changes nothing until the
final approval. Cancelling that preview leaves the files and local learning
unchanged. Automatic and Quick Action runs rename clear results directly, but
hold any flagged document at its original filename until it is reviewed.
The Filename Settings tab lists every available built-in and saved format with its field order. Selecting a row only previews the choice; Use Selected Format on macOS or Save filename format on Windows applies it. The separate Customize… editor changes included fields, ordering, separators, date style, and person-name order without cluttering the main settings view.
Successful rename batches are recorded in a bounded local journal containing filename paths, timestamps, and the workflow source—not document or OCR content. The main macOS and Windows windows expose Undo Rename for the most recent batch only. A successful undo consumes that action; Erynva never walks backward and offers an older batch. Advanced Settings also provides a newest/oldest sortable rename history grouped into collapsible local-calendar days, showing the original and renamed filenames from the latest 100 batches, including entries that were later undone. Undo preflights the whole batch and refuses to move anything if a renamed file is missing or an original filename has been reused. A blocked attempt changes nothing and remains retryable after the filename conflict is resolved.
The journal stays on the device and is not included in app-data exports. Filename history can be sensitive. Clear History permanently removes the local rename list and the records used by Undo Rename, but it never renames, moves, or deletes documents.
The Windows implementation preserves the same Python extraction, naming, review, and local-learning backend. Its platform-specific pieces are:
- a .NET 8 WPF shell for drag/drop, progress, and editable review;
Windows.Media.Ocrbehind the normalized structured-OCR adapter;- separate Microsoft Store MSIX packages for Windows x64 and ARM64, with supported-document associations and package identity;
- a native
IExplorerCommandentry named Rename with Erynva.
The Windows app shows its own Getting Started tour on the first ordinary app
launch. It explains drag/drop, Rename, Review / Edit, and how the installed
Rename with Erynva Explorer action is used. The main-window
Getting Started… button replays it at any time. Explorer action launches are
not delayed by onboarding.
Each Windows build must be produced and tested on its matching x64 or ARM64 host; PyInstaller is not a cross-compiler. The unsigned Store-upload artifacts target Windows 10 build 19041 or later on x64 and Windows 11 build 22000 or later on ARM64. Microsoft signs accepted packages and delivers subsequent Windows updates through the Store.
The ARM64 package is native except for pinned x64 Poppler subprocesses, which
run through Windows 11's x64 emulation. That mixed-architecture payload is an
explicit Windows App Certification Kit and Store-certification risk. ARM64
must not be published until certification accepts it; otherwise replace
Poppler with an ARM64-native equivalent or omit ARM64 from the Store submission.
See packaging/windows/README.md for the complete packaging and acceptance
contract. The self-signed setup EXE is retained for development testing only.
On macOS, settings are saved per user in:
~/Library/Application Support/Erynva/config.toml
On Windows, settings and learning data are saved under:
%APPDATA%\Erynva
Tutorial completion is versioned shell-only state. macOS stores it in the
Erynva app preferences and Windows stores it under
%LOCALAPPDATA%\Erynva; it is not part of learning data or app-data exports.
The Automator entry point is:
filesort_cli.py
When an automatic batch finds suspicious or unresolved enabled fields, the app holds those documents for review before changing their filenames and shows:
Renamed: 8
Needs review: 2
Errors: 0
Choose Review unknowns to correct each file. The app previews the current
document, then the correction prompt shows the fields detected from OCR/model
extraction before asking for edits. The prompt shows a separate input for each
enabled filename field. You can:
- choose
Saveto rename and save a correction; - choose
Skipto leave that file as-is; - choose
Exit reviewto stop reviewing the remaining files.
When you draw a box around a value, keep the box if it covers the correct, stable location and edit any OCR mistake in the field. This is especially useful for handwriting: the corrected value is authoritative, while the box only teaches Erynva where to look on similar documents. Remove or redraw the box when it covers the wrong area, includes unrelated text, or is not a useful reusable location.
If a box produces empty, invalid, or unusually noisy text, Erynva asks you to
correct the value and choose either Keep box and correct or
Remove box and correct. Both choices preserve your typed correction; only the
first saves the box location for future documents.
If Save is clicked while any enabled field still says unknown, the app
treats that as a skip and does not save a correction record.
Corrections are used to rename the reviewed files and are saved locally in:
~/Library/Application Support/Erynva/corrections.jsonl
That file may contain real OCR text and real document details. It is private local learning data and must not be committed to git. The app keeps the most recent 2000 correction records.
tests/regression_cases.json remains the curated, privacy-safe test suite. Add
to it only with fictional/anonymised examples that have been manually reviewed.
Coworker Macs should have:
- macOS with Apple Vision OCR support
- Ollama model:
granite4.1:3b(recommended default, approximately 2.1 GB)
The packaged app bundles Python, its Python dependencies, Poppler, Ollama, and the compiled Vision helper. Coworkers do not need Python, Conda, Homebrew, or Xcode Command Line Tools for normal use.
The packaged app starts its bundled Ollama runtime automatically. If
granite4.1:3b is missing, the app prompts once and downloads the model locally
while showing native progress. The app's model settings can also install and
select compact Qwen 1.7B, Qwen 4B choices, or the optional Granite 8B model
when the bundled runtime supports them. Compact models use less memory but may
be less accurate and require more review. Granite 8B is intended for powerful
computers with at least 16 GB memory and preferably 24 GB or more. Installed
Erynva models that are not selected can be removed there.
Edit config.toml:
[renamer]
dry_run = false
debug_mode = "off"
vision_dpi = 225
health_check = true
update_check = true
[output]
separator = " - "
date_format = "DD-MM-YY"
include_date = true
include_sender = false
include_name = true
include_type = true
include_recipient = false
include_reference = false
include_amount = false
include_location = false
include_status = false
name_order = "given-family"
[output.presets."General documents"]
id = "builtin-general"
fields = ["Date", "Subject", "Document type"]
separator = " - "
date_format = "DD-MM-YY"
name_order = "given-family"
[updates]
github_repo = "PeterStoney/osa-pdf-renamer"
[performance]
mode = "balanced"
timing_logs = false
batch_parallelism = "auto"
[ollama]
model = "granite4.1:3b"
url = "http://localhost:11434/api/generate"
timeout_seconds = 15
obsolete_models = []Useful options:
dry_run: calculate intended filenames without renaming.debug_mode:off,failures, orall.vision_dpi: optional balanced-mode scan rendering resolution override, clamped to 150–300.health_check: verify dependencies before processing.performance.mode:qualityuses 250 DPI,balancedusesvision_dpior 225 DPI, andfastuses 175 DPI.timing_logs: print per-stage timing lines to stderr for benchmarking.batch_parallelism:autouses up to 2 document preparation workers for multi-file batches,offkeeps sequential processing, or set a number capped at 2.include_date,include_sender,include_recipient,include_name,include_reference,include_type,include_amount,include_location,include_status: choose which extracted fields appear in output filenames.include_nameis the internal compatibility name for the generic Subject field: the person, organisation, property, claim, case, project, or other document subject.name_order:given-familyproducesJohn Smith;family-givenproducesSmith, John. This is applied when Erynva detects explicit given/first-name and surname/family-name components, including text extracted from review boxes.separator: text placed between enabled filename fields. It is saved per preset and may be a single space, punctuation, multiple characters, or Unicode text. Filesystem-reserved characters are replaced with visually equivalent full-width characters.date_format: per-preset date pattern containingDD,MM, and eitherYYorYYYYonce each. Examples includeDD.MM.YY,DDMMYY,DDMMYYYY,DD/MM/YY,MM/DD/YY,YYYY-MM-DD, andYYYYMMDD. Because/cannot occur literally in macOS or Windows filenames, it is displayed as the narrower division slash∕character.ollama.model: selected local model. Prefer changing it through the app so installation and runtime compatibility are checked first. Erynva raises a low configured request timeout to 60 seconds for its 8B choice.obsolete_models: retained for compatibility with older configurations. Current releases leave cleanup to the model settings screen.
The packaged coworker default is debug_mode = "off" for clean operation. Use
debug_mode = "failures" only when deliberately troubleshooting a problematic
document.
Double-clickable scripts live in scripts/:
health_check.command: checks local dependencies and the Ollama model.run_unit_tests.command: runs the lightweight unit-style test suite.run_regression.command: runs the privacy-safe synthetic regression suite against the configured local Ollama model.build_vision_helper.command: rebuilds the Swift Vision OCR helper.
These scripts are intended for setup/support rather than everyday use.
The packaging/ folder contains the PyInstaller scaffold for building
architecture-specific macOS apps:
dist/arm64/Erynva.app # macOS 14+, Apple Silicon
dist/x86_64/Erynva.app # macOS 14+, Intel
dist/legacy/arm64/Erynva.app # macOS 11.3–13, Apple Silicon
dist/legacy/x86_64/Erynva.app # macOS 11.3–13, Intel
The packaged app is intended for coworkers who do not have Python installed. It bundles the Python runtime, this project code, Poppler, Ollama, and helper tools. The configured local model is downloaded on first use if missing.
All five selectable Granite and Qwen choices are Apache-2.0-licensed model releases. Keep their exact tags, notices, benchmark evidence, and runtime compatibility gates under review before each release.
Build notes are in packaging/README.md.
The Finder Quick Action template is also in packaging/, ready to be included
in the eventual main installer.
The package builder creates a coworker-facing installer:
dist/Erynva Installer.pkg
Builds should use the clean Conda environment defined in
packaging/environment.yml, not the default Conda base environment.
Direct macOS installations use Sparkle. Developer ID releases use the stable
public feed, while tester-only ad-hoc betas use the separate
macos-beta-feed GitHub release asset. Microsoft Store owns Windows
installation and updates; Windows does not download a GitHub installer. Older
direct Mac builds retain the legacy updater only for the first Sparkle bridge
release.
Release process:
- Merge the feature and tests into
main. - Set the shared top-level
VERSIONto1.0.0for the first Store-capable release. - Create and push the matching
v1.0.0tag. - Let the Signed macOS release workflow build the signed/notarized macOS
installer, sign its final bytes for Sparkle, generate
appcast.xml, and create its unpublished GitHub draft. - Run the separate Microsoft Store package workflow for the same immutable tag to build the unsigned x64 and ARM64 Store-upload MSIX artifacts.
- Test and publish the macOS-only GitHub draft manually.
- Upload the unsigned Windows artifacts to the reserved Erynva product in Partner Center. Microsoft signs them after certification.
Draft releases are not offered as updates. The GitHub draft contains the exact
Erynva-Installer-Apple-Silicon-Intel.pkg, its signed appcast.xml, and
checksums; publishing it
makes the package and feed available together. Windows customers receive the
matching Store-signed MSIX and Store-managed updates. The supported floor is
macOS 11.3, Windows 10 build 19041 on x64, and Windows 11 build 22000 on ARM64.
Repository secrets, signing requirements, workflow behavior, and the complete
release checklist are documented in docs/releasing.md.
Until Apple Developer ID is available, the manual GitHub macOS Sparkle beta
workflow can build an ad-hoc signed, unnotarized Mac app and intentionally
unsigned installer from a mac-beta-vX.Y.Z-BUILD tag. It creates a tester-only
prerelease draft and still signs the final package with Erynva's independent
Sparkle Ed25519 key. macOS will require testers to approve the first install
through System Settings > Privacy & Security > Open Anyway. This beta path
does not weaken or impersonate the later Developer ID release.
Run the lightweight unit-style suite:
/opt/miniconda3/envs/filesort-build/bin/python -m pytestIf pytest is not installed yet, the compatibility wrapper can run the same
lightweight suite directly:
/opt/miniconda3/envs/filesort-build/bin/python tests/test_unit_scripts.pyThe full extraction regression suite uses the configured local Ollama model:
/opt/miniconda3/bin/python tests/run_regression.pyThe regression suite uses synthetic OCR text and requires the configured local
Ollama model by default. Use --allow-no-model only for offline smoke checks;
do not treat that as a full regression run. Do not add real names, real OCR
output, PDFs, or debug files to the test manifest.
filesort_cli.py Neutral CLI entry point
config.toml Local default settings
helpers/vision_ocr.swift macOS Vision OCR helper source
pdf_renamer/ Application package
tests/ Privacy-safe regression suite
scripts/ Coworker/setup helper scripts
helpers/ Helper source files used by the runtime
packaging/ PyInstaller macOS app packaging scaffold
Key package modules:
app.py: batch orchestration and Ollama shutdown.config.py: executable paths and model settings.extraction.py: deterministic rules, model prompt, and validation.fields.py: central filename field metadata.health.py: dependency checks and Vision helper rebuilds.models.py: shared result dataclasses.naming.py: filename formatting and duplicate handling.document_sources.py: supported-type registry and PDF/raster page adapters.ocr.py: embedded PDF text and normalized local OCR.workflow.py: processing and renaming one supported document.
The intended core/shell split is documented in docs/architecture.md.