Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.

Expand All @@ -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

Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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:

Expand Down
21 changes: 21 additions & 0 deletions docs/e4-proofs/README.md
Original file line number Diff line number Diff line change
@@ -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.
82 changes: 82 additions & 0 deletions docs/e4-proofs/verify-263-band2-logging.txt
Original file line number Diff line number Diff line change
@@ -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<fs::FileImpl>)
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
65 changes: 65 additions & 0 deletions docs/e4-proofs/verify-265a-band4-lvgl_port.txt
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions docs/e4-proofs/verify-265b-band4-oled_screens.txt
Original file line number Diff line number Diff line change
@@ -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
Loading