The script runs under 'set -o pipefail'. grep -q exits at the first match, which
SIGPIPEs the strings feeding it, so the pipeline reported failure and the gate
declared every image untagged — on its first real run, against images that
carried the tag correctly. Count instead of short-circuiting.
The build-date change in the previous commit was precautionary (it matches the
convention the meshcomod release flow already uses); I have no evidence the
spaces were actually breaking the flags, and should not have called it the fix.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PLATFORMIO_BUILD_FLAGS is whitespace-split, so '10 Aug 2026' tore the string
apart and every -D after it was dropped — including FIRMWARE_RELEASE_TAG, which
is precisely the untagged-build failure the new gate exists to catch. The gate
caught it on the first real run and stopped beta_61 before anything published.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The vendored core is gitignored and fetch-deps rm -rf's it, so a hand-edit
there dies with no history. That cost us the esp-nimble-cpp 2.x port and then
the P4's polled receive. Both live in the core now; fetch-deps verifies they
survived the copy and fails loudly if not.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The firmware asked for these keys and no .lang file carried them, so they
rendered English in all 13 languages with nothing to show a translator that
they were missing. Mostly strings that shipped with a feature whose PR did not
touch the language files: the SD arbitration and migration warnings, the
Wi-Fi/BLE coexistence messages, the keyboard-navigation vocabulary
(Up/Down/Left/Right/Select/Scroll) and section names (General, Clock & time,
Sensors, Screen, Home, Messages, Keys, Compact, Snake).
They go in with an EMPTY translation, which is the format's own way of saying
'not translated yet': the runtime loader requires a non-empty value
(`if (*p && tab[1])`) and gen-lang-builtin.py requires `if k and v`, so both
skip these rows and the English falls through exactly as before. Confirmed:
i18n_builtin.h regenerates byte-identical. No `# ver:` bump for the same
reason — device behaviour is unchanged, so there is nothing to re-download.
Also fixes the audit's own row parser to match the loader: a line is a comment
only when it has NO tab. Testing for a leading '#' hid every key starting with
LVGL recolor markup ("#7A7F87 Wardrive: …#"), the exact class the loader has a
comment warning about.
Every language is now at 0 missing keys.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nothing checked this. The .lang files are hand-maintained and i18n_builtin.h is
generated FROM them, so drift is invisible in both directions: a row whose
English no longer appears in any TR() call is translated for nothing, and a
TR() key with no row renders English in every language forever.
Matching mirrors TR() in i18n.cpp, which is the part a grep gets wrong —
adjacent C literals concatenate, LV_SYMBOL_* icon prefixes are stripped before
lookup, non-ASCII is often written as \x escapes, and menu/tab labels reach
TR() through a table field rather than as a literal (resolved by harvesting the
table's initialiser).
Current state: 910 TR() keys; every language carries ~57 dead rows and is
missing ~53 keys (hu: 77 dead, 52 missing).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Core (core-v1.17.1, meshcomod 5406093): 1.17 reserved MAX_ANON_CONTACTS slots
at the head of contacts[], which broke two things in beta_60 —
- resetContacts() claimed those slots while the lazily-allocated PSRAM table was
still NULL, so the new bootstrapRTCfromContacts() NULL-deref'd at boot on any
device whose contact store loaded nothing: fresh install, erase-flash, or SD
not mounted yet (#249). Boards with saved contacts booted fine, which is how
it passed bench testing.
- getContactByIdx() stayed raw while getNumContacts() excludes the reserved
slots, so every pairing of the two — contact list, action sheets, phone-app
sync, getContactForSave — read empty slots and could not reach the newest 8
real contacts. A just-added contact was invisible and its action-sheet
operations resolved to a blank slot (#252).
Fork side:
- loadContacts() skips blank records, clearing the placeholder contacts beta_60
wrote into the contacts file (the next save drops them permanently).
- Chat threads follow a peer's rename. The thread is matched by key but its name
was never updated, while inbound messages are filed by sender name — so the
first message after a rename created a duplicate thread (#252).
- release.sh derives FIRMWARE_VERSION / build date / core version from the tag
and the pinned core, keeps the in-tree dev default in step, and ABORTS the
release if any staged image does not embed its own tag. beta_60 shipped
reporting v1.16.0-touch because that value was hand-maintained.
All 8 S3 envs build green on core-v1.17.1.
Reported-by: Pierre747, rustinmyeye, myshoeisonfire
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
startFrame()'s default background became UIColor::window_bkg in the 1.17
core, and boards whose DISPLAY_CLASS is a core driver link upstream's
palette — window_bkg=WHITE, primary_txt=BLACK. The boot splash grew a
white frame around the logo, and the radio-halt text would have drawn
black-on-black. Boot splash, halt screen, REMOTE placeholder and the
shutdown screen now pass explicit black/white/red instead of palette
entries whose values depend on which display driver got linked.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The core moved to 1.17 (core-v1.17.0 pin) but the About string on dev
flashes still said v1.16.0-touch — release builds override it via
release.sh, only the #ifndef default was stale.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 8 S3 envs rebuilt green against the GitHub tag; the tag's tree is the
PR #46 merge commit (609c903) minus prebuilt/ — identical content to the
local test pin the branch was validated on.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TanmatsuDisplay.h / RM69A10Display.h / HI8561Display.h still had the 1.16
Color signatures (missed in the first sweep); pagers additionally hand the
core lib scope the TFT_eSPI include path (target.h -> ST7796LCDDisplay.h).
All 8 S3 envs + the Tanmatsu now build green on the 1.17 test pin.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- UIColor migration: DisplayDriver::Color enum -> ColorVal/UIColor statics in the
6 driver headers + 3 driver .cpps + all main.cpp/UITask.cpp call sites.
- ONE palette definition: src/helpers/ui/UIColorPalette.cpp. Envs whose
DISPLAY_CLASS is a core driver (v4_tft, attaky, tdeck, M9) take the palette
the core driver .o carries; envs on our drivers (r8, rak via src_filter;
pagers via helpers/ui glob; both IDF apps via the whole-archive src glob)
compile ours. Never both — same-name core objects collide (core LGFXDisplay).
- 1.17 ESP32Board.cpp includes <target.h>: hand the core lib scope the libdeps
include paths it can't chain to (MicroNMEA everywhere, SensorLib on pagers).
- CustomLR1121Wrapper: setRxBoostedGainMode void->bool (1.17 base).
- IDF: fetch-deps.sh gains durable vendor-time patches (ESP32Board minimal
deep-sleep — no target.h on IDF; SerialBLEInterface esp-nimble-cpp 2.x port
incl. esp_mac.h). helpers/ethernet/ excluded in both meshcore CMakeLists.
- platformio.ini pins point at a LOCAL test tag (mc117-clean#core-v1.17.0-test);
swap to the real core-v1.17.0 before merging to main.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Audited every non-ASCII codepoint in the v9 Hungarian file against the
extras-font ranges: full coverage (incl. Hungarian ő/ű, Latin Extended-A)
except a single „ low opening quote used once — swapped for the covered “
rather than regenerating three fonts for one glyph. Add U+201E to the
extras --symbols on the next font regen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hu.lang: 43 improved translations + 21 newly covered strings from pisti87,
applied as ver 9; builtin table regenerated.
Lang store (#247): the update check treated ANY version difference as
'update available' — a side-loaded NEWER file than the catalog got
DOWNGRADED on Update. Now numeric: offer only when the catalog is newer
(non-numeric versions keep the old inequality).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The DISCOVER_RESP wire format carries no position, so distance is shown when
the node is also a contact with a known advert position AND we have a GPS
fix — formatted with the existing km/miles badge. Rows without both fixes
are unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ø/Ø and å/Å were already in the o/a accent sets; æ was the missing third.
Long-press a/A now offers it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#185: file rows in the Files app now append the modified time (dd Mon HH:MM)
when the filesystem provides a sane one (FAT 1980-epoch / clockless writes
are omitted rather than shown as garbage).
#178: the core's overwrite-oldest eviction already skips contacts with flags
bit 0 set — but the device-side favorite star lived only in TouchPrefs,
invisible to the core, so a starred contact was still evictable when the
table filled. The star toggle now mirrors into the contact table's flags
(uiSetContactFavorite), and a one-shot boot sync stamps the flag for
contacts starred before this fix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The doc claimed the HI8561 TFT's backlight is panel-internal (DCS 0x51 only);
in reality it's a real LED on GPIO 51 driven by LEDC PWM, which the code has
done since ~beta_53. Stale doc line flagged by bmorcelli.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A dead or absent LoRa module halted boot behind the frozen logo with the only
clue on serial. Retry init once (transient SPI/reset flakes), then paint the
failure on the panel — 'LoRa radio not detected / wadamesh needs the LoRa
module' — before halting, so users aren't left staring at the wordmark.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The composer's send button calls composerSend() and then refreshes the open
detail itself. The two programmatic resend paths (long-press menu Resend and
the failed-bubble one-tap retry) called composerSend() without the refresh,
so the new bubble only appeared once something else redrew the thread.
Mirror the send button: refresh the open dm/ch detail + dirty the thread
list after a successful resend.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Boot-time normalize/repair setters re-put unchanged values every boot; each
one dirtied the shared file cache and bought a full touch.kv A/B snapshot.
On the Pager's internal SPIFFS that is a 0.7-1.5 s flash burst that stalls
both cores (flash-cache suspend -> the reported ui:lvgl/ui:gps stalls) and
widens the interrupted-write window that can drop a user's saved change.
Same-value puts now return success without dirtying, in both the file-mode
and legacy paths. The storage:SPIFFS-despite-SD half of #246 (profile
demotion on upgrade) was fixed separately by #206's 4778347.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>