One static library and thin front ends: CLI, hdTransEngine.dll (C API), J2KEngine.dll
(J2K_* exports for hosts that load that DLL), WebAssembly. PCRE2 and libyaml are vendored
as sources in third_party/ (built by CMake, no network): PCRE2 for the regex rows of
the filter stage (src/core/filter), libyaml for optional settings (src/core/config).
README.md is for users; developer documentation is in docs/ (index: docs/README.md).
Working procedures are Claude Code skills in skills/ (skills/README.md); link them once per
checkout with mkdir -p .claude && ln -s ../skills .claude/skills.
- CMake presets (CMakePresets.json):
mac/linuxuse Ninja with clang or gcc;windows-x86/windows-x64use Visual Studio 2026 without a Developer prompt (CMake 4.2+).windows-x86-ninja/windows-x64-ninjaneed Ninja and a matching MSVC Developer prompt orvcvarsallenvironment. All native presets build the static/shared libraries, CLI and tests; J2KEngine.dll is Windows-only.tools/build.ps1selects installed Visual Studio 2022 or 2026. Visual Studio Release binaries are underbuild/<preset>/Release; Ninja binaries are directly underbuild/<preset>.wasmneeds Emscripten and Ninja:emcmake cmake --preset wasm. cmake --preset mac && cmake --build --preset mac --parallel && ctest --preset macis the development loop; substitute any native platform's preset formac.- CI uses Ninja with sccache for native builds and ccache for Emscripten's Clang calls; neither compiler cache is required by local presets.
- Unit tests use small local fixtures, not external expectation sets or dictionary totals.
tools/snapshot.shrecords built-CLI outputs;tools/compare.shreports baseline changes (docs/testing.md).- End-to-end test data (texts and their expected outputs) is kept outside this repository
and found through
HDTRANS_TESTDATA; without it the tests skip.tools/e2e.sh --expected "$HDTRANS_TESTDATA" CLI dictsreports changed lines and--acceptrewrites the expected outputs there; ctest runs it astestdata(docs/testing.md). - A translation behaviour change needs its own commit with the reason; once it is on
main, accept the new outputs in the test-data repository and commit them there. tools/detect-bad.shlists ill-formed Korean sequences in outputs; a pattern becomes a constraint rule (src/core/word/constraints.cpp) only when its checked hits are all errors.- C++20. Library and DLLs are built without exceptions and RTTI; errors are return values. Each DLL links the CRT statically and is a single file.
- Warnings are errors; fix the code, not the flags.
developis the default branch: work branches start from it and pull requests target it.mainreceives release promotions fromdevelopthrough PRs. After CI passes, the release bot commits version files and cuts release tags.- Commit and PR titles use Conventional Commits:
fix(scope): summary,feat(scope): summary, ordocs,perf,refactor,style,test,build,ci,chore,revert. Scope is optional. Breaking changes use!or aBREAKING CHANGE:footer, not amajor:type. - Squash ordinary PRs into
develop; mergedevelopintomainwithout squashing to preserve change history. The promotion PR title controls the release bump. After CI, the bot commits version files and CHANGELOG, then creates the release without another PR (docs/contributing.md). - A squash merge has the PR title as its subject and a short body of two to four lines: what changed, why, and whether expectation sets moved. No co-author or session trailers and no list of branch commits.
- Dates shown to users (release notes, CHANGELOG, the web page) are in KST (Asia/Seoul).
- docs/fixing-translations.md has the steps. Fix the stage where the error first appears (segmentation, analysis, selection, assembly), not its symptom downstream; count how many words share the cause first, and fix the mechanism (codes, rules over classes, engine code) when it spans many words. For the same cause, data (dictionary rows, codes) before rules, rules before engine code. Each fix gets a fix test and a keep test.
- Translation output follows the rules in spec/ (index: spec/README.md); fixes and reviews are checked against them.
- A change that contradicts a rule updates that rule and its cases in the same change.
- Judge behaviour changes against a build of a fixed commit in its own worktree, not against a moving main; revert or narrow a change behind a line both judges call worse.
- External user dictionaries (Ehnd) are never merged into dicts/.
- Run engines and ctest with
HOMEset to a fresh temporary folder when builds run in parallel; the dictionary cache is per user.
- Standard library only, with PCRE2 in src/core/filter and libyaml in src/core/config only. UTF-8
std::stringinternally. Shift-JIS and CP949 exist only at theJ2K_*boundary, converted by the OS; the engine has no code-page tables. - Types and functions
CamelCase, local variableslowerCamel, private members with a trailing underscore, filessnake_case, one pipeline stage per directory undersrc/core/. Internal functions go inhdTrans::internaland in headers undersrc/, never ininclude/. Identifiers starting with an underscore are not used. - Plain functions and structs; no abstraction layer unless two implementations exist.
- The library runs inside other people's processes: no global constructors with side
effects, no stdout/stderr, no
exit, no locale changes;Translateis callable from several threads at once on one loaded dictionary. - Inputs of any length are handled; no fixed-size buffers.
- Tests: table tests on real inputs with
tests/unit/hdtest.h; no framework, no mocks. Every stage has tests next to it.
- Comment only what a reader needs to use the code: non-obvious behaviour, invariants, data layouts. Short. Prefer no comment over a meta comment.
- Docs and comments say what is needed and stop: no restating tables in prose, no filler, no summaries of what was just said. In Korean text use commas or 와/과, not the middle dot.
- No justification or history comments ("kept because", "verified against", "used to be"); no references to other projects, agents, test results, machines, users or paths.
- One coherent change per commit; the message says what changed and whether the expectation sets moved.
src/core/dict/builtin/holds the builtin tables compiled into the library (cmake/builtin_tables.cmaketurns them into C++ at build time); edit the TSVs.- The rules program of the word stage is the
ctxcolumn of the dictionary rows that name a local rule plus the global rules insrc/core/rules/globals.txt(docs/rule-language.md). The program is dumped withhdtrans --dump-rules;src/core/rules/compiled_gen.cppis generated from that dump byhdrulec cppandprogram.hashbyhdrulec dump -hash(docs/rule-compiler.md). dicts/is the shipped dictionary set and is committed; each file starts with#! layer=...and a#description line (docs/dictionary-files.md).- Never commit build outputs, expectation sets, machine names, user names or absolute paths. Locations outside the repository come from environment variables with relative defaults.