Summary
Cross-frontends audit (Heide's "does my training file auto-update?" question) surfaced an engine-level design flaw: user training written by the engine is never read back unless a frontend passes the same directory as both data_dir and user_dir (only Dasher-Windows does, by accident).
The mismatch
- Startup load:
CNodeCreationManager trains from FileUtils::ScanFiles(&pn, alphabet->GetTrainingFile()) (src/DasherCore/NodeCreationManager.cpp:99-101), which walks only s_dataDirectory, recursively (src/DasherCore/FileUtils.cpp:68-71). A file found there counts as "user" text only if writable (FileUtils.cpp:115, IsFileWriteable).
- Adaptive append:
TrainSymbol → WriteTrainFileFull → WriteTrainFile → ResolveUserDataPath writes to s_userDataDirectory/training_<alphabet>.txt — root of the user dir, no training/ segment (src/DasherCore/FileUtils.cpp:134-159, called from src/DasherCore/AlphabetManager.cpp:286,672).
- Import:
dasher_import_training_text (src/CAPI.cpp:2087-2101) trains the live model via a temp file and never touches the persistent training file — so imported text evaporates on the next launch unless the frontend appends it itself (three frontends do, but see below).
dasher_create deliberately keeps the two dirs distinct (src/CAPI.cpp:888-893), which is right for bundled corpora — but nothing ever bridges the user's training back into the scan.
Consequences (filed per repo)
| Frontend |
data_dir |
user_dir |
Typed learning survives restart? |
Import survives restart? |
| Dasher-Windows |
%APPDATA%\Dasher |
same |
yes (accidentally) |
no (frontend calls CAPI import only) |
| Dasher-GTK |
cwd-relative Data |
~/.local/share/dasher |
no |
n/a (no UI) |
| Dasher-Apple (iOS/macOS/visionOS/Keyboard) |
read-only bundle |
App Group container |
no |
file written by frontend, never loaded |
| Dasher-Android |
extracted filesDir/dasher |
filesDir/dasher_user |
no |
file written by frontend, never loaded |
Additionally, three frontends manage training under <user_dir>/training/ while the engine appends to <user_dir>/ root — two divergent files, and the UIs (export/reset/size) read the one the engine never writes.
Suggested direction
- Startup: after scanning
s_dataDirectory, also scan s_userDataDirectory for the training filename (user copy wins / is parsed in addition), OR ResolveUserDataPath + scan agree on one canonical location (<user_dir>/training/) and the engine writes there.
- Decide whether import persistence belongs in the engine (e.g.
dasher_import_training_text(ctx, text, bool persist) or a new dasher_append_training_text) instead of each frontend re-implementing it — currently Apple appends, Android appends, Windows doesn't.
- Expose
dasher_get_training_path() (or equivalent) so frontends stop hard-coding/deriving the path and the export/reset/size UI reads the same file the engine writes.
- This is also the foundation for any future cross-device training sync (governance RFC 0019 discussion) — sync can't be built on files that are never loaded.
Context: flagged during the training-file review of 2026-09-08; cross-refs in dasher-project/Dasher-Apple, Dasher-Android, Dasher-Windows, Dasher-GTK issues.
Summary
Cross-frontends audit (Heide's "does my training file auto-update?" question) surfaced an engine-level design flaw: user training written by the engine is never read back unless a frontend passes the same directory as both
data_diranduser_dir(only Dasher-Windows does, by accident).The mismatch
CNodeCreationManagertrains fromFileUtils::ScanFiles(&pn, alphabet->GetTrainingFile())(src/DasherCore/NodeCreationManager.cpp:99-101), which walks onlys_dataDirectory, recursively (src/DasherCore/FileUtils.cpp:68-71). A file found there counts as "user" text only if writable (FileUtils.cpp:115,IsFileWriteable).TrainSymbol→WriteTrainFileFull→WriteTrainFile→ResolveUserDataPathwrites tos_userDataDirectory/training_<alphabet>.txt— root of the user dir, notraining/segment (src/DasherCore/FileUtils.cpp:134-159, called fromsrc/DasherCore/AlphabetManager.cpp:286,672).dasher_import_training_text(src/CAPI.cpp:2087-2101) trains the live model via a temp file and never touches the persistent training file — so imported text evaporates on the next launch unless the frontend appends it itself (three frontends do, but see below).dasher_createdeliberately keeps the two dirs distinct (src/CAPI.cpp:888-893), which is right for bundled corpora — but nothing ever bridges the user's training back into the scan.Consequences (filed per repo)
%APPDATA%\DasherData~/.local/share/dasherfilesDir/dasherfilesDir/dasher_userAdditionally, three frontends manage training under
<user_dir>/training/while the engine appends to<user_dir>/root — two divergent files, and the UIs (export/reset/size) read the one the engine never writes.Suggested direction
s_dataDirectory, also scans_userDataDirectoryfor the training filename (user copy wins / is parsed in addition), ORResolveUserDataPath+ scan agree on one canonical location (<user_dir>/training/) and the engine writes there.dasher_import_training_text(ctx, text, bool persist)or a newdasher_append_training_text) instead of each frontend re-implementing it — currently Apple appends, Android appends, Windows doesn't.dasher_get_training_path()(or equivalent) so frontends stop hard-coding/deriving the path and the export/reset/size UI reads the same file the engine writes.Context: flagged during the training-file review of 2026-09-08; cross-refs in dasher-project/Dasher-Apple, Dasher-Android, Dasher-Windows, Dasher-GTK issues.