From 8bc7125f97da72eea60dbc49bab28496bfdfcfc4 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 7 Sep 2026 21:25:44 -0400 Subject: [PATCH] docs(#212): the E4 session record, the proofs, and the facts CLAUDE.md got wrong docs/e4-session-2026-09-07.md: the owner's standing grant verbatim and the agent's confirmed reading of it; every band with its PR and merge SHA and whether it merged under the grant; each decision made under the grant with its reasoning and where it is recorded; what was deliberately set aside for the owner (hardware verification, #264/#265/#212, dead state, #99, the parallel-agent rule); the two process faults; where the evidence is and how to re-check a band; what the session superseded. docs/e4-proofs/: the verbatim verify_extraction.py output for every proven extraction (bands 2, 3a-3e, 4 units 1-3) with a README saying what a PASS means and what is not included. Until now these lived only in a session scratchpad. CLAUDE.md: the project is no longer a single ~8,700-line .ino; the layout table lists the units; the credential placeholders are in src/web.cpp; the parallel-agent rule keeps its policy but its premise now says the split happened and that relaxing it is the owner's call. Docs only. Nothing here changes code. --- CLAUDE.md | 18 +- docs/e4-proofs/README.md | 21 ++ docs/e4-proofs/verify-263-band2-logging.txt | 82 ++++++ .../e4-proofs/verify-265a-band4-lvgl_port.txt | 65 +++++ .../verify-265b-band4-oled_screens.txt | 27 ++ .../verify-265c-band4-screens-navigation.txt | 253 ++++++++++++++++++ .../verify-270-band3a-fram-settings-rtc.txt | 86 ++++++ .../verify-271-band3b-gps-imu-env-battery.txt | 136 ++++++++++ .../verify-272-band3c-weather-geocache.txt | 87 ++++++ ...-273-band3d-oled-display-uistate-touch.txt | 67 +++++ docs/e4-proofs/verify-274-band3e-web.txt | 88 ++++++ docs/e4-session-2026-09-07.md | 94 +++++++ 12 files changed, 1015 insertions(+), 9 deletions(-) create mode 100644 docs/e4-proofs/README.md create mode 100644 docs/e4-proofs/verify-263-band2-logging.txt create mode 100644 docs/e4-proofs/verify-265a-band4-lvgl_port.txt create mode 100644 docs/e4-proofs/verify-265b-band4-oled_screens.txt create mode 100644 docs/e4-proofs/verify-265c-band4-screens-navigation.txt create mode 100644 docs/e4-proofs/verify-270-band3a-fram-settings-rtc.txt create mode 100644 docs/e4-proofs/verify-271-band3b-gps-imu-env-battery.txt create mode 100644 docs/e4-proofs/verify-272-band3c-weather-geocache.txt create mode 100644 docs/e4-proofs/verify-273-band3d-oled-display-uistate-touch.txt create mode 100644 docs/e4-proofs/verify-274-band3e-web.txt create mode 100644 docs/e4-session-2026-09-07.md diff --git a/CLAUDE.md b/CLAUDE.md index c44b271..e4bb083 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,7 +29,7 @@ physical press). | Organization | Strycher (personal) | | Repository | https://github.com/Strycher/Field_Compass | | Archive | https://github.com/Strycher/Field_Compass-archive — private, read-only (#245) | -| Project Type | Embedded firmware (single-file Arduino `.ino`) | +| Project Type | Embedded firmware (PlatformIO; an Arduino `.ino` residue plus C++ translation units under `src/`, since E4 #212) | | GitHub Project | Field Compass Backlog (#3) | ### This repository was rebuilt clean on 2026-09-07 (#245) @@ -46,11 +46,11 @@ consequences that matter day to day: and in Citadel's `external_issue_number` still resolves. Numbers that were pull requests exist as **closed placeholders** pointing at the archive — the PRs themselves could not be recreated. See #245 for the list. -- **`src/src.ino` holds placeholder credentials** (`REDACTED_SSID_1` and friends). A - build from `main` compiles and **cannot join WiFi**, which also means no - `fieldcompass.local` diagnostics. Until #99 moves credentials to runtime storage, - flashing needs a local uncommitted edit — and nothing in `.gitignore` protects such - an edit from being committed by accident. +- **`src/web.cpp` holds placeholder credentials** (`REDACTED_SSID_1` and friends; they + were in `src.ino` until E4 band 3e, #274). A build from `main` compiles and **cannot + join WiFi**, which also means no `fieldcompass.local` diagnostics. Until #99 moves + credentials to runtime storage, flashing needs a local uncommitted edit — and nothing + in `.gitignore` protects such an edit from being committed by accident. Pre-migration commit SHAs quoted in old issues refer to the archive, not here. @@ -64,7 +64,7 @@ Pre-migration commit SHAs quoted in old issues refer to the archive, not here. | CITADEL_PROJECT | `Field_Compass` | | GITHUB_PROJECT_ID | `PVT_kwHODGcOBc4BOJgD` | | INFRA_PROFILE | Maker | -| Main Source File | `src/src.ino` (~8,700 lines) — named for the directory, see Build & Flash | +| Main Source File | `src/src.ino` — `setup`, `loop`, `initLVGL`, the buttons; ~4,400 lines on `main` after band 3 of E4, ~750 once #280 (band 4) merges. Named for the directory, see Build & Flash. The rest of the firmware is one unit per domain under `src/`, see Repository Layout | ## Hardware Specifications @@ -95,7 +95,7 @@ Standard PlatformIO layout. There is no `Field_Compass/` sketch directory any mo | Path | Contents | |------|----------| -| `src/` | `src.ino` (the firmware) + `lv_psram_alloc.c` (LVGL PSRAM allocator, #164) | +| `src/` | `src.ino` (`setup`, `loop`, `initLVGL`, buttons) + one unit per domain, each a `.h`/`.cpp` pair: `logging`, `fram`, `settings`, `rtc`, `gps`, `imu`, `env`, `battery`, `weather`, `geocache`, `oled`, `display`, `ui_state`, `touch`, `web`, `geo`, `ui_widgets` (and, once #280 merges, `lvgl_port`, `oled_screens`, `screen_*`, `navigation`); headers `fc_config.h` (pins), `fc_theme.h`, `fc_version.h` (`FW_VERSION`); `lv_psram_alloc.c` (LVGL PSRAM allocator, #164). Headers live in `src/`, not `include/`, because arduino-cli does not read `-I include` (#161). The record of how it got this way: `docs/god-object-inventory.md`, `docs/e4-session-2026-09-07.md` | | `include/` | `lv_conf.h` — vendored, #158. Reached via `-I include` in `build_flags` | | `lib/` | Vendored libraries. Empty; everything is pinned in `lib_deps` | | `partitions/` | Flash layout CSV | @@ -228,7 +228,7 @@ unconditional and never skipped. ### Single-File Source ⇒ Serialize Parallel Agents (MANDATORY) -Field Compass has a single ~8,000-line source file. **Parallel agents virtually guarantee merge conflicts, even with worktrees.** +Field Compass was a single ~8,000-line source file until E4 (#212) split it into units; `src/src.ino` is now the residue and the code lives in one file per domain. The premise of this rule is gone, but **the rule stands until the owner relaxes it** — it was written for the single-file era and the owner decides whether the per-issue branch override (above) and this serialisation survive the split. At session start, after preflight passes, check `dw --project Field_Compass list --status in_progress`. If ANY task is already claimed: diff --git a/docs/e4-proofs/README.md b/docs/e4-proofs/README.md new file mode 100644 index 0000000..30967f9 --- /dev/null +++ b/docs/e4-proofs/README.md @@ -0,0 +1,21 @@ +# E4 proofs + +One file per extraction, the verbatim output of `scripts/verify_extraction.py` at the time the band was committed. The header line of each file says which commits were compared. These are the evidence behind the "Measured" blocks of [`../god-object-inventory.md`](../god-object-inventory.md) §9–§15; the decision log is [`../e4-session-2026-09-07.md`](../e4-session-2026-09-07.md). + +| file | band | issue / PR | merge | +|---|---|---|---| +| `verify-263-band2-logging.txt` | 2 | #263 / #269 | `4cc1f21` | +| `verify-270-band3a-fram-settings-rtc.txt` | 3a | #270 / #275 | `43e569a` | +| `verify-271-band3b-gps-imu-env-battery.txt` | 3b | #271 / #276 | `e6465bb` | +| `verify-272-band3c-weather-geocache.txt` | 3c | #272 / #277 | `0053f77` | +| `verify-273-band3d-oled-display-uistate-touch.txt` | 3d | #273 / #278 | `7bfefa7` | +| `verify-274-band3e-web.txt` | 3e | #274 / #279 | `ae514ed` | +| `verify-265a-band4-lvgl_port.txt` | 4, unit 1 | #265 / #280 | commit `9ce4ddb`, PR open | +| `verify-265b-band4-oled_screens.txt` | 4, unit 2 | #265 / #280 | commit `6ec9fe6`, PR open | +| `verify-265c-band4-screens-navigation.txt` | 4, unit 3 | #265 / #280 | commit `b6f2261`, PR open | + +Bands 1 and E4-1/E4-2 (#260–#262) predate the script; their proofs were done by hand with the same `nm` method and are written up in inventory §8. + +**What a PASS means.** The union of `nm --print-size --defined-only` over every object under `src/` has the same symbol set before and after, matched by demangled name (a linkage change or a deliberately changed signature is listed and must have been declared); every function whose size changed is attributed by comparing its reference set — calls, external and static variables — from the relocation graph, with the only tolerated differences being the ones a move produces; from band 3c on, every moved function's reference set is compared as well; and the linked output sections' deltas are printed so growth outside function bodies is visible. Anything else fails the script. + +**What is not here.** The `nm` captures, reference-graph JSON, ELF and map files each proof consumed were session artefacts and are not committed; they are reproducible from the commits named in each header (build the "before" commit, capture, build the "after" commit, run the script with the `--moved` / `--published` lists in that band's commit message). The script's docstring is the usage. diff --git a/docs/e4-proofs/verify-263-band2-logging.txt b/docs/e4-proofs/verify-263-band2-logging.txt new file mode 100644 index 0000000..14efb83 --- /dev/null +++ b/docs/e4-proofs/verify-263-band2-logging.txt @@ -0,0 +1,82 @@ +scripts/verify_extraction.py -- band 2 (#263, PR #269, merge 4cc1f21): logging +before = e8a404d rebuilt (band-1 result), after = the PR head. Verbatim output. + +1. symbols: 572 before, 573 after (union of 4 objects) + relocated: 31 + src -> logging fs::File::File(std::shared_ptr) + src -> logging fs::File::~File() + src -> logging lastLogFlush + src -> logging lastLogRotation + src -> logging logPrint(char const*) + src -> logging logPrintf(char const*, ...) + src -> logging logPrintln(char const*) + src -> logging logTimestamp() + src -> logging logTimestamp()::tsBuf + src -> logging recordSDError(SDErrorType) + src -> logging recordSDSuccess() + src -> logging sdAvailable + src -> logging sdHealth + src -> logging sdOpenSafe(char const*, char const*, bool) + src -> logging serialLogActive + src -> logging serialLogAppend(char const*) + src -> logging serialLogBuf + src -> logging serialLogBufPos + src -> logging serialLogFile + src -> logging serialLogFilename + src -> logging serialLogFlush() + src -> logging serialLogRotate() + src -> logging serialRing + src -> logging serialRingAppend(char const*) + src -> logging serialRingHead + src -> logging serialRingPeek() + src -> logging serialRingRead() + src -> logging serialRingTail + src -> logging shouldAttemptReInit() + src -> logging std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() + src -> logging trySDReInit() + linkage changes: 8 + lastLogFlush b -> B + lastLogRotation b -> B + sdHealth b -> B + serialLogActive b -> B + serialLogFilename b -> B + serialRing b -> B + serialRingHead b -> B + serialRingTail b -> B + re-signed: 1 + initSerialLog() -> initSerialLog(bool) + removed: none + added: ['_GLOBAL__sub_I_serialRing'] + tolerated: _GLOBAL__sub_I_serialRing (static initialiser of the new TU) + +2. size deltas: 23 symbols, net -27 bytes + -34 _GLOBAL__sub_I_WIFI_SSID_1 identical references; layout/relaxation + -4 handleWebOps() identical references; layout/relaxation + -4 initTouch() calls moved: ['logPrint(char const*)', 'logPrintln(char const*)'] + -4 loadCacheFoundStatus() calls moved: ['logPrintf(char const*, ...)', 'logPrintln(char const*)', 'recordSDSuccess()', 'sdOpenSafe(char const*, char const*, bool)'] + -4 saveBsecState() calls moved: ['logPrintf(char const*, ...)', 'logPrintln(char const*)', 'recordSDError(SDErrorType)', 'sdOpenSafe(char const*, char const*, bool)'] + -4 updateGeocacheData() identical references; layout/relaxation + -4 updateSettingsData() calls moved: ['logPrintf(char const*, ...)'] + -3 handleWebGPS() identical references; layout/relaxation + -3 updateDisplay() identical references; layout/relaxation + -2 loadGeocachesFromSD() calls moved: ['logPrintf(char const*, ...)', 'logPrintln(char const*)', 'recordSDSuccess()', 'sdOpenSafe(char const*, char const*, bool)'] + -1 drawOLEDScreenEnv() identical references; layout/relaxation + -1 loadWeatherHistory() calls moved: ['logPrint(char const*)', 'logPrintf(char const*, ...)', 'recordSDSuccess()', 'sdOpenSafe(char const*, char const*, bool)'] + +1 serialRingAppend(char const*) moved function; relaxation in the new TU + +2 parseGPXFromString(String const&, bool) calls moved: ['logPrintf(char const*, ...)'] + +3 initWiFi() calls moved: ['logPrint(char const*)', 'logPrintf(char const*, ...)', 'logPrintln(char const*)'] + +3 updateCompassData() identical references; layout/relaxation + +4 TFT_eSPI::~TFT_eSPI() not an authored function; layout + +4 TFT_eSPI::~TFT_eSPI() not an authored function; layout + +4 handleButtons() touches published: ['sdAvailable'] + +4 handleWebDiags() touches published: ['sdAvailable'] + +4 initBME688() calls moved: ['logPrint(char const*)', 'logPrintf(char const*, ...)', 'logPrintln(char const*)'] + +4 loadBsecState() calls moved: ['logPrintf(char const*, ...)', 'logPrintln(char const*)', 'recordSDError(SDErrorType)', 'sdOpenSafe(char const*, char const*, bool)'] + +8 setup() calls re-signed ['initSerialLog']; argument now ['rtcAvailable'] + attribution: {'layout only': 7, 'calls a moved function': 10, 'moved function itself': 1, 'library instantiation': 2, 'touches published state': 2, 'caller of a re-signed function': 1} + +3. linked output sections (non-debug) that changed: + .flash.rodata 479148 -> 479180 (+32) + .flash.text 1273996 -> 1274044 (+48) + +RESULT: PASS - move is behaviour-neutral by symbol set and reference sets diff --git a/docs/e4-proofs/verify-265a-band4-lvgl_port.txt b/docs/e4-proofs/verify-265a-band4-lvgl_port.txt new file mode 100644 index 0000000..2798ab9 --- /dev/null +++ b/docs/e4-proofs/verify-265a-band4-lvgl_port.txt @@ -0,0 +1,65 @@ +scripts/verify_extraction.py -- band 4 unit 1 (#265, PR #280, commit 9ce4ddb): lvgl_port +before = ae514ed built in the task worktree before any move, after = commit 9ce4ddb. Verbatim output. + +1. symbols: 592 before, 592 after (union of 20 objects) + relocated: 27 + src -> lvgl_port fcStyleBody + src -> lvgl_port fcStyleError + src -> lvgl_port fcStyleHeader + src -> lvgl_port fcStyleHero + src -> lvgl_port fcStyleLabel + src -> lvgl_port fcStyleValue + src -> lvgl_port fcStyleWarn + src -> lvgl_port initFCTheme() + src -> lvgl_port lastTouchX + src -> lvgl_port lastTouchY + src -> lvgl_port lvglAvailable + src -> lvgl_port lvglBuf1 + src -> lvgl_port lvglBuf2 + src -> lvgl_port lvglDisplay + src -> lvgl_port lvglEncoderIndev + src -> lvgl_port lvglEncoderReadCb(_lv_indev_t*, lv_indev_data_t*) + src -> lvgl_port lvglEncoderReadCb(_lv_indev_t*, lv_indev_data_t*)::prevA + src -> lvgl_port lvglEncoderReadCb(_lv_indev_t*, lv_indev_data_t*)::prevB + src -> lvgl_port lvglFlushCb(_lv_display_t*, lv_area_t const*, unsigned char*) + src -> lvgl_port lvglGroup + src -> lvgl_port lvglLogCb(signed char, char const*) + src -> lvgl_port lvglTickCb() + src -> lvgl_port lvglTouchIndev + src -> lvgl_port lvglTouchReadCb(_lv_indev_t*, lv_indev_data_t*) + src -> lvgl_port touchPressCount + src -> lvgl_port touchReleaseCount + src -> lvgl_port touchWasPressed + linkage changes: 13 + lastTouchX d -> D + lastTouchY d -> D + lvglAvailable b -> B + lvglBuf1 b -> B + lvglBuf2 b -> B + lvglDisplay b -> B + lvglEncoderIndev b -> B + lvglGroup b -> B + lvglTickCb() t -> T + lvglTouchIndev b -> B + touchPressCount b -> B + touchReleaseCount b -> B + touchWasPressed b -> B + re-signed: 0 + removed: none + added: none + +2. size deltas: 5 symbols, net +2 bytes + -4 initSD() identical references; layout/relaxation + -1 handleGeocacheButtons(bool, bool) identical references; layout/relaxation + +1 updateGeocacheData() identical references; layout/relaxation + +2 screenGestureCb(_lv_event_t*) identical references; layout/relaxation + +4 updateCompassData() identical references; layout/relaxation + attribution: {'layout only': 5} + +2b. moved functions checked by reference set: 5, 0 references now cross-TU, every other reference kept + +3. linked output sections (non-debug) that changed: + .flash.rodata 479448 -> 479440 (-8) + .flash.text 1274440 -> 1274428 (-12) + +RESULT: PASS - move is behaviour-neutral by symbol set and reference sets diff --git a/docs/e4-proofs/verify-265b-band4-oled_screens.txt b/docs/e4-proofs/verify-265b-band4-oled_screens.txt new file mode 100644 index 0000000..9499fc3 --- /dev/null +++ b/docs/e4-proofs/verify-265b-band4-oled_screens.txt @@ -0,0 +1,27 @@ +scripts/verify_extraction.py -- band 4 unit 2 (#265, PR #280, commit 6ec9fe6): oled_screens +before = commit 9ce4ddb built, after = commit 6ec9fe6. Verbatim output. + +1. symbols: 592 before, 592 after (union of 21 objects) + relocated: 6 + src -> oled_screens drawOLEDNavBar() + src -> oled_screens drawOLEDScreenCompass() + src -> oled_screens drawOLEDScreenEnv() + src -> oled_screens drawOLEDScreenGeocache() + src -> oled_screens drawOLEDScreenTelemetry() + src -> oled_screens updateOLED() + linkage changes: 0 + re-signed: 0 + removed: none + added: none + +2. size deltas: 1 symbols, net -1 bytes + -1 updateOLED() moved; same references, 0 now cross-TU + attribution: {'moved function itself': 1} + +2b. moved functions checked by reference set: 6, 0 references now cross-TU, every other reference kept + +3. linked output sections (non-debug) that changed: + .flash.rodata 479440 -> 479432 (-8) + .flash.text 1274428 -> 1274488 (+60) + +RESULT: PASS - move is behaviour-neutral by symbol set and reference sets diff --git a/docs/e4-proofs/verify-265c-band4-screens-navigation.txt b/docs/e4-proofs/verify-265c-band4-screens-navigation.txt new file mode 100644 index 0000000..3dc6968 --- /dev/null +++ b/docs/e4-proofs/verify-265c-band4-screens-navigation.txt @@ -0,0 +1,253 @@ +scripts/verify_extraction.py -- band 4 unit 3 (#265, PR #280, commit b6f2261): the five LVGL screens and navigation +before = commit 6ec9fe6 built, after = commit b6f2261. Verbatim output. The one tolerated addition is a +constant-propagation clone GCC made of an LVGL inline for a single caller in the smaller telemetry TU. + +1. symbols: 592 before, 593 after (union of 27 objects) + relocated: 213 + src -> navigation fcHeaderCreate(_lv_obj_t*, char const*) + src -> navigation fcListPickerActiveOverlay + src -> navigation fcListPickerItemCb(_lv_event_t*) + src -> navigation fcListPickerOpen(char const*, char const**, int, int, _lv_obj_t*) + src -> navigation gearIconClickCb(_lv_event_t*) + src -> navigation mainScreens + src -> navigation navigateFromSettings() + src -> navigation navigateScreen(int) + src -> navigation navigateToSettings() + src -> navigation screenGestureCb(_lv_event_t*) + src -> navigation updateDisplay() + src -> navigation updateDisplay()::lastUpdate + src -> navigation updateSDIndicators() + src -> screen_compass buildCompassScreen() + src -> screen_compass compassHeader + src -> screen_compass compassLastHeading + src -> screen_compass compassLblAlt + src -> screen_compass compassLblE + src -> screen_compass compassLblFcst + src -> screen_compass compassLblGps + src -> screen_compass compassLblHdg + src -> screen_compass compassLblLat + src -> screen_compass compassLblLon + src -> screen_compass compassLblN + src -> screen_compass compassLblS + src -> screen_compass compassLblSpd + src -> screen_compass compassLblTemp + src -> screen_compass compassLblTime + src -> screen_compass compassLblW + src -> screen_compass compassNavBar + src -> screen_compass compassRoseDrawCb(_lv_event_t*) + src -> screen_compass compassRoseObj + src -> screen_compass compassScr + src -> screen_compass updateCompassData() + src -> screen_env buildEnvScreen() + src -> screen_env buildEnvScreen()::{lambda(_lv_obj_t**, _lv_obj_t**, char const*, int)#1}::operator()(_lv_obj_t**, _lv_obj_t**, char const*, int) const + src -> screen_env envHeader + src -> screen_env envLblCo2Label + src -> screen_env envLblCo2Value + src -> screen_env envLblFcstLabel + src -> screen_env envLblFcstValue + src -> screen_env envLblHumidLabel + src -> screen_env envLblHumidValue + src -> screen_env envLblIaqLabel + src -> screen_env envLblIaqValue + src -> screen_env envLblNoSensors + src -> screen_env envLblPressLabel + src -> screen_env envLblPressValue + src -> screen_env envLblTempLabel + src -> screen_env envLblTempValue + src -> screen_env envNavBar + src -> screen_env envScr + src -> screen_env updateEnvData() + src -> screen_geocache buildGeocacheScreen() + src -> screen_geocache buildGeocacheScreen()::pulseAnim + src -> screen_geocache buildGeocacheScreen()::{lambda(void*, long)#1}::_FUN(void*, long) + src -> screen_geocache gcDPresetIdx + src -> screen_geocache gcDTPresets + src -> screen_geocache gcDetLblCoords + src -> screen_geocache gcDetLblCount + src -> screen_geocache gcDetLblDT + src -> screen_geocache gcDetLblDist + src -> screen_geocache gcDetLblFound + src -> screen_geocache gcDetLblGC + src -> screen_geocache gcDetLblHint + src -> screen_geocache gcDetLblHintLabel + src -> screen_geocache gcDetLblHints + src -> screen_geocache gcDetLblName + src -> screen_geocache gcDetNavBar + src -> screen_geocache gcDistPresets + src -> screen_geocache gcFiltLblActive + src -> screen_geocache gcFiltLblDRange + src -> screen_geocache gcFiltLblDist + src -> screen_geocache gcFiltLblFound + src -> screen_geocache gcFiltLblSort + src -> screen_geocache gcFiltLblTRange + src -> screen_geocache gcFilterBackCb(_lv_event_t*) + src -> screen_geocache gcFilterBtnCb(_lv_event_t*) + src -> screen_geocache gcFilterDMinUpCb(_lv_event_t*) + src -> screen_geocache gcFilterDistCb(_lv_event_t*) + src -> screen_geocache gcFilterFoundCb(_lv_event_t*) + src -> screen_geocache gcFilterResetCb(_lv_event_t*) + src -> screen_geocache gcFilterSortCb(_lv_event_t*) + src -> screen_geocache gcFilterTMinUpCb(_lv_event_t*) + src -> screen_geocache gcListLblCount + src -> screen_geocache gcListLblHints + src -> screen_geocache gcListNavBar + src -> screen_geocache gcListRowLabel + src -> screen_geocache gcListRowTapCb(_lv_event_t*) + src -> screen_geocache gcListRows + src -> screen_geocache gcListScrollCtr + src -> screen_geocache gcNavGraphicObj + src -> screen_geocache gcNavHeader + src -> screen_geocache gcNavLastBearing + src -> screen_geocache gcNavLastHeading + src -> screen_geocache gcNavLastInZone + src -> screen_geocache gcNavLblAccuracy + src -> screen_geocache gcNavLblBearing + src -> screen_geocache gcNavLblDT + src -> screen_geocache gcNavLblDist + src -> screen_geocache gcNavLblHint + src -> screen_geocache gcNavLblName + src -> screen_geocache gcNavListBtnCb(_lv_event_t*) + src -> screen_geocache gcNavNavBar + src -> screen_geocache gcNavPulseRadius + src -> screen_geocache gcTPresetIdx + src -> screen_geocache gcUpdateFilterLabels() + src -> screen_geocache geocacheDetailsCtr + src -> screen_geocache geocacheFilterCtr + src -> screen_geocache geocacheListCtr + src -> screen_geocache geocacheNavCtr + src -> screen_geocache geocacheNavDrawCb(_lv_event_t*) + src -> screen_geocache geocacheScr + src -> screen_geocache handleGeocacheButtons(bool, bool) + src -> screen_geocache updateGeocacheData() + src -> screen_settings aboutBackCb(_lv_event_t*) + src -> screen_settings aboutValueLabels + src -> screen_settings buildSettingsScreen() + src -> screen_settings buildSettingsScreen()::aboutLabels + src -> screen_settings buildSettingsScreen()::diagLabels + src -> screen_settings calArc + src -> screen_settings calBackCb(_lv_event_t*) + src -> screen_settings calCountdownLabel + src -> screen_settings calIdleActBar + src -> screen_settings calInstructLabel + src -> screen_settings calMinMaxLabel + src -> screen_settings calOffsetsLabel + src -> screen_settings calStartBtn + src -> screen_settings calStartBtnCb(_lv_event_t*) + src -> screen_settings calStatusLabel + src -> screen_settings cfgBackCb(_lv_event_t*) + src -> screen_settings cfgDistToggle + src -> screen_settings cfgOKCb(_lv_event_t*) + src -> screen_settings cfgPreviewLabel + src -> screen_settings cfgTempToggle + src -> screen_settings cfgTimeToggle + src -> screen_settings cfgToggleCb(_lv_event_t*) + src -> screen_settings cfgTzDropdown + src -> screen_settings cfgTzDropdownCb(_lv_event_t*) + src -> screen_settings cfgTzDropdownCb(_lv_event_t*)::tzNames + src -> screen_settings cfgTzSelectedCb(_lv_event_t*) + src -> screen_settings diagValueLabels + src -> screen_settings diagsBackCb(_lv_event_t*) + src -> screen_settings dispBackCb(_lv_event_t*) + src -> screen_settings dispBrightnessChangedCb(_lv_event_t*) + src -> screen_settings dispBrightnessLabel + src -> screen_settings dispBrightnessSlider + src -> screen_settings dispOKCb(_lv_event_t*) + src -> screen_settings dispOledDropdown + src -> screen_settings dispOledDropdownCb(_lv_event_t*) + src -> screen_settings dispOledSelectedCb(_lv_event_t*) + src -> screen_settings dispTftDropdown + src -> screen_settings dispTftDropdownCb(_lv_event_t*) + src -> screen_settings dispTftSelectedCb(_lv_event_t*) + src -> screen_settings fcSettingsScrollCreate(_lv_obj_t*, int) + src -> screen_settings logLvglHeap(char const*) + src -> screen_settings lv_obj_set_style_pad_all + src -> screen_settings resetBackCb(_lv_event_t*) + src -> screen_settings resetBtnCb(_lv_event_t*) + src -> screen_settings settingsAboutCtr + src -> screen_settings settingsBackToScreenCb(_lv_event_t*) + src -> screen_settings settingsCalCtr + src -> screen_settings settingsConfigCtr + src -> screen_settings settingsDiagsCtr + src -> screen_settings settingsDisplayCtr + src -> screen_settings settingsMenuBtnCb(_lv_event_t*) + src -> screen_settings settingsMenuCtr + src -> screen_settings settingsMenuItems + src -> screen_settings settingsResetCtr + src -> screen_settings settingsScr + src -> screen_settings settingsScrollAreas + src -> screen_settings updateSettingsData() + src -> screen_settings updateSettingsData()::prevSubScreen + src -> screen_telemetry _ZZ20buildTelemetryScreenvENKUlPP9_lv_obj_tPKciiPK10_lv_font_t10lv_color_tE_clES1_S3_iiS6_S7_$isra$0 + src -> screen_telemetry buildTelemetryScreen() + src -> screen_telemetry telDivider + src -> screen_telemetry telHeader + src -> screen_telemetry telLblAccelLabel + src -> screen_telemetry telLblAccelValue + src -> screen_telemetry telLblAltLabel + src -> screen_telemetry telLblAltValue + src -> screen_telemetry telLblGpsAcquiring + src -> screen_telemetry telLblGpsCheckConn + src -> screen_telemetry telLblGpsElapsed + src -> screen_telemetry telLblGpsNoData + src -> screen_telemetry telLblGpsSatCount + src -> screen_telemetry telLblGpsSection + src -> screen_telemetry telLblGpsSkyHint + src -> screen_telemetry telLblHdgLabel + src -> screen_telemetry telLblHdgValue + src -> screen_telemetry telLblHdopLabel + src -> screen_telemetry telLblHdopValue + src -> screen_telemetry telLblImuSection + src -> screen_telemetry telLblLatLabel + src -> screen_telemetry telLblLatValue + src -> screen_telemetry telLblLonLabel + src -> screen_telemetry telLblLonValue + src -> screen_telemetry telLblNoImu + src -> screen_telemetry telLblPitchLabel + src -> screen_telemetry telLblPitchValue + src -> screen_telemetry telLblRollLabel + src -> screen_telemetry telLblRollValue + src -> screen_telemetry telLblSatLabel + src -> screen_telemetry telLblSatValue + src -> screen_telemetry telLblSpdLabel + src -> screen_telemetry telLblSpdValue + src -> screen_telemetry telLblStatusLabel + src -> screen_telemetry telLblStatusValue + src -> screen_telemetry telNavBar + src -> screen_telemetry telShowGpsDataRows(bool) + src -> screen_telemetry telShowImuDataRows(bool) + src -> screen_telemetry telemetryScr + src -> screen_telemetry updateTelemetryData() + linkage changes: 12 + compassHeader b -> B + compassNavBar b -> B + compassScr b -> B + envHeader b -> B + envScr b -> B + gcNavHeader b -> B + geocacheScr b -> B + screenGestureCb(_lv_event_t*) t -> T + settingsScr b -> B + settingsScrollAreas b -> B + telHeader b -> B + telemetryScr b -> B + re-signed: 0 + removed: none + added: ['lv_obj_set_style_pad_all$constprop$0'] + tolerated: lv_obj_set_style_pad_all$constprop$0 (a compiler clone of a library inline: the smaller TU specialised it for one caller; reference sets compare it as the original) + +2. size deltas: 6 symbols, net +104 bytes + -8 buildTelemetryScreen() moved; same references, 2 now cross-TU + -4 updateCompassData() moved; same references, 0 now cross-TU + +4 initSD() identical references; layout/relaxation + +36 buildCompassScreen() moved; same references, 3 now cross-TU + +36 buildGeocacheScreen() moved; same references, 3 now cross-TU + +40 buildEnvScreen() moved; same references, 3 now cross-TU + attribution: {'moved function itself': 5, 'layout only': 1} + +2b. moved functions checked by reference set: 61, 27 references now cross-TU, every other reference kept + +3. linked output sections (non-debug) that changed: + .flash.rodata 479432 -> 479336 (-96) + .flash.text 1274488 -> 1274672 (+184) + +RESULT: PASS - move is behaviour-neutral by symbol set and reference sets diff --git a/docs/e4-proofs/verify-270-band3a-fram-settings-rtc.txt b/docs/e4-proofs/verify-270-band3a-fram-settings-rtc.txt new file mode 100644 index 0000000..fa10020 --- /dev/null +++ b/docs/e4-proofs/verify-270-band3a-fram-settings-rtc.txt @@ -0,0 +1,86 @@ +scripts/verify_extraction.py -- band 3a (#270, PR #275, merge 43e569a): fram, settings, rtc +before = 4cc1f21 built in the task worktree before any move, after = the PR head. Verbatim output. + +1. symbols: 584 before, 585 after (union of 8 objects) + relocated: 38 + src -> fram fram + src -> fram framAvailable + src -> fram framFormat() + src -> fram framHeader + src -> fram framReadHeader() + src -> fram framWriteHeader() + src -> fram initFRAM() + src -> rtc initRTC() + src -> rtc rtc + src -> rtc rtcAvailable + src -> rtc syncRTCFromSystemTime(char const*) + src -> settings OLED_TIMEOUT_COUNT + src -> settings TFT_TIMEOUT_COUNT + src -> settings applyTimezone() + src -> settings factoryReset() + src -> settings findTimeoutIndex(unsigned long const*, int, unsigned long) + src -> settings formatTimeStr(char*, int, int, int, bool) + src -> settings loadSettings() + src -> settings loadSettingsFromFRAM() + src -> settings mktimeUTC(tm*) + src -> settings oledSleepMs + src -> settings oledTimeoutLabels + src -> settings oledTimeoutPresets + src -> settings posixTZ + src -> settings saveSettings() + src -> settings saveSettingsToFRAM() + src -> settings settingsLoadedFromSD + src -> settings std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() + src -> settings tftBrightness + src -> settings tftSleepMs + src -> settings tftTimeoutLabels + src -> settings tftTimeoutPresets + src -> settings tzDisplayName + src -> settings tzPresets + src -> settings tzSelectedIndex + src -> settings use12Hour + src -> settings useFahrenheit + src -> settings useMetricUnits + linkage changes: 2 + settingsLoadedFromSD b -> B + tzPresets r -> R + re-signed: 0 + removed: none + added: ['_GLOBAL__sub_I_fram'] + tolerated: _GLOBAL__sub_I_fram (static initialiser of the new TU) + +2. size deltas: 26 symbols, net +22 bytes + -34 _GLOBAL__sub_I_WIFI_SSID_1 identical references; layout/relaxation + -4 checkWiFi() identical references; layout/relaxation + -4 handleGeocacheUploadComplete() identical references; layout/relaxation + -4 handleWebLogDownload() identical references; layout/relaxation + -4 loadSettings() moved function; relaxation in the new TU + -2 findTimeoutIndex(unsigned long const*, int, un moved function; relaxation in the new TU + -1 drawOLEDScreenTelemetry() touches published: ['useMetricUnits'] + -1 extractXMLField(String const&, char const*, ch identical references; layout/relaxation + -1 getTrendArrow() identical references; layout/relaxation + -1 handleGeocacheButtons(bool, bool) identical references; layout/relaxation + +1 drawOLEDScreenEnv() touches published: ['useFahrenheit'] + +1 loadWeatherHistory() identical references; layout/relaxation + +1 updateCompassData() calls moved: ['formatTimeStr(char*, int, int, int, bool)'] + +2 loadGeocachesFromSD() identical references; layout/relaxation + +3 updateDisplay() identical references; layout/relaxation + +4 saveBsecState() identical references; layout/relaxation + +4 updateGeocacheData() touches published: ['useMetricUnits'] + +4 updateSettingsData() calls moved: ['formatTimeStr(char*, int, int, int, bool)'] + +5 dispOledDropdownCb(_lv_event_t*) calls moved: ['findTimeoutIndex(unsigned long const*, int, unsigned long)'] + +5 dispTftDropdownCb(_lv_event_t*) calls moved: ['findTimeoutIndex(unsigned long const*, int, unsigned long)'] + +8 buildSettingsScreen() calls moved: ['findTimeoutIndex(unsigned long const*, int, unsigned long)'] + +8 dispBackCb(_lv_event_t*) calls moved: ['findTimeoutIndex(unsigned long const*, int, unsigned long)', 'loadSettings()'] + +8 dispOledSelectedCb(_lv_event_t*) touches published: ['oledSleepMs', 'oledTimeoutLabels'] + +8 dispTftSelectedCb(_lv_event_t*) touches published: ['tftSleepMs', 'tftTimeoutLabels'] + +8 handleWebOps() calls moved: ['formatTimeStr(char*, int, int, int, bool)'] + +8 resetBtnCb(_lv_event_t*) calls moved: ['factoryReset()', 'findTimeoutIndex(unsigned long const*, int, unsigned long)'] + attribution: {'layout only': 11, 'moved function itself': 2, 'touches published state': 5, 'calls a moved function': 8} + +3. linked output sections (non-debug) that changed: + .dram0.bss 79312 -> 79328 (+16) + .flash.rodata 479180 -> 479236 (+56) + .flash.text 1274044 -> 1274108 (+64) + +RESULT: PASS - move is behaviour-neutral by symbol set and reference sets diff --git a/docs/e4-proofs/verify-271-band3b-gps-imu-env-battery.txt b/docs/e4-proofs/verify-271-band3b-gps-imu-env-battery.txt new file mode 100644 index 0000000..823ef98 --- /dev/null +++ b/docs/e4-proofs/verify-271-band3b-gps-imu-env-battery.txt @@ -0,0 +1,136 @@ +scripts/verify_extraction.py -- band 3b (#271, PR #276, merge e6465bb): gps, imu, env, battery +before = 43e569a rebuilt in a scratch worktree with the fixed reference-graph.py (read-only data counted), +after = the PR head. Verbatim output. The one tolerated addition, gnrmcFixThisCycle, is write-only dead +state the compiler had eliminated as a static; it is the owner's call to delete. + +1. symbols: 585 before, 589 after (union of 12 objects) + relocated: 76 + src -> battery battery + src -> battery batteryAvailable + src -> battery initBattery() + src -> battery isBatteryConnected() + src -> battery logBatteryToFRAM() + src -> battery logBatteryToSD() + src -> battery std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() + src -> env bmeAvailable + src -> env bsec2_config + src -> env bsecDataCallback(bme68x_data, bsecOutputs, Bsec2) + src -> env bsecDataCallback(bme68x_data, bsecOutputs, Bsec2)::lastAccuracy + src -> env bsecState + src -> env bsecStateLoaded + src -> env bsecStateSaved + src -> env envData + src -> env envSensor + src -> env getIaqAccuracyText(unsigned char) + src -> env getIaqQualityText(float) + src -> env hPaToInHg(float) + src -> env initBME688() + src -> env initSHT41() + src -> env lastBsecStateSave + src -> env loadBsecFromFRAM() + src -> env loadBsecState() + src -> env readBME688() + src -> env readSHT41() + src -> env saveBsecState() + src -> env saveBsecToFRAM() + src -> env sht4 + src -> env shtAvailable + src -> env shtData + src -> gps getGpsAccuracyMeters() + src -> gps gprmcFixThisCycle + src -> gps gpsBuffer + src -> gps gpsBufferIndex + src -> gps gpsData + src -> gps gpsDebugEnabled + src -> gps gpsFirstFixTime + src -> gps gpsFirstReceiveTime + src -> gps gpsHadFirstFix + src -> gps gpsHadFirstReceive + src -> gps gpsLastByteTime + src -> gps gpsSignalLostTime + src -> gps initGPS() + src -> gps lastRmcCycleTime + src -> gps nmeaParse(char*, char**, int) + src -> gps parseNMEA(char*) + src -> gps readGPS() + src -> gps rtcSyncedFromGPS + src -> imu Adafruit_LIS3MDL::~Adafruit_LIS3MDL() + src -> imu Adafruit_LSM6DSOX::~Adafruit_LSM6DSOX() + src -> imu imuAvailable + src -> imu imuData + src -> imu initIMU() + src -> imu lis + src -> imu loadMagCal() + src -> imu lsm + src -> imu magAvailable + src -> imu magCalMaxX + src -> imu magCalMaxY + src -> imu magCalMaxZ + src -> imu magCalMinX + src -> imu magCalMinY + src -> imu magCalMinZ + src -> imu magCalStartTime + src -> imu magCalibrated + src -> imu magCalibrating + src -> imu magLogPrintln(char const*) + src -> imu magOffsetX + src -> imu magOffsetY + src -> imu magOffsetZ + src -> imu readIMU() + src -> imu readIMU()::lastDropoutLog + src -> imu readIMU()::lastMagDebug + src -> imu readIMU()::smoothedHeading + src -> imu saveMagCal() + linkage changes: 11 + bsecStateLoaded b -> B + bsecStateSaved b -> B + gprmcFixThisCycle b -> B + gpsDebugEnabled b -> B + gpsFirstFixTime b -> B + gpsFirstReceiveTime b -> B + gpsHadFirstFix b -> B + gpsHadFirstReceive b -> B + gpsLastByteTime b -> B + gpsSignalLostTime b -> B + lastRmcCycleTime b -> B + re-signed: 0 + removed: none + added: ['_GLOBAL__sub_I_battery', '_GLOBAL__sub_I_envSensor', '_GLOBAL__sub_I_lsm', 'gnrmcFixThisCycle'] + tolerated: _GLOBAL__sub_I_battery (static initialiser of the new TU) + tolerated: _GLOBAL__sub_I_envSensor (static initialiser of the new TU) + tolerated: _GLOBAL__sub_I_lsm (static initialiser of the new TU) + tolerated: gnrmcFixThisCycle (declared published; absent before because the compiler eliminated an unread static -- dead state, worth deleting) + +2. size deltas: 25 symbols, net -88 bytes + -106 _GLOBAL__sub_I_WIFI_SSID_1 identical references; layout/relaxation + -12 drawOLEDScreenGeocache() touches published: ['gpsData', 'imuData'] + -9 drawOLEDScreenEnv() touches published: ['bmeAvailable', 'envData', 'shtAvailable', 'shtData'] + -8 updateGeocacheData() touches published: ['gpsData', 'imuData'] + -4 geocacheNavDrawCb(_lv_event_t*) touches published: ['gpsData', 'imuData'] + -4 handleWebEnv() calls moved: ['getIaqAccuracyText(unsigned char)'] + -4 loadBsecState() moved function; relaxation in the new TU + -4 updateCompassData() touches published: ['bmeAvailable', 'envData', 'gpsData', 'imuAvailable'] + -2 bsecDataCallback(bme68x_data, bsecOutputs, Bse moved function; relaxation in the new TU + -2 decodeROT13(char*) identical references; layout/relaxation + -2 handleWebGeocaches() identical references; layout/relaxation + -1 handleWebBattLog() identical references; layout/relaxation + +1 drawOLEDScreenTelemetry() touches published: ['gpsData', 'imuAvailable', 'imuData', 'magAvailable'] + +1 extractXMLField(String const&, char const*, ch identical references; layout/relaxation + +1 getTrendArrow() identical references; layout/relaxation + +1 handleGeocacheButtons(bool, bool) identical references; layout/relaxation + +4 checkWiFi() identical references; layout/relaxation + +4 handleGeocacheUploadComplete() identical references; layout/relaxation + +4 handleWebLogDownload() identical references; layout/relaxation + +4 handleWebOps() calls moved: ['isBatteryConnected()'] + +4 initTouch() identical references; layout/relaxation + +4 loadCacheFoundStatus() identical references; layout/relaxation + +8 handleWebGpsReset() touches published: ['gprmcFixThisCycle', 'gpsData', 'gpsFirstFixTime', 'gpsFirstReceiveTime'] + +14 handleWebJSON() calls moved: ['getIaqQualityText(float)', 'isBatteryConnected()'] + +20 parseNMEA(char*) moved function; relaxation in the new TU + attribution: {'layout only': 12, 'touches published state': 7, 'calls a moved function': 3, 'moved function itself': 3} + +3. linked output sections (non-debug) that changed: + .flash.rodata 479236 -> 479332 (+96) + .flash.text 1274108 -> 1274272 (+164) + +RESULT: PASS - move is behaviour-neutral by symbol set and reference sets diff --git a/docs/e4-proofs/verify-272-band3c-weather-geocache.txt b/docs/e4-proofs/verify-272-band3c-weather-geocache.txt new file mode 100644 index 0000000..c2c52ad --- /dev/null +++ b/docs/e4-proofs/verify-272-band3c-weather-geocache.txt @@ -0,0 +1,87 @@ +scripts/verify_extraction.py -- band 3c (#272, PR #277, merge 0053f77): weather, geocache +before = e6465bb built in the task worktree before any move, after = the PR head. Verbatim output. +First band checked with the moved-function reference-set rule (section 2b). + +1. symbols: 589 before, 589 after (union of 14 objects) + relocated: 46 + src -> geocache String::replace(char const*, char const*) + src -> geocache cacheList + src -> geocache cacheListCount + src -> geocache decodeROT13(char*) + src -> geocache extractXMLField(String const&, char const*, char const*, char*, unsigned int) + src -> geocache extractXMLFloat(String const&, char const*, char const*, float) + src -> geocache gcApplyFilters() + src -> geocache gcCachedDist + src -> geocache gcFilterDMax + src -> geocache gcFilterDMin + src -> geocache gcFilterFoundMode + src -> geocache gcFilterMaxDistKm + src -> geocache gcFilterTMax + src -> geocache gcFilterTMin + src -> geocache gcFilteredCount + src -> geocache gcFilteredIndices + src -> geocache gcLastSortTime + src -> geocache gcSortMode + src -> geocache gcUpdateDistances() + src -> geocache listHighlightIndex + src -> geocache loadCacheFoundStatus() + src -> geocache loadGeocachesFromSD() + src -> geocache parseGPXFromString(String const&, bool) + src -> geocache saveCacheFoundStatus() + src -> weather addToWeatherHistory(WeatherReading) + src -> weather calculateForecast() + src -> weather calculateWeatherTrend() + src -> weather framFlushToSD() + src -> weather getCurrentTimestamp() + src -> weather getTrendArrow() + src -> weather getWeatherFilename(char*, int) + src -> weather getWeatherReading(int) + src -> weather lastFramFlush + src -> weather lastWeatherLog + src -> weather lastWeatherLogCheck + src -> weather loadWeatherHistory() + src -> weather logWeatherReading() + src -> weather logWeatherToFRAM(WeatherReading&) + src -> weather std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() + src -> weather updateWeatherLogStats() + src -> weather weatherHistory + src -> weather weatherHistoryCount + src -> weather weatherHistoryHead + src -> weather weatherLogEntryCount + src -> weather weatherLogFileCount + src -> weather weatherTrend + linkage changes: 2 + weatherLogEntryCount b -> B + weatherLogFileCount b -> B + re-signed: 0 + removed: none + added: none + +2. size deltas: 18 symbols, net +43 bytes + -4 handleButtons() calls moved: ['framFlushToSD()'] + -4 handleWebLogDownload() identical references; layout/relaxation + -4 initSD() identical references; layout/relaxation + -4 updateSettingsData() touches published: ['weatherHistoryCount', 'weatherLogEntryCount', 'weatherLogFileCount'] + -1 gcUpdateFilterLabels() touches published: ['gcFilterDMax', 'gcFilterDMin', 'gcFilterFoundMode', 'gcFilterMaxDistKm'] + -1 handleWebGpsReset() identical references; layout/relaxation + -1 handleWebIMU() identical references; layout/relaxation + -1 updateGeocacheData() calls moved: ['gcApplyFilters()'] + -1 updateOLED() identical references; layout/relaxation + +1 drawOLEDScreenEnv() calls moved: ['getTrendArrow()'] + +1 handleWebBattLog() identical references; layout/relaxation + +2 decodeROT13(char*) moved; same references, 0 now cross-TU + +2 handleWebJSON() identical references; layout/relaxation + +4 handleGeocacheUploadData() calls moved: ['gcApplyFilters()', 'loadCacheFoundStatus()', 'parseGPXFromString(String const&, bool)'] + +4 handleWebDiags() touches published: ['weatherHistoryCount', 'weatherLogEntryCount', 'weatherLogFileCount'] + +4 handleWebGPS() identical references; layout/relaxation + +16 loadCacheFoundStatus() moved; same references, 0 now cross-TU + +30 loadGeocachesFromSD() moved; same references, 0 now cross-TU + attribution: {'calls a moved function': 4, 'layout only': 8, 'touches published state': 3, 'moved function itself': 3} + +2b. moved functions checked by reference set: 21, 0 references now cross-TU, every other reference kept + +3. linked output sections (non-debug) that changed: + .flash.rodata 479332 -> 479400 (+68) + .flash.text 1274272 -> 1274388 (+116) + +RESULT: PASS - move is behaviour-neutral by symbol set and reference sets diff --git a/docs/e4-proofs/verify-273-band3d-oled-display-uistate-touch.txt b/docs/e4-proofs/verify-273-band3d-oled-display-uistate-touch.txt new file mode 100644 index 0000000..af95e9e --- /dev/null +++ b/docs/e4-proofs/verify-273-band3d-oled-display-uistate-touch.txt @@ -0,0 +1,67 @@ +scripts/verify_extraction.py -- band 3d (#273, PR #278, merge 7bfefa7): oled, display, ui_state, touch +before = 0053f77 built in the task worktree before any move, after = the PR head. Verbatim output. + +1. symbols: 589 before, 592 after (union of 18 objects) + relocated: 29 + src -> display TFT_eSPI::~TFT_eSPI() + src -> display checkDisplaySleep() + src -> display checkTFTHealth() + src -> display initTFT() + src -> display lastActivityTime + src -> display lastTFTReinit + src -> display lastTFTUpdate + src -> display sleepTFT() + src -> display tft + src -> display tftSleeping + src -> display tftUpdateCount + src -> display wakeAllDisplays() + src -> display wakeTFT() + src -> oled initOLED() + src -> oled oled + src -> oled oledAvailable + src -> oled oledSleeping + src -> oled sleepOLED() + src -> oled wakeOLED() + src -> touch ctp + src -> touch initTouch() + src -> touch touchAvailable + src -> touch touchISR() + src -> ui_state currentScreen + src -> ui_state geocacheSubScreen + src -> ui_state listScrollOffset + src -> ui_state previousScreen + src -> ui_state selectedCacheIndex + src -> ui_state settingsSubScreen + linkage changes: 3 + lastTFTReinit b -> B + lastTFTUpdate b -> B + tftUpdateCount b -> B + re-signed: 0 + removed: none + added: ['_GLOBAL__sub_I_ctp', '_GLOBAL__sub_I_oled', '_GLOBAL__sub_I_tft'] + tolerated: _GLOBAL__sub_I_ctp (static initialiser of the new TU) + tolerated: _GLOBAL__sub_I_oled (static initialiser of the new TU) + tolerated: _GLOBAL__sub_I_tft (static initialiser of the new TU) + +2. size deltas: 12 symbols, net -88 bytes + -80 _GLOBAL__sub_I_WIFI_SSID_1 identical references; layout/relaxation + -4 checkWiFi() identical references; layout/relaxation + -4 handleWebDiags() touches published: ['oledAvailable', 'touchAvailable'] + -4 handleWebGPS() identical references; layout/relaxation + -3 lvglTouchReadCb(_lv_indev_t*, lv_indev_data_t* touches published: ['ctp', 'currentScreen', 'lastActivityTime', 'settingsSubScreen'] + -3 updateDisplay() touches published: ['currentScreen', 'lastTFTUpdate', 'oledAvailable', 'oledSleeping'] + +1 handleWebGpsReset() identical references; layout/relaxation + +1 handleWebIMU() identical references; layout/relaxation + +1 updateGeocacheData() touches published: ['currentScreen', 'geocacheSubScreen', 'selectedCacheIndex'] + +1 updateOLED() touches published: ['currentScreen', 'oled'] + +2 handleWebGeocaches() identical references; layout/relaxation + +4 initSD() identical references; layout/relaxation + attribution: {'layout only': 7, 'touches published state': 5} + +2b. moved functions checked by reference set: 11, 4 references now cross-TU, every other reference kept + +3. linked output sections (non-debug) that changed: + .flash.rodata 479400 -> 479420 (+20) + .flash.text 1274388 -> 1274412 (+24) + +RESULT: PASS - move is behaviour-neutral by symbol set and reference sets diff --git a/docs/e4-proofs/verify-274-band3e-web.txt b/docs/e4-proofs/verify-274-band3e-web.txt new file mode 100644 index 0000000..2bd1d07 --- /dev/null +++ b/docs/e4-proofs/verify-274-band3e-web.txt @@ -0,0 +1,88 @@ +scripts/verify_extraction.py -- band 3e (#274, PR #279, merge ae514ed): web, fc_version.h +before = 7bfefa7 built in a scratch worktree (the in-worktree baseline had raced the extraction; see +docs/god-object-inventory.md section 14), after = the PR head. Verbatim output. + +1. symbols: 592 before, 592 after (union of 19 objects) + relocated: 55 + src -> web NTP_SERVER + src -> web String::endsWith(char const*) const + src -> web String::length() const + src -> web Uri::canHandle(String const&, std::vector >&) + src -> web Uri::clone() const + src -> web Uri::initPathArgs(std::vector >&) + src -> web Uri::~Uri() + src -> web WIFI_PASS_1 + src -> web WIFI_PASS_2 + src -> web WIFI_PASS_3 + src -> web WIFI_SSID_1 + src -> web WIFI_SSID_2 + src -> web WIFI_SSID_3 + src -> web _GLOBAL__sub_I_WIFI_SSID_1 + src -> web _ZN9WebServer10streamFileIN2fs4FileEEEjRT_RK6Stringi$isra$0 + src -> web checkWiFi() + src -> web gpxUploadBuffer + src -> web gpxUploadError + src -> web gpxUploadSuccess + src -> web handleGeocacheClear() + src -> web handleGeocacheDelete() + src -> web handleGeocacheDownload() + src -> web handleGeocacheToggleFound() + src -> web handleGeocacheUploadComplete() + src -> web handleGeocacheUploadData() + src -> web handleWebBattLog() + src -> web handleWebBattLogClear() + src -> web handleWebDiags() + src -> web handleWebEnv() + src -> web handleWebGPS() + src -> web handleWebGeocaches() + src -> web handleWebGpsDebug() + src -> web handleWebGpsReset() + src -> web handleWebIMU() + src -> web handleWebJSON() + src -> web handleWebLogDownload() + src -> web handleWebLogs() + src -> web handleWebOps() + src -> web handleWebRoot() + src -> web handleWebSerial() + src -> web handleWebSerialData() + src -> web initWebServer() + src -> web initWiFi() + src -> web lastWiFiAttempt + src -> web ntpSynced + src -> web rtcSyncedFromNTP + src -> web std::_Function_base::~_Function_base() + src -> web std::_Function_handler::_M_invoke(std::_Any_data const&) + src -> web std::_Function_handler::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) + weather -> web std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() + src -> web vtable for Uri + src -> web webSerialReadPos + src -> web webServer + src -> web webServerStarted + src -> web wifiConnected + linkage changes: 0 + re-signed: 0 + removed: none + added: none + +2. size deltas: 12 symbols, net +16 bytes + -4 handleGeocacheClear() moved; same references, 0 now cross-TU + -3 initWiFi() moved; same references, 0 now cross-TU + -2 screenGestureCb(_lv_event_t*) identical references; layout/relaxation + -1 updateGeocacheData() identical references; layout/relaxation + +1 gcUpdateFilterLabels() identical references; layout/relaxation + +3 handleWebDiags() moved; same references, 0 now cross-TU + +3 lvglTouchReadCb(_lv_indev_t*, lv_indev_data_t* identical references; layout/relaxation + +3 updateDisplay() identical references; layout/relaxation + +4 checkWiFi() moved; same references, 0 now cross-TU + +4 handleButtons() identical references; layout/relaxation + +4 handleWebGPS() moved; same references, 0 now cross-TU + +4 handleWebLogDownload() moved; same references, 0 now cross-TU + attribution: {'moved function itself': 6, 'layout only': 6} + +2b. moved functions checked by reference set: 27, 0 references now cross-TU, every other reference kept + +3. linked output sections (non-debug) that changed: + .flash.rodata 479420 -> 479448 (+28) + .flash.text 1274412 -> 1274440 (+28) + +RESULT: PASS - move is behaviour-neutral by symbol set and reference sets diff --git a/docs/e4-session-2026-09-07.md b/docs/e4-session-2026-09-07.md new file mode 100644 index 0000000..62dcaf7 --- /dev/null +++ b/docs/e4-session-2026-09-07.md @@ -0,0 +1,94 @@ +# E4 — the autonomous session of 2026-09-07 + +The record of one session in which an agent worked Epic [#212](https://github.com/Strycher/Field_Compass/issues/212) (break up the `src/src.ino` god object) alone, under a standing grant from the owner, for about six hours. It exists so that anyone — the owner in a month, another agent tomorrow — can see what was decided, why, under what authority, and what was deliberately left undone. The per-band engineering records are §8–§15 of [`god-object-inventory.md`](god-object-inventory.md); the raw proof outputs are in [`e4-proofs/`](e4-proofs/). This document is the decision log that ties them together. + +## 1. The grant + +The owner's instruction, verbatim, given with bands 1 and 2 already under way: + +> I'm going to give you some latitude to operate. I'm going to be gone driving for a few hours. I'd like you to continue work in my absence to finish this refactor so that the god object is broken up. We have a plan that you can execute against, and it seems like a solid plan. If a change is not destructive, you can make reasonable decisions for me to review when I return. If you need to do something destructive, set that aside for when I return and proceed to other work. I'd like to come home and find the god object broken up and be ready for #99, if possible. Confirm your understanding. + +The agent's confirmed reading, which the owner accepted before leaving: + +- Merge its own PRs after CI is green and the `nm` proof passes, with an approval comment that says plainly "merged autonomously under the owner's standing grant of 2026-09-07" — never "human approval received". +- Make and list reasonable calls: unit grouping, header placement, closing task issues whose PR merged. +- Set aside anything behaviour-changing, any flash, closing Epic #212, #99 itself, and anything destructive (archive, force-push, directory deletion). + +Every standing constraint stayed in force: SAFELANE gates and checkpoints, `dw` task claims before commits, the two-task convention (work task closed before push, PR task carries the merge), no raw flash, no parentless issues, no closing of issues that need the owner's agreement. + +## 2. What happened, in order + +| Step | Issue | PR | Result | Merged under the grant? | +|---|---|---|---|---| +| E4-1 name the anonymous structs | #260 | — | `664b6c2` | before the grant | +| E4-2 explicit prototypes | #261 | #267 | `e8a404d` | before the grant | +| E4-3 band 1: geo, ui_widgets, fc_theme | #262 | #268 | `b9a3327` | before the grant | +| E4-4 band 2: logging | #263 | #269 | `4cc1f21` | yes | +| band 3 split into five children | #270–#274 created under #264 | — | — | structural call, see §3.1 | +| E4-5a band 3a: fram, settings, rtc | #270 | #275 | `43e569a` | yes | +| E4-5b band 3b: gps, imu, env, battery | #271 | #276 | `e6465bb` | yes | +| E4-5c band 3c: weather, geocache | #272 | #277 | `0053f77` | yes | +| E4-5d band 3d: oled, display, ui_state, touch | #273 | #278 | `7bfefa7` | yes | +| E4-5e band 3e: web, fc_version.h | #274 | #279 | `ae514ed` | yes | +| E4-6 band 4: lvgl_port, oled_screens, five screens, navigation | #265 | #280 | open, four commits | **no — see §4** | + +Every merged PR carries a comment naming the grant. Every task issue whose PR merged was closed with the merge SHA and a pointer to its inventory section. #264 and #265 were **not** closed: both carry hardware verification in their acceptance. + +## 3. Decisions made under the grant, and why + +Each of these is also in the PR body it belongs to, marked "for review". Any of them can be reversed; none changes behaviour. + +### 3.1 Band 3 was split into five children by dependency layer, with the web unit last + +[#264](https://github.com/Strycher/Field_Compass/issues/264) said "taken in removal order, `webServer` first" and "expect this to be split into one child task per domain once band 2 has landed". The split happened, but not per domain and not web-first. The relocation graph after band 2 showed `webServer` referenced only by the 23 web handlers, `initWebServer` and `loop`, with `initWiFi` calling `initWebServer`. A web unit moved before the domains its handlers read would need those handlers declared in a header while they stayed in `src.ino` — the reach-back pattern the owner's no-`globals.h` ruling forbids — or would own one variable and no code. So the children went in the order the graph allows: storage and settings, sensors, the data domains that consume them, display and navigation state, and the web server last, when everything it reads is published. #264's acceptance is met at the end regardless of order; it was measured and posted on #264. + +Recorded in: #270's body, inventory §10. + +### 3.2 `initSerialLog()` became `initSerialLog(bool rtcOk)` + +It read `rtcAvailable`, which the RTC domain owns. Publishing that variable through `logging.h` would have been an extern reaching back into `src.ino`. Its single caller, `setup()`, passes the value: the same read of the same variable at the same moment. The proof attributes the +8 bytes in `setup`. Recorded in: #269, inventory §9. + +### 3.3 Where a few things landed + +- `src/fc_config.h` (started in band 2) holds the pin map: `SD_CS`, `SPI_*`, `FRAM_CS`, `TFT_BL`, `CTP_INT`, `BUTTON_A/B/C`. +- `src/fc_version.h` holds `FW_VERSION` (band 3e): `src.ino` and `web.cpp` both show it and one file is where a bump goes. CLAUDE.md's Versioning pointer was updated in the same PR. +- The timeout preset tables and `findTimeoutIndex` are `settings`, not display: they define what the sleep settings may be. +- `framFlushToSD` lives in `weather` (as #272's text proposed): it flushes the battery ring and the weather ring, both reachable through headers after band 3b, and `weather` is the higher of the two domains. +- `ui_state` (band 3d) holds state and no functions: `navigateScreen` indexes the LVGL screen objects the screen builders own, so the functions could not leave until band 4 — where they did, into `navigation`. +- A `touch` unit that no task listed: `handleWebDiags` reads `touchAvailable`, and #274 would have reached back for it. +- The RGB565 `COLOR_*` palette and the LVGL draw-buffer defines are `display.h`'s. +- The WiFi/NTP flags and the credential placeholders are private to `web.cpp`; nothing outside reads them, and a header is no place for credentials even as placeholders. **This is where #99 replaces them.** +- Band 4's `initLVGL` stays in `src.ino`: it builds every screen and wires the port, the composition root. `lvglTickCb` lost `static` because `initLVGL` registers it. +- Band 4 is one branch and one PR rather than five merged children: see §4. + +### 3.4 Two corrections to the #215 diagnosis + +- **The relocation graph never counted read-only data.** `reference-graph.py` took `nm` classes B/b/D/d as variables and skipped R/r, so every `const` table — `tzPresets`, the timeout tables, the 1,943-byte BSEC config blob — was invisible to the fan-in tables, the cut set and the reference sets the band-2 and 3a proofs compared. Found when `initBME688` would not compile without the blob (band 3b). Fixed; the cut set was unchanged (the corrected pool was 79 variables with the same 22-variable cut set on the post-3a object), so no ordering decision changed. The band-3b proof was re-run against a rebuilt baseline with the fixed graph. **Appendix A/B of the inventory still show the pre-correction numbers, as of `main` before E4-3; §11 says so.** +- **`gnrmcFixThisCycle` is write-only dead state** since #115: set in `parseNMEA` and cleared by `handleWebGpsReset`, read by nothing. The compiler had eliminated it as a static. It is published now (one byte and its stores). Deleting it is the owner's call — a behaviour question, not a move. + +## 4. Set aside for the owner + +- **Hardware verification of everything.** Nothing in E4 was flashed. The per-issue rule ("human flashes locally + verifies on hardware" before merge) was consciously not applied to bands 2–3 under the grant: their proof was the `nm` symbol-set and reference-set comparison, and hardware verification of the whole sequence is Epic #212's integration test. Band 4 is different: [#265](https://github.com/Strycher/Field_Compass/issues/265) says *full hardware verification, every screen and every peripheral, before merge*, so [#280](https://github.com/Strycher/Field_Compass/pull/280) is open and waits for the owner. +- Closing #264 and #265 (acceptance includes hardware), Epic #212 (needs its integration test task and sign-off), and Feature #151's E4 status. +- Deleting `gnrmcFixThisCycle` and the six dead functions listed in inventory §8. +- #99, runtime credentials — next; the placeholders are in `src/web.cpp`. +- Relaxing CLAUDE.md's "single-file source ⇒ serialise parallel agents" rule. The premise is gone (25 units, `src.ino` is 750 lines on the #280 branch); the rule stands until the owner says otherwise. + +## 5. Process faults, recorded + +- **Band 3e's baseline raced the extraction.** The in-worktree baseline build was started, and the extraction ran while PlatformIO was still compiling libraries; it picked up post-move source and failed on it, which is how it was noticed. The before-side artefacts for that proof came from a scratch worktree at `main` (7bfefa7). Band 4 captured every baseline before touching the tree. Rule: capture the baseline (nm, graph, ELF, map) **before** running `extract_unit.py`. +- **A harness fault that would have deleted a `switch` case.** The existing-prototype matcher accepted any prefix and matched `case SCREEN_COMPASS: updateCompassData(); …; break;` inside `updateDisplay`. The dry run showed it; nothing was written; it now requires type words at column 0 and nothing after the semicolon. +- Everything else the build found — a missing include, a define left behind, a static callback referenced before its definition, a preprocessor guard the harness does not carry — is listed per band in the inventory and fixed in the harness where it could be. + +## 6. Where the evidence is, and how to check it + +- Per band: inventory §9–§15 (what moved, calls for review, harness changes, measured figures). +- Raw verifier output per band: [`e4-proofs/`](e4-proofs/), one file per proof, exactly as `scripts/verify_extraction.py` printed it. +- The method: `scripts/reference-graph.py` (who uses what, from relocations), `scripts/extract_unit.py` (the move), `scripts/verify_extraction.py` (the proof). Each has a docstring that is the usage. +- To re-check a band: check out the merge commit's parent, `pio run -e feather_s3`, capture `nm --print-size --defined-only` over `.pio/build/feather_s3/src/*.o` and `python scripts/reference-graph.py --json --raw`, check out the merge commit, build again, and run `verify_extraction.py` with the `--moved`/`--published` lists from the commit message. + +## 7. Superseded + +- The planning document the session executed against (an out-of-repo plan file, and the `[REGEX]` figures in #212's body) is superseded by the inventory: §6 of the inventory says which figures survived. +- #264's "webServer first" and "one child per domain" wording: superseded by §3.1 above; #264 carries the measured acceptance in a comment. +- #265's "roughly 567 lines" residue estimate: measured 750, in §15 and on #265.