feat(strategies): human labels under Moonbot's own identifiers, every field - #441
Merged
Conversation
The Strategies editor speaks Moonbot's internal language: every field row prints the raw identifier (SilentNoCharts), the sections list shows schema names (Filters / Price/Position), the tree's 57/57 (50) counts are unexplained, three surfaces repeat the same empty-state sentence, and the footer buttons read as sentence fragments. Add a fail-closed label table beside the existing tooltip table: a field with a known label shows the human name with the raw identifier as a muted subtitle (194 of 216 fields; the 22 nobody can name honestly keep the raw name), sections get translated titles with the schema name in a tooltip, the orders count in the tree gets its own tone, only the parameter pane keeps the empty-state sentence, and the footer buttons read "Запустить" / "Остановить" with "отмеченные стратегии" in the tooltip. Handlers are untouched. Claude-Session: https://claude.ai/code/session_01RaQ2tnAyr4pyiAiwGktPBv
A field row led with the Russian label and kept Moonbot's identifier as a caption, the reverse of what a trader reading the manual or a forum post needs, and 220 fields the label table had no arm for rendered as a bare identifier: the whole MoonShot, MoonStrike, Hook, Liq, VLite and Waves families among them. Every row now leads with the identifier and carries the human name as a caption under it (or in the tooltip in the fixed-pitch full view). The table gains a label for every field the local strategy replica has ever seen, in all three languages, and its help key becomes optional: a field known only from the replica has a name but no dependency note. The label-lookup contract reads every label token in the table and pins the new count. Claude-Session: https://claude.ai/code/session_01RaQ2tnAyr4pyiAiwGktPBv
guyverino
added a commit
that referenced
this pull request
Sep 6, 2026
rust-i18n builds its backend once, lazily, on the first t!() — and its generated initializer materialises all ~2600 keys in ONE stack frame. Whether that frame fits depends entirely on where the first lookup happens, which nothing was choosing deliberately: it landed deep inside chart-tab construction, the __chkstk probe entering the frame faulted, and the application died at startup with 0xC00000FD before opening a window. The threshold moved because the dictionary grew — this branch adds thirteen keys and #441 added the field-label table — but the trigger is the call site, not the count, so a dictionary this size will cross it again from anywhere. Touched at the base of the stack instead, the frame is built where there is room and every later t!() is a map lookup. Verified live: the same configuration that crashed on every launch now starts.
guyverino
added a commit
that referenced
this pull request
Sep 6, 2026
rust-i18n builds its backend once, lazily, on the first t!() — and its generated initializer materialises all ~2600 keys in ONE stack frame. Whether that frame fits depends entirely on where the first lookup happens, which nothing was choosing deliberately: it landed deep inside chart-tab construction, the __chkstk probe entering the frame faulted, and the application died at startup with 0xC00000FD before opening a window. The threshold moved because the dictionary grew — this branch adds thirteen keys and #441 added the field-label table — but the trigger is the call site, not the count, so a dictionary this size will cross it again from anywhere. Touched at the base of the stack instead, the frame is built where there is room and every later t!() is a map lookup. Verified live: the same configuration that crashed on every launch now starts.
guyverino
added a commit
that referenced
this pull request
Sep 6, 2026
…ers (#445) * feat(strategies): follow the core's strategy order, and keep its folders Two halves of the same story, both of them things moonproto only started synchronizing in 2e67562 and this terminal had no way to represent before. Strategy order. A core's strategy list is an arrangement the operator made in MoonBot, and the library now carries it as the row sequence of a Full snapshot. The tree used to throw it away, sorting folders into a byte-wise alphabet that put Zeta above alpha and every Cyrillic name after every Latin one. A folder now sits where its first strategy sits in the core's own list, taken from the unfiltered list so that typing in the search box cannot rearrange the tree. The operator can also change that order: two footer buttons, Ctrl+Shift+Up/Down, and two context-menu items move the selection inside its folder, and the result goes to the core as a complete id sequence. Alt+Up/Down was avoided on purpose — it ships as "shift the sell order's price", and one chord with two meanings is a trap. Until the core echoes the new order back, the tree draws the sequence that was sent; without that a second press would be computed from the arrangement the first one had already replaced. The same sequence is applied to every other outgoing strategy sync, or a checkbox toggled in the meantime would hand the core back the order the operator had just replaced. Folders. The core keeps a versioned folder tree now, empty folders included, so "new folder" is a real edit rather than a mark that lives until the window closes. Edits travel as intents — add this folder, move that subtree — and the complete desired tree is assembled on the feed thread, where the newest tree is known: the wire form deletes every folder the list omits, so a tree assembled from a window's snapshot turns a create into a silent delete of whatever arrived meanwhile. A rename sends the rewritten tree with the rows, or the emptied old path stays behind as a folder of its own. Two capability flags, not one. `supported` means the core keeps a tree and its empty folders can be drawn; `editable` means every path in that tree would also survive moonproto's validator. They differ on a real account: MoonBot allows a `/` inside a folder name, moonproto's own state then reports the split halves as parent folders, and its validator refuses those — so on such a core every folder edit would be refused whole. The terminal asks once and keeps its local marks instead of failing per command. An empty folder draws neither a caret that opens onto nothing nor a checkbox that covers no strategy, and its tooltip says whether the core keeps it. Known gaps, deliberate: a refused reorder or folder edit reaches only the log, so the arrangement on screen reverts without saying why; the analytics purge still deletes an emptied folder the legacy way; empty folders are hidden while a search is running; their order among themselves is by name, which the protocol leaves unsynchronized and MoonBot orders differently. * fix(diagnostics): record what crashed before capturing its backtrace Both handlers — the Rust panic hook and the native SEH filter — captured a backtrace first and wrote the record afterwards. Symbolizing goes through dbghelp, which is not thread-safe and cannot run on an exhausted stack, so when the capture itself faulted the process died inside the handler with nothing written at all: an empty panic.log, an empty app log, and a Windows crash record naming dbghelp.dll as the faulting module rather than anything of ours. That is not a hypothetical. A startup stack overflow presented exactly this way and was undiagnosable until the order was reversed; with the record written first, the same run reported `NATIVE CRASH: code=0xC00000FD` and named its address, which is the whole of what was needed. The backtrace is still attempted, appended as a second write — panic_log opens for append, so a crash between the two costs the backtrace and nothing else. * fix(startup): build the translation backend before the stack runs out rust-i18n builds its backend once, lazily, on the first t!() — and its generated initializer materialises all ~2600 keys in ONE stack frame. Whether that frame fits depends entirely on where the first lookup happens, which nothing was choosing deliberately: it landed deep inside chart-tab construction, the __chkstk probe entering the frame faulted, and the application died at startup with 0xC00000FD before opening a window. The threshold moved because the dictionary grew — this branch adds thirteen keys and #441 added the field-label table — but the trigger is the call site, not the count, so a dictionary this size will cross it again from anywhere. Touched at the base of the stack instead, the frame is built where there is room and every later t!() is a map lookup. Verified live: the same configuration that crashed on every launch now starts.
kirillDevPro
added a commit
that referenced
this pull request
Sep 7, 2026
…#447) The localized name under every section title and field identifier (#441) is useful to someone reading a strategy in their own language and noise to someone who thinks in Moonbot's identifiers and wants a denser pane. Both are real users of the same window, so the caption becomes a preference in the Strategies settings gear rather than a fixed part of the row. It ships ON: the captions arrived on by construction, so keeping them is what leaves an upgraded pane unchanged. Off, the sections pane keeps one line per section, the parameters header reads `Main` instead of `Main · Основные`, and a field row keeps only the name the core speaks — the help tooltip is unaffected, it never was a translation of the name. Persisted as one optional lenient key in layout.toml next to the other Strategies preferences, resolved through the same PrefRow table, so an absent key keeps the default and a malformed hand edit cannot discard the surrounding layout.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Strategy fields showed only Moonbot's identifiers and the Sections list only Russian names. Every field row now leads with the identifier and carries a human label under it in the user's language (all 414 fields the local replica has ever seen, ru/en/es); sections read
Main · Основныеin the heading and identifier-over-label in the list. The tree footer buttons are «Start»/«Stop» with the «checked strategies» qualifier in a tooltip so a narrow tree keeps its captions, and the57/57 (50)counters explain both numbers on hover.How to verify
MaxPingover «Макс. пинг»; unknown fields keep one line.Filters / Pingover «Фильтры / Пинг»; headingFilters / Ping · Фильтры / Пинг.cargo test -p moon-ui-gpui --test theme_contract— label table ↔ dictionary bijection at 414.https://claude.ai/code/session_01RaQ2tnAyr4yiwGktPBv