Skip to content

feat: multicore task architecture - #170

Draft
stritti wants to merge 30 commits into
mainfrom
feat/multicore-task-architecture
Draft

stritti wants to merge 30 commits into
mainfrom
feat/multicore-task-architecture

Conversation

@stritti

@stritti stritti commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor

Final architecture

This PR isolates only blocking sensor acquisition on ESP32 Core 0. Mutable application state remains single-writer on the Arduino control-loop task on Core 1.

Core 0

  • SensorTask owns DS18B20 / OneWire access and the internal ESP32 temperature acquisition.
  • Measurements are published through SensorSlots.
  • Sensor discovery metadata is exposed through bounded cached snapshots.
  • SensorTask is registered with the task watchdog.

Core 1

  • Rules, relays, operation mode and configuration remain on the control-loop task.
  • MQTT callbacks hand commands to MqttCommandQueue; command processing and MqttPublisher serialization run on Core 1.
  • TelemetryQueue defers publish requests, but is drained directly by PoolController::loop() on Core 1.
  • NORVI button handling, UI state and OLED rendering are serialized by DisplayCoordinator on Core 1.

The earlier three-worker design (SensorTask + PublishTask + DisplayTask) was deliberately narrowed during concurrency review. Obsolete PublishTask, DisplayTask and TaskStartupPolicy artifacts have been removed. CoreScheduler now owns only SensorTask lifecycle and stack-watermark logging.

Reliability fixes included

  • Correct bounded TelemetryQueue wrap-around storage.
  • Explicit shared-bus vs. dedicated-bus DS18B20 measurement scheduling.
  • Non-blocking Dallas conversion with setWaitForConversion(false).
  • Consistent cross-core sensor snapshots instead of volatile pairs.
  • Atomic degradation status flags.
  • Checked task creation with safe restart path for critical SensorTask startup failure.
  • MQTT command ownership aligned with the Core-1 control task.
  • Documentation aligned with the final runtime ownership model.

Verification

CI is expected to cover:

  • PlatformIO builds: esp32dev, norvi_ae01_r, olimex_esp32_c6_evb
  • Native tests + relay-safety regression tests + coverage generation
  • MegaLinter
  • CodeQL

Manual verification (on-device)

The following checks intentionally remain open until verified on real hardware:

  • Control-loop latency stays acceptable during DS18B20 conversions.
  • NORVI OLED rendering and all front-panel button flows work correctly.
  • MQTT discovery/state cadence remains correct across reconnects.
  • No watchdog resets during extended operation.
  • SensorTask stack high-water mark has sufficient margin after extended operation.

@github-actions

github-actions Bot commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor

✅⚠️MegaLinter analysis: Success with warnings

Descriptor Linter Files Fixed Errors Max errors Warnings Elapsed time
✅ ACTION actionlint 8 0 0 0.56s
✅ BASH bash-exec 2 0 0 0.42s
✅ BASH shellcheck 2 0 0 0.47s
✅ BASH shfmt 2 0 0 0.01s
✅ C clang-format 1 0 0 0.04s
✅ C cppcheck 1 0 0 0.02s
✅ C cpplint 1 0 0 0.25s
✅ CPP clang-format 100 0 0 0.6s
✅ CPP cppcheck 100 0 0 4.58s
✅ CPP cpplint 100 0 0 5.59s
✅ EDITORCONFIG editorconfig-checker 284 0 0 0.48s
✅ JSON jsonlint 6 0 0 0.09s
✅ JSON v8r 6 0 0 3.32s
⚠️ MARKDOWN markdownlint 107 3 0 2.98s
✅ YAML yamllint 25 0 0 0.69s

Detailed Issues

⚠️ MARKDOWN / markdownlint - 3 errors
.opencode/skills/web-ui/SKILL.md:34 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/superpowers/plans/2026-08-10-olimex-c6-local-ui-implementation.md:106:401 error MD013/line-length Line length [Expected: 400; Actual: 452]
docs/superpowers/plans/2026-08-16-norvi-button-calibration.md:7:401 error MD013/line-length Line length [Expected: 400; Actual: 412]

Notices

⚠️ Your configuration references items that have been removed from MegaLinter and are ignored: MAKEFILE, MAKEFILE_CHECKMAKE, MARKDOWN_MARKDOWN_LINK_CHECK. See Removed linters to find their replacements.

See detailed reports in MegaLinter artifacts

Your project could benefit from a custom flavor, which would allow you to run only the linters you need, and thus improve runtime performances. (Skip this info by defining FLAVOR_SUGGESTIONS: false)

  • Documentation: Custom Flavors
  • Command: npx mega-linter-runner@10.1.0 --custom-flavor-setup --custom-flavor-linters ACTION_ACTIONLINT,BASH_EXEC,BASH_SHELLCHECK,BASH_SHFMT,C_CPPCHECK,C_CPPLINT,C_CLANG_FORMAT,CPP_CPPCHECK,CPP_CPPLINT,CPP_CLANG_FORMAT,EDITORCONFIG_EDITORCONFIG_CHECKER,JSON_JSONLINT,JSON_V8R,MARKDOWN_MARKDOWNLINT,YAML_YAMLLINT

MegaLinter is provided by OX Security
Show us your support by starring ⭐ the repository

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 45.1%
Branch Coverage 60.9%
Lines Hit/Total 237/526
Branches Hit/Total 78/128

Report from native unit tests (ASan + gcov).

@stritti stritti left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the current head b6894da against the ESP32 reliability, concurrency, and Clean-Code rules. The multicore direction is good, but I see several merge blockers in the current implementation:

  1. TelemetryQueue has an out-of-bounds ring-buffer bug. items_ is declared as items_[CAPACITY] with CAPACITY == 8, but head/tail advance modulo CAPACITY + 1. Index 8 is therefore reachable and items_[8] can be read/written after wrap-around. This is real memory corruption. Please either allocate CAPACITY + 1 storage or implement the full/empty scheme with indices constrained to [0, CAPACITY-1]. Add a wrap-around regression test (fill -> dequeue -> enqueue -> drain) because the current tests do not exercise this path.

  2. The dedicated-bus esp32dev path does not start the pool sensor conversion. SensorTask only calls solarTemperatureNode.beginMeasurement(), waits, and then calls finishMeasurement() on both nodes. That works for the shared NORVI bus, but on esp32dev solar and pool use separate DallasTemperature instances, so the pool bus needs its own beginMeasurement() before the wait/read phase. Please model shared-bus and dedicated-bus scheduling explicitly and test both hardware topologies.

  3. volatile is being used as cross-core synchronization. SensorSlots, DegradationManager, and display flags rely on word-sized volatile reads/writes. volatile does not provide C++ inter-task/inter-core synchronization or ordering. SensorSlots also publishes value and found independently, so readers can observe a mixed snapshot. Please use std::atomic where a single value is sufficient, or a proper snapshot/sequence-counter/FreeRTOS synchronization mechanism for related fields.

  4. PublishTask introduces concurrent access to MQTT/application state. PublishTask calls MqttPublisher::publishDiscovery() / publishStates() on Core 0, while MQTT command callbacks can still execute handleMqttMessage() and call publishStates() / mutate OperationModeNode, ConfigManager, etc. This breaks the intended single-writer architecture. The later command-queue approach from #209 should be incorporated conceptually so both inbound commands and outbound publishing have explicit task ownership.

  5. Task creation failures are ignored. All xTaskCreatePinnedToCore() return values should be checked. If SensorTask fails to start after sensor handling has been removed from the control loop, the controller silently runs without fresh sensor data. A critical task startup failure needs a defined safe failure path and should be testable.

  6. The PR is significantly behind current main. Its base SHA is still 1870c3e while main has moved substantially, including MQTT, OTA, sensor-recovery and reliability changes. I would rebase/update this architecture onto current main before doing deeper integration work; otherwise several fixes in newer PRs will conflict with assumptions made here.

The architectural goal itself is worth keeping: moving sensor conversion and OLED I/O off the safety-critical control loop is a good fit for ESP32. I would not merge this implementation until the memory-safety and cross-core ownership issues above are resolved.

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 58.8%
Branch Coverage 70.1%
Lines Hit/Total 463/788
Branches Hit/Total 197/281

Report from native unit tests (ASan + gcov).

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 58.9%
Branch Coverage 70.1%
Lines Hit/Total 465/790
Branches Hit/Total 197/281

Report from native unit tests (ASan + gcov).

1 similar comment
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 58.9%
Branch Coverage 70.1%
Lines Hit/Total 465/790
Branches Hit/Total 197/281

Report from native unit tests (ASan + gcov).

claude added 5 commits October 1, 2026 22:07
Head and tail advance modulo CAPACITY + 1 (one free slot tells full from
empty), but the storage only had CAPACITY slots, so index 8 was written
and read after wrap-around. Size the storage CAPACITY + 1 and add a
fill/dequeue/enqueue/drain wrap-around test (ASan flags the old code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkKZgzzbP5Tq235ytaiKfi
SensorTask only called beginMeasurement() on the solar node. On the
shared NORVI bus that starts the conversion for both sensors, but on
esp32dev each sensor has its own bus, so the pool sensor was read
without a conversion. runDallasMeasurementCycle() now begins both
measurements before the single wait; tests cover both topologies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkKZgzzbP5Tq235ytaiKfi
…zation

SensorSlots copies value and found flag together under a portMUX
critical section (std::mutex natively) and offers snapshot(), so readers
never see a mixed pair; the OLED uses snapshots. Sensor status flags in
DegradationManager and the display redraw/render flags are std::atomic,
and render() resets the redraw flag with exchange() so a request cannot
be lost. Adds a concurrent writer/reader test for SensorSlots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkKZgzzbP5Tq235ytaiKfi
Task start functions return whether xTaskCreatePinnedToCore() succeeded.
decideTaskStartupAction() (TaskStartupPolicy.hpp, tested) defines the
failure path: a missing SensorTask or PublishTask restarts the
controller, a persistent failure ends in boot-loop safe mode; a missing
DisplayTask is logged and the controller continues without OLED.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkKZgzzbP5Tq235ytaiKfi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkKZgzzbP5Tq235ytaiKfi

stritti commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Status of the review points (commits 4d671a7…latest):

  1. TelemetryQueue out of bounds: fixed. The storage now has CAPACITY + 1 slots, matching the modulo indices. A new wrap-around test (fill → dequeue → enqueue → drain, 3 rounds) fails with an ASan stack-buffer-overflow on the old code and passes now.

  2. Dedicated-bus pool conversion: fixed. runDallasMeasurementCycle() (SensorCycle.hpp) calls beginMeasurement() on both nodes before the single wait. On the shared bus the slave's call is a no-op; on dedicated buses both conversions run in parallel. Tests cover both topologies with fake buses.

  3. volatile cross-core: replaced. SensorSlots copies value and found together under a portMUX critical section (std::mutex natively) and adds snapshot(), which the OLED now uses. A concurrent writer/reader test checks that a reader never sees a mixed pair. The DegradationManager sensor flags and the display/render flags are now std::atomic. render() resets the redraw flag with exchange(), so a request can no longer be lost.

  4. Task creation failures: handled. The start() functions return the xTaskCreatePinnedToCore() result. A tested decideTaskStartupAction() defines the path: a missing SensorTask or PublishTask means restart; a persistent failure ends in boot-loop safe mode with all relays off. A missing DisplayTask is logged, and the controller continues without OLED. Documented in docs/multicore-architecture(.de).md.

  5. Behind main: already resolved by the main merge in f8c310a; the base is current main.

  6. PublishTask / state ownership: still open, needs a design decision. The same issue also applies to DisplayTask::render(), which reads loop state such as operationModeNode.getMode() (a String) from Core 0. The options are on their way to the author.


Generated by Claude Code

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage —%
Branch Coverage —%
Lines Hit/Total 0/0
Branches Hit/Total 0/0

Report from native unit tests (ASan + gcov).

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 60.0%
Branch Coverage 70.6%
Lines Hit/Total 488/813
Branches Hit/Total 204/289

Report from native unit tests (ASan + gcov).

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.7%
Branch Coverage 71.6%
Lines Hit/Total 523/848
Branches Hit/Total 214/299

Report from native unit tests (ASan + gcov).

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.7%
Branch Coverage 71.6%
Lines Hit/Total 523/848
Branches Hit/Total 214/299

Report from native unit tests (ASan + gcov).

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.7%
Branch Coverage 71.6%
Lines Hit/Total 523/848
Branches Hit/Total 214/299

Report from native unit tests (ASan + gcov).

@stritti stritti left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Die Architektur geht in die richtige Richtung, ist auf dem aktuellen Head aber noch nicht merge-reif. Die neuen Core-0-Tasks greifen weiterhin auf mutable Core-1-Zustände und auf den Dallas/OneWire-Bus ohne vollständige Ownership-Grenze zu. Zusätzlich ist der DS18B20-Zyklus nicht tatsächlich asynchron und die bisherige 5-s-Recovery-Kadenz geht verloren.

Blockierend:

  1. PublishTask/DisplayTask lesen mutable Loop-Singletons cross-core ohne Snapshot/Lock.
  2. DallasTemperature/OneWire wird nach der Auslagerung weiterhin von Core 1 über Web/MQTT angesprochen.

Weitere funktionale Regressionen:
3. DallasTemperature 4.0.6 wartet standardmäßig bereits in requestTemperatures(); die zusätzliche 800-ms-Wartephase verdoppelt die Blockierzeit des SensorTasks.
4. Bei fehlendem Sensor wird nicht mehr mit RECOVERY_INTERVAL=5 s gemessen, sondern mit loopInterval, standardmäßig 10 s.

Die CI ist grün, kann diese Hardware-/Concurrency-Probleme aber nicht abdecken. Die im PR beschriebenen On-Device-Prüfungen sind außerdem noch offen.

Comment thread src/PublishTask.cpp Outdated
Comment thread src/SensorTask.cpp
Comment thread src/SensorTask.cpp
Comment thread src/SensorTask.cpp Outdated
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.7%
Branch Coverage 71.6%
Lines Hit/Total 523/848
Branches Hit/Total 214/299

Report from native unit tests (ASan + gcov).

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.8%
Branch Coverage 71.8%
Lines Hit/Total 525/850
Branches Hit/Total 216/301

Report from native unit tests (ASan + gcov).

1 similar comment
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.8%
Branch Coverage 71.8%
Lines Hit/Total 525/850
Branches Hit/Total 216/301

Report from native unit tests (ASan + gcov).

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.8%
Branch Coverage 71.8%
Lines Hit/Total 525/850
Branches Hit/Total 216/301

Report from native unit tests (ASan + gcov).

stritti commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Architekturhinweise und Follow-up-Plan

Der aktuelle Head von #170 setzt die entscheidende Multicore-Grenze inzwischen richtig: OneWire/Dallas gehört nach dem Start exklusiv dem SensorTask auf Core 0; der veränderliche Application-/Controller-Zustand bleibt Single-Writer auf Core 1. Diese Ownership-Regel soll beibehalten werden.

Scope von #170

#170 soll nur die sichere Multicore-Basis liefern und nicht gleichzeitig den gesamten Controller architektonisch umbauen. Vor dem Merge sollten deshalb nur noch Inkonsistenzen innerhalb dieses Scopes bereinigt werden:

  • nicht mehr verwendete PublishTask-/DisplayTask-Artefakte entfernen, sofern sie im finalen Scheduler nicht mehr gestartet werden;
  • Kommentare/Doku an das finale Modell angleichen (CoreScheduler startet nur SensorTask);
  • die On-Device-Checkliste bleibt relevant, insbesondere Watchdog, Sensor-Mapping während laufender Messzyklen und Stack-High-Water-Mark.

Architekturregeln für die Folgearbeiten

  1. Mutable Application State hat exakt einen Writer: PoolControllerRuntime/Loop auf Core 1.
  2. Core 0 enthält ausschließlich Sensor-I/O. Kein MQTT, Web, UI, OTA oder Rule-State auf Core 0.
  3. Adapter wie MQTT/Web/UI ändern Zustand nur über Commands und lesen nur immutable Snapshots.
  4. Keine neuen direkten Zugriffe auf globale Nodes aus Adaptern.
  5. Hardwaretreiber kennen keine Business Rules; Rules/Control Engine schalten keine GPIOs direkt.
  6. Board-spezifische #ifdef-Logik soll langfristig in den Platform-Layer wandern.
  7. Cross-Core-Kommunikation erfolgt über begrenzte POD-Snapshots/Queues, nicht über geteilte mutable Objektgraphen.

Geplante Folge-PRs

  • Architecture foundation: typed OperationMode, ControllerCommand, SensorSnapshot, SystemSnapshot und dokumentierte Ownership-Grenzen.
  • Unified command path: MQTT/Web/Local UI erzeugen Commands; Zustandsänderungen werden ausschließlich im Loop verarbeitet.
  • Read model: MQTT/Web/Displays konsumieren SystemSnapshot statt direkt Nodes.hpp/globale Singletons zu lesen.
  • Pure control engine: Rules liefern eine ControlDecision; Relay-I/O wird erst in der Application-Schicht ausgeführt.
  • Sensor bus abstraction: Dallas-Bus/ROM-Mapping von logischen Sensorrollen trennen und komplette Messzyklen als konsistenten Snapshot publizieren.
  • Composition root / platform layer: globale Nodes und statische Application-Manager schrittweise durch explizite Ownership/Dependency Injection ersetzen; Board-spezifische Details isolieren.

Wichtig für die Reihenfolge: Datei-/Verzeichnisverschiebungen erst nach der Entkopplung. Der reine Layer-Ordnerumbau ohne vorherige Dependency-Grenzen bringt wenig und erschwert die Reviews.

Damit bleibt #170 klein genug, um die Concurrency-Änderung separat zu verifizieren, während die strukturelle Entkopplung in eigenständigen PRs reviewbar bleibt.

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.5%
Branch Coverage 71.2%
Lines Hit/Total 519/844
Branches Hit/Total 210/295

Report from native unit tests (ASan + gcov).

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.5%
Branch Coverage 71.2%
Lines Hit/Total 519/844
Branches Hit/Total 210/295

Report from native unit tests (ASan + gcov).

stritti commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Angelegte Architektur-PRs und Reihenfolge

Die Architekturarbeit ist jetzt in eigenständige, reviewbare Schritte zerlegt:

  1. refactor(multicore): remove obsolete worker-task paths #213 refactor(multicore): remove obsolete worker-task paths — direkt auf feat: multicore task architecture #170 gestapelt. Entfernt die aufgegebenen PublishTask-/DisplayTask-Pfade und synchronisiert Kommentare mit dem finalen Ownership-Modell. Dieser PR gehört fachlich noch zu feat: multicore task architecture #170.
  2. refactor(architecture): add typed controller command and snapshot contracts #214 refactor(architecture): add typed controller command and snapshot contracts — Basisverträge: typed OperationMode, ControllerCommand, SensorSnapshot, SystemSnapshot; keine Produktivverhaltensänderung.
  3. refactor(domain): store operation mode as typed enum #215 refactor(domain): store operation mode as typed enum — migriert den internen Mode-State weg vom String, External-/NVS-Werte bleiben kompatibel. Gestapelt auf refactor(architecture): add typed controller command and snapshot contracts #214.
  4. refactor(application): add bounded typed controller command queue #216 refactor(application): add bounded typed controller command queue — feste, heap-freie Queue für Adapter -> Core-1-Anwendungsowner. Gestapelt auf refactor(architecture): add typed controller command and snapshot contracts #214.
  5. refactor(application): add coherent controller snapshot store #217 refactor(application): add coherent controller snapshot store — konsistente komplette Read-Model-Snapshots für Adapter. Gestapelt auf refactor(architecture): add typed controller command and snapshot contracts #214.
  6. refactor(adapters): migrate MQTT, Web and UI to commands and snapshots #218 refactor(adapters): migrate MQTT, Web and UI to commands and snapshots — Draft/OpenSpec für die eigentliche Adaptermigration. Abhängig von refactor(architecture): add typed controller command and snapshot contracts #214/refactor(application): add bounded typed controller command queue #216/refactor(application): add coherent controller snapshot store #217 und dem Ownership-Modell aus feat: multicore task architecture #170.
  7. refactor(domain): make control rules return decisions instead of driving relays #219 refactor(domain): make control rules return decisions instead of driving relays — Draft/OpenSpec für einen reinen Control-Engine-Pfad mit zentraler Actuator-/Safety-Stufe.
  8. refactor(sensors): separate Dallas buses from logical sensor roles #220 refactor(sensors): separate Dallas buses from logical sensor roles — Draft/OpenSpec für physische Dallas-Busse vs. logische Pool-/Solarrollen; Hardware-Verifikation für Shared-/Dedicated-Bus ist explizit erforderlich.
  9. refactor(architecture): introduce composition root and board platform layer #221 refactor(architecture): introduce composition root and board platform layer — Draft/OpenSpec als Abschluss: echte Ownership im PoolControllerContext, Entfernung globaler Nodes/Service-Locator und Isolation der Board-Varianten.

Empfohlene Merge-Reihenfolge

#213 -> #170 -> #214 -> (#215, #216, #217) -> #218 -> (#219, #220) -> #221

#215/#216/#217 sind nach #214 weitgehend unabhängig und können parallel reviewed werden. #219 und #220 sind ebenfalls fachlich getrennt, sollten aber vor #221 abgeschlossen sein.

Die Draft-PRs #218-#221 enthalten absichtlich zunächst OpenSpec/Design/Tasks. Damit sind Scope, Invarianten und Hardware-/Safety-Verifikation festgelegt, bevor Produktivcode in große Refactorings gezogen wird.

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 61.5%
Branch Coverage 71.2%
Lines Hit/Total 519/844
Branches Hit/Total 210/295

Report from native unit tests (ASan + gcov).

stritti commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Review-Nacharbeit zum aktuellen Architekturhinweis ist auf Head 3f00319 umgesetzt.

  • PublishTask, DisplayTask und die veraltete Drei-Task-TaskStartupPolicy wurden entfernt.
  • CoreScheduler besitzt nur noch Lifecycle und Stack-Logging des SensorTask auf Core 0.
  • TelemetryQueue wird direkt durch PoolController::loop() auf Core 1 geleert; MqttPublisher bleibt damit beim Owner des veränderlichen Controller-Zustands.
  • NORVI Button-Handling, UI-State und OLED-Rendering laufen vollständig über DisplayCoordinator auf Core 1.
  • EN/DE-Multicore-Doku, Doxygen-Kommentare und PR-Beschreibung wurden auf dieses finale Ownership-Modell abgeglichen. Die ursprünglichen Drei-Worker-Dokumente sind ausdrücklich nur noch historische Entwurfsunterlagen.
  • Die On-Device-Checkliste bleibt bewusst offen und wurde nicht als verifiziert markiert.

Verifikation für 3f00319:

  • PlatformIO CI: ✅ esp32dev, norvi_ae01_r, olimex_esp32_c6_evb
  • Native Tests + Relay-Safety + Coverage: ✅
  • MegaLinter: ✅ (nur die bereits bestehenden non-blocking Markdown-Befunde außerhalb dieses Changes)
  • CodeQL: ✅

Die weitergehenden Architekturvorschläge aus dem Review (Command Dispatcher / Runtime Snapshot / MQTT-Presentation-Split etc.) behandle ich als separate Follow-up-PRs und nicht als Scope-Erweiterung von #170.

stritti commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Architektur-Follow-up aktualisiert

Die Nacharbeit aus der CI-/Review-Prüfung ist umgesetzt:

Aktualisierte Reihenfolge

#170 -> #214 -> (#215, #216, #217) -> #218 -> (#219, #220) -> #221

#213 entfällt aus der Merge-Reihenfolge, weil seine Änderungen bereits Bestandteil von #170 sind.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants