Thank you for considering a contribution. The two most useful things you can give the project today:
- Excel oracle data from your locale. This is what keeps Formulon's 1-bit compatibility claim honest. If you have Excel 365 running in a locale we don't yet cover (anything other than Mac ja-JP), one command donates a golden snapshot to the project — see Donating oracle data below.
- Bug reports, divergence reports, and code. Standard GitHub PR flow; see Code contributions at the bottom.
Oracle data must come from Excel 365 (Microsoft 365 subscription). Office 2019 and earlier are not supported and PRs containing goldens generated against them will not be merged.
Post-2019 functions — ARRAYTOTEXT, LAMBDA, and the entire
dynamic-array family (SORT, FILTER, UNIQUE, XLOOKUP, …) —
silently return #NAME? on Office 2019 with no warning. Generating
goldens on such an install would bake #NAME? into the JSON for every
one of those functions, which is the exact failure mode that produced
the now-deleted win-2019-ja_JP archive.
The oracle harness enforces this at the tool level: all three
generators (oracle-gen, oracle-gen-cf, oracle-gen-workbook) run a
startup sentinel that evaluates =ARRAYTOTEXT(1) and aborts with a
clear error message if Excel does not recognise it. You should never
hit this in practice, but it is your safety net if you accidentally
target the wrong install.
Formulon's 1-bit compatibility is anchored to a single primary oracle: Mac Excel 365 in ja-JP locale. Several Excel function families behave differently across locales and across the Mac/Windows ports, in ways no amount of code review can paper over:
- Locale-bound text functions —
BAHTTEXT(Thai),NUMBERSTRING(Chinese),TEXTwith localized format strings,DATEVALUEparsing of locale date strings, weekday/month name output. - System-locale-bound formatting — decimal separator (
.vs,), date order (mdy / dmy / ymd), currency symbols, list separator. - Mac vs Windows divergences — boundary date arithmetic near the
1900-02-29 phantom leap day, certain
[$-...]locale-tag fallbacks, and a handful of text-encoding and rounding edge cases.
The maintainer team can't reproduce these from a single install:
- Excel locale isn't a runtime switch. It's tied to system language + Office language pack + (on Windows) registry. Switching requires editing OS settings and restarting Excel — not automatable on a CI runner.
- Excel licenses are per-account / per-platform. The maintainer team has Mac Excel 365 ja-JP. Practically and legally, we cannot run "all locales" on one machine.
- Authenticity matters. A locale oracle generated by a contributor who actually uses Excel in that locale is more trustworthy than a one-shot reconfiguration. They notice when results look wrong; we wouldn't.
- Excel ships monthly updates. Each variant target needs periodic refresh. Distributing this across the community keeps any single update from blocking the project.
If Formulon ever ships a runtime compat_mode switch (Mac ↔ Windows,
locale A ↔ locale B), the goldens you contribute are exactly what
teaches the engine the right answer in each mode.
A target name has the shape <host>-<excel-major>-<locale>, with
<host> ∈ {mac, win} and <locale> in BCP 47 with _ instead of -.
The authoritative list is in
tools/oracle/targets.yaml. Each entry is
tagged with one of:
| status | meaning |
|---|---|
primary |
the 1-bit reference; maintained by the core team. |
scaffolded |
wired up; goldens may be partial or stale. |
wanted |
reserved slot, no goldens yet. These are exactly what we'd love to receive from you. |
Currently reserved as wanted:
mac-365-en_US,win-365-en_USwin-365-de_DE(decimal-comma locale)win-365-fr_FR(decimal-comma locale)win-365-zh_CN(NUMBERSTRING, localized format codes)win-365-ko_KRwin-365-th_TH(BAHTTEXT)
If your locale isn't on this list, see Adding a new target.
| Host | Excel | Tooling |
|---|---|---|
| macOS 12+ | Excel 365 (any locale) | Python 3.10+ available; make oracle-contribute will bootstrap a venv with xlwings. macOS will prompt for Automation permission the first time — grant it under System Settings → Privacy & Security → Automation → (your terminal) → Microsoft Excel. |
| Windows 10/11 | Excel 365 (any locale) | Python 3.10+ from winget install Python.Python.3.12; install xlwings pywin32 pyyaml in that interpreter. |
| WSL2 on Windows | Excel 365 on the Windows side | Same as Windows for the Windows-side Python. WSL2 Python only orchestrates and ferries JSON. Export FORMULON_WIN_PYTHON pointing at the Windows-side python.exe so your per-machine path never lands in targets.yaml (e.g. export FORMULON_WIN_PYTHON=/mnt/c/Users/<you>/AppData/Local/Programs/Python/Python312/python.exe). |
From a fresh checkout:
make oracle-contributeThat single command:
- Bootstraps a Python venv with
xlwings(only on first run). - Probes Excel briefly to read your installed version and locale.
- Maps your environment to a target name in
targets.yaml. If the target isn't tracked yet, it prints how to open an issue to reserve the slot. - Compares versions. If the repository's committed
ENVIRONMENT.mdfor that target was generated against an Excel build at or above yours, it just thanks you and exits. No PR is needed in that case — your contribution would be a no-op refresh. - Otherwise, runs preflight, generates the goldens (15–25 min for
the full suite), records
ENVIRONMENT.md, and prints exactgitcommands plus the GitHub PR URL.
If the auto-detection picks the wrong target (you have multi-language Excel installed, or the country code maps ambiguously), override it:
make oracle-contribute TARGET=mac-365-en_USThe locale Formulon cares about is Excel's, not the OS's. To check your Excel locale before running:
- Mac Excel: Excel → Preferences → General → Display Language. The setting must match what's in System Settings → Language & Region for the function results to be self-consistent.
- Windows Excel: File → Options → Language → Office display language.
If those don't agree, switch one and restart Excel before contributing.
After make oracle-contribute finishes successfully, it prints
something like:
Done. Your oracle data is on disk -- thank you for the donation!
Target: mac-365-en_US
Locale: en-US
Host: Darwin
Env file: tests/oracle/variants/mac-365-en_US/ENVIRONMENT.md
Files git considers new or changed:
tests/oracle/variants/mac-365-en_US/golden/...
tests/oracle/variants/mac-365-en_US/ENVIRONMENT.md
...
Next: push your branch and open a PR.
git checkout -b oracle/mac-365-en_US-2026-04-28
git add tests/oracle/variants/mac-365-en_US/
git add tests/oracle/variants/mac-365-en_US/ENVIRONMENT.md
git add tools/oracle/targets.yaml # bump status: wanted -> scaffolded
git commit -m 'test(oracle): contribute mac-365-en_US goldens'
git push -u origin oracle/mac-365-en_US-2026-04-28
Then open a PR (oracle template auto-loads):
https://github.com/libraz/formulon/compare/main...oracle/mac-365-en_US-2026-04-28?expand=1&template=oracle.md
Run those commands as printed. The PR description should include:
- Your Excel version as shown in Excel → About (paste verbatim).
- Your OS version (
sw_vers -productVersionon Mac,winveron Windows). - Confirmation that you ran on real Excel, not a modified driver, and not under a debugger that could have intercepted results.
- Anything you noticed during generation (warnings, cases that opened Excel dialogs, etc.).
When a target flips from status: wanted to its first real goldens, we
ask a second contributor with the same target to regenerate
independently and post a git diff against the first PR. Identical
output across two independent runs is what justifies labelling the
target scaffolded.
If you're the second verifier, run:
git fetch origin pull/<PR-NUMBER>/head:contrib-pr
git checkout contrib-pr
make oracle-contribute TARGET=<target-name>
git diffA clean diff (only generated_at timestamps and any explicit
divergences) is the signal we accept the target.
When Excel updates and your installed version is now ahead of the
repository, make oracle-contribute will regenerate automatically
(version comparison decides). The PR is small in that case —
typically ENVIRONMENT.md plus any goldens that actually moved. Call
out which results changed in the PR description so reviewers can
classify each: bug, ULP drift, or new spec interpretation.
If your locale isn't yet in targets.yaml, the contribute command
prints:
[contribute] derived target name: mac-365-pt_BR
[contribute] this target isn't tracked in tools/oracle/targets.yaml.
Two ways forward:
1) Open an issue so we can reserve the slot, then re-run:
https://github.com/libraz/formulon/issues/new?title=Oracle+target%3A+mac-365-pt_BR
2) Or pass --target=<existing-name> if you intend to map your
environment onto an already-tracked target.
Open the issue with:
- The derived target name as printed.
- Your Excel version and OS.
- A note about any locale-specific functions you expect divergences in
(e.g.,
pt_BRexercises decimal-comma TEXT and Brazilian date formats).
A maintainer will append a wanted entry to targets.yaml and merge.
You can then re-run make oracle-contribute to actually generate.
For bug fixes, new functions, parser improvements, etc.:
- Fork, branch off
main. - Read CLAUDE.md for the development standards
(
Expected<T, Error>, no exceptions, RAII, oracle-driven function work). - Run
make build && make test(fast suite, ~30s) before pushing. - For functions, add cases to
tests/oracle/cases/<category>.yamland regenerate the primary golden on a Mac (make oracle-gen SUITE=<category>). Reviewers will not merge an unimplemented function PR without oracle coverage. - Open a PR. The default template applies for code; oracle PRs use
?template=oracle.md(themake oracle-contributeoutput already embeds this).
Issues with a minimal reproducer are easiest to act on:
- The formula (or the file fragment) that misbehaves.
- The expected output — and how you obtained it (Mac Excel, Windows Excel, LibreOffice, ...).
- Formulon's observed output, plus the version of Formulon you're
on (
formulon_cli --versionor the@libraz/formulonpackage version). - For divergences from Excel, ideally a YAML case in the format under
tests/oracle/cases/so we can drop it straight into the suite.
By contributing, you agree your contributions are licensed under the project's Apache 2.0 license.