From c43eaaf107cf376014e5fb42b97b366e40ba7151 Mon Sep 17 00:00:00 2001 From: liu weikai Date: Sun, 14 Jun 2026 12:41:44 +0800 Subject: [PATCH] docs(protocol): record runtime architecture conformance --- docs/audits/PROTOCOL_ADAPTER_DRIFT_AUDIT.md | 30 ++++++-- .../PROTOCOL_RUNTIME_DESIGN_SPEC.md | 74 +++++++++++++++---- 2 files changed, 82 insertions(+), 22 deletions(-) diff --git a/docs/audits/PROTOCOL_ADAPTER_DRIFT_AUDIT.md b/docs/audits/PROTOCOL_ADAPTER_DRIFT_AUDIT.md index adbaf973..6584d000 100644 --- a/docs/audits/PROTOCOL_ADAPTER_DRIFT_AUDIT.md +++ b/docs/audits/PROTOCOL_ADAPTER_DRIFT_AUDIT.md @@ -11,7 +11,9 @@ Primary root cause: - ESP32 and nRF platform adapters historically owned too much protocol business logic. - The current protocol decisions covered by the runtime migration are delegated to shared runtime/policy helpers. -- The remaining gaps are platform execution, storage, projection, and intentionally advertised capability differences. +- Standard protocol use-case orchestration now enters through `MeshProtocolFacade` / `ProtocolRuntimeBundle`; + remaining differences are platform execution, storage, projection, MeshCore-specific runtime extensions, and + intentionally advertised capability differences. | Area | Status | Risk | Notes | | --- | --- | --- | --- | @@ -19,9 +21,9 @@ Primary root cause: | Meshtastic broadcast `want_response` | Shared policy in working tree | Medium | ESP32 and nRF now share the app-data destination/ACK/response decision; broadcast air ACK is suppressed while request response intent is preserved. | | Meshtastic request/reply core | Shared runtime/policy, platform IO remains | Medium | NodeInfo/Position reply gating, NodeInfo/Position payload construction, direct Position/Waypoint share packet decisions, TraceRoute/Position outgoing request packet decisions, TraceRoute reply gating, TraceRoute payload mutation, and TraceRoute/Position action lifecycle tracking are shared; radio send still lives in adapters/UI executors. | | Meshtastic duplicated policy ownership | Shared runtime/policy, platform IO remains | Medium | App-data send intent, NodeInfo reannounce gate, NodeInfo/Position reply gates, NodeInfo self-announcement packet construction, Position/Waypoint payload construction, direct Position/Waypoint share packet decisions, TraceRoute/Position outgoing request packet decisions, TraceRoute reply gate, TraceRoute payload mutation, TraceRoute/Position result lifecycle, and PKI/NO_CHANNEL resync decisions now live in shared runtime/policy. Radio IO and local data sources still live in adapters. | -| MeshCore NodeInfo query/reply | Shared runtime/effects | Medium | ESP32 and nRF now route `requestNodeInfo()` through `MeshCoreRuntime` and shared control payload codecs; platform adapters still own packet IO/projection. | -| MeshCore trace | Shared lifecycle, platform-limited routing | Medium | ESP32 and nRF use native `PAYLOAD_TYPE_TRACE` and shared completion/timeout policy; nRF still uses a minimal one-hop hash route. | -| MeshCore app-data ACK/capability | Shared lifecycle | Medium | ESP32 and nRF now declare ACK tracking only when runtime pending/completion handling is wired. ACK frame scheduling remains adapter IO. | +| MeshCore NodeInfo query/reply | Facade -> shared runtime/effects | Medium | ESP32 and nRF now route standard NodeInfo use-cases through `MeshProtocolFacade -> MeshCoreRuntime` and shared control payload codecs; platform adapters still own packet IO/projection. | +| MeshCore trace | Facade -> shared lifecycle, platform-limited routing | Medium | ESP32 and nRF use native `PAYLOAD_TYPE_TRACE` and shared completion/timeout policy through the standard facade path; nRF still uses a minimal one-hop hash route. | +| MeshCore app-data ACK/capability | Shared runtime extension | Medium | ESP32 and nRF now declare ACK tracking only when runtime pending/completion handling is wired. ACK registration/completion is still a MeshCoreRuntime extension; ACK frame scheduling remains adapter IO. | | MeshCore direct routing/identity | Runtime-owned policy, capability-split execution | Medium | ESP32 advertises direct route table, identity keys, peer secret derivation, and rich trace projection; nRF advertises identity keys and peer secret derivation only. Missing-key and auto-discover decisions now go through `MeshCoreRuntime` effects/state. | | MeshCore duplicated/incomplete ownership | Shared runtime/policy, platform IO remains | Medium | MeshCore NodeInfo, discover, trace lifecycle, ACK lifecycle, direct route policy, and direct secret expansion are shared; route/pubkey/private-key storage, frame IO, and platform projections remain in adapters. | | Capability granularity | Fine-grained flags added | Medium | Coarse legacy fields remain for compatibility; new flags describe NodeInfo, Position, TraceRoute, app response, and ACK tracking separately. | @@ -37,6 +39,9 @@ Target architecture: - Shared Meshtastic runtime core owns NodeInfo, Position, TraceRoute, ACK/request-response, and PKI resync policy. - Shared MeshCore runtime core owns NodeInfo control frames, discover decisions, trace, ACK tracking, route/identity policy, and app-data semantics. +- `MeshProtocolFacade` plus `ProtocolRuntimeBundle` now provide the standard protocol use-case orchestration + boundary. UI paths use capture-mode projection; platform adapters use execute-mode projection when they need to + write `Publish*` / route effects into platform queues. - `PROTOCOL_RUNTIME_DESIGN_SPEC.md` now defines the required Strategy / Command / State / Bridge / Adapter architecture for this migration. - ESP32 and nRF adapters own physical IO, SDK lifecycle, platform storage, timers, buffers, and queues only. @@ -253,6 +258,10 @@ Current state: - NodeInfo query/info decision mapping is shared by `MeshCoreRuntime`. - NodeInfo control payload layout is shared by `meshcore_payload_helpers`. +- ESP32 and nRF standard `requestNodeInfo()` paths now enter through + `MeshProtocolFacade -> ProtocolRuntimeBundle -> MeshCoreRuntime`. +- Incoming NodeInfo control handling enters runtime through `handleIncomingPacket(...)`, which carries explicit + `PacketHandling` semantics before effects are executed. - ESP32 and nRF differ only in how the resulting `SendNodeInfoEffect` is transmitted and projected. Residual risk: @@ -277,6 +286,8 @@ Expected behavior: Current state: - Trace base payload build/decode and lifecycle policy are shared. +- Standard trace request / terminal incoming trace / tick lifecycle orchestration now enters through + `MeshProtocolFacade` on ESP32 and nRF MeshCore adapters. - ESP32 still owns richer route scheduling and BLE `TraceData` projection. - nRF declares `supports_trace_route_request` for its minimal native MeshCore trace path, but not richer route projection. @@ -296,6 +307,8 @@ Expected behavior: Current state: - ACK pending/completed/timeout lifecycle is shared. +- ACK registration, binding, and incoming ACK completion remain explicit `MeshCoreRuntime` extension calls until the + generic facade grows an ACK-signature use-case API. They are runtime state calls, not adapter-local state machines. Residual risk: @@ -323,6 +336,9 @@ Current state: - Incoming MeshCore discover request/response decisions now run through `MeshCoreRuntime`: request filter/since matching emits `SendDiscoverResponseEffect`, and responses emit `PublishNodeInfoEffect` plus `UpdatePeerRouteEffect`. +- Standard incoming discover control execution now enters through `MeshProtocolFacade` with + `ProtocolProjectionPolicy::ExecuteAppFacing`, so platform route/contact projections are executed without the + adapter reinterpreting protocol semantics. - ESP32 receive-side missing-peer auto-discover now uses `MeshCoreRuntime` for the peer-hash validity and cooldown decision table; the adapter only executes the discover effect and reports TX success back to runtime. - MeshCore direct shared-secret expansion and nRF peer-key derivation now use `MeshCoreDirectSecretCore`; @@ -339,6 +355,8 @@ Residual risk: transmission remain platform-owned. - Direct text/app-data missing-key discover side effects now enter the same `DiscoverIntent -> MeshCoreRuntime -> SendDiscoverRequestEffect` path as user-initiated discovery. +- ESP32 detailed discovery result mapping remains platform-owned because `MeshActionResult` exposes product-specific + failure detail not represented by `MeshProtocolFacadeResult`. - nRF can derive identity/peer secrets for request payloads through the shared helper but does not implement the ESP32 direct route table. @@ -365,8 +383,8 @@ Residual risk: 2. Move MeshCore peer route storage / pubkey persistence state toward shared runtime ownership only if future behavior requires cross-platform route-table parity; today it is an advertised ESP32 capability difference. 3. Keep ACK burst scheduling, concrete frame transmission, private identity persistence, BLE `TraceData` - projection, and GPS/source selection as platform execution unless a future spec promotes one into shared - protocol state. + projection, detailed discovery result mapping, and GPS/source selection as platform execution unless a future + spec promotes one into shared protocol state or facade API. ## Guardrail diff --git a/docs/specification/PROTOCOL_RUNTIME_DESIGN_SPEC.md b/docs/specification/PROTOCOL_RUNTIME_DESIGN_SPEC.md index 03590b1f..bac144dc 100644 --- a/docs/specification/PROTOCOL_RUNTIME_DESIGN_SPEC.md +++ b/docs/specification/PROTOCOL_RUNTIME_DESIGN_SPEC.md @@ -177,8 +177,15 @@ Facade 不得: - 变成新的 God Object 或 Mediator。 Facade 的职责是隔离上层与协议编排。上层不应该直接拼 `ProtocolIntent` 后调用 runtime, -也不应该直接遍历 `ProtocolEffects` 后调用 executor。过渡期允许少量平台 adapter 仍然这样做, -但每个 active UI / ChatService 入口必须有迁入 `MeshProtocolFacade` 的计划和测试。 +也不应该直接遍历 `ProtocolEffects` 后调用 executor。active UI / ChatService 入口必须迁入 +`MeshProtocolFacade` 或本规格明确命名的等价边界。 + +`MeshProtocolFacade` 默认捕获 `EmitActionResultEffect`、`PublishIncomingTextEffect`、 +`PublishIncomingDataEffect`、`PublishNodeInfoEffect` 等 app-facing projection,让 UI 可以从 +`MeshProtocolFacadeResult` 读取结果而不把 projection 当平台 IO 执行。平台 adapter 需要把这些 +projection 写入平台队列、路由表或 contact projection 时,必须显式选择 +`ProtocolProjectionPolicy::ExecuteAppFacing`,这样 projection 处理策略是组合配置,而不是散落的 +if 分支。 ### Effect @@ -227,12 +234,18 @@ facts provider 接起来。这里使用 Abstract Factory / Factory Method 的意 ```cpp struct ProtocolRuntimeBundle { - MeshProtocolFacade* facade; + MeshProtocol protocol; IProtocolRuntime* runtime; IProtocolEffectExecutor* executor; + const IProtocolRuntimeContextProvider* contextProvider; + + MeshProtocolFacade createFacade(ProtocolProjectionPolicy policy); }; -ProtocolRuntimeBundle protocolRuntimeFor(MeshProtocol protocol); +ProtocolRuntimeBundle protocolRuntimeFor(MeshProtocol protocol, + const ProtocolRuntimeSelection& selection, + IProtocolEffectExecutor& executor, + const IProtocolRuntimeContextProvider& contextProvider); ``` Factory 可以知道产品/平台能力,但不得决定协议语义。它选择“用哪套 runtime/executor”,不决定 @@ -253,6 +266,10 @@ enum class PacketHandling }; ``` +代码级边界是 `IProtocolRuntime::handleIncomingPacket(...) -> IncomingPacketHandlingResult`。 +旧的 `handleIncoming(...) -> ProtocolEffects` 可以保留为兼容入口,但 active facade path 必须调用 +`handleIncomingPacket(...)`,并且 shared tests 必须断言 `PacketHandling`。 + Meshtastic incoming chain 的概念顺序必须是: 1. wire/Data 解码与 channel / PKI 解密事实建立; @@ -328,7 +345,7 @@ classDiagram class RecordingProtocolExecutor class ProtocolRuntimeFactory { - +protocolRuntimeFor(MeshProtocol protocol) ProtocolRuntimeBundle + +protocolRuntimeFor(MeshProtocol protocol, selection, executor, contextProvider) ProtocolRuntimeBundle } class MeshtasticCodec @@ -427,7 +444,7 @@ sequenceDiagram Platform->>Adapter: raw packet/frame Adapter->>Adapter: decode/decrypt enough to form IncomingPacket facts Adapter->>Facade: handleIncoming(IncomingPacket) - Facade->>Runtime: handleIncoming(IncomingPacket) + Facade->>Runtime: handleIncomingPacket(IncomingPacket) Runtime->>Chain: decrypt/classify/route handlers alt NodeInfo request Chain->>State: apply NodeInfo rule @@ -641,18 +658,43 @@ radio execution details. The question "is this local send still pending, complet 分支维持。 8. 如果规格中的核心类名只存在于 Mermaid 图中,而代码里没有真实对象或等价声明,则该项未完成。 -## Current Implementation Gaps +## Implementation Status -截至本规格日期,runtime / effect 主干已经部分落地,但以下缺口仍然阻止本规格被判定为完成: +截至 2026-06-14,本规格中的核心 runtime architecture 已落成代码对象,并由 shared smoke / +platform build 覆盖: -1. `MeshProtocolFacade` 还没有真实代码对象。当前若干 active UI / platform path 仍直接构造 - `ProtocolIntent`、调用 `IProtocolRuntime`、遍历 `ProtocolEffects` 或手动处理 TX failure 回灌。 -2. Product composition 还没有统一的 factory / bundle 边界。平台代码各自持有 runtime / executor, - 组合逻辑仍可能继续散落。 -3. Incoming handler chain 尚未以 `PacketHandling` 或等价返回语义显式化。部分平台 adapter 仍保留 - 巨型 decode/classify 分支,虽然其中若干协议决策已经下沉到 runtime/policy。 -4. `RecordingProtocolExecutor` 已覆盖 effect 可记录性,但还缺少 `MeshProtocolFacade` 级别的 smoke, - 用来证明 UI / ChatService 可以只通过 facade 发起 action 并观察 app-facing result。 +1. `IProtocolRuntime`、`MeshtasticRuntime`、`MeshCoreRuntime` 是真实代码对象; + `trailmate_meshtastic_runtime_smoke` 和 `trailmate_meshcore_runtime_smoke` 覆盖 outgoing / + incoming / tx result / tick。 +2. `MeshProtocolFacade` 是真实代码对象;`trailmate_mesh_protocol_facade_smoke` 覆盖 send text、 + trace、NodeInfo、position、incoming、tx result、tick,以及 UI capture / platform execute 两种 + projection policy。 +3. `ProtocolRuntimeBundle`、`ProtocolRuntimeSelection`、`protocolRuntimeFor(...)` 是真实 product + composition 边界;`trailmate_protocol_runtime_factory_smoke` 覆盖 protocol selection、 + invalid protocol、context provider 更新和 platform-style projection execution。 +4. nRF mono UI、Linux uConsole 的 active protocol use-case path 已经通过 facade / factory 进入 + runtime;ESP32 与 nRF MeshCore adapter 的标准 NodeInfo、discover、incoming、tx result、tick path + 也通过 facade / factory 进入 runtime。 +5. Incoming handler chain 已以 `PacketHandling` / `IncomingPacketHandlingResult` 显式化; + `MeshtasticRuntime` 和 `MeshCoreRuntime` 都由 `handleIncomingPacket(...)` 组织 handler 顺序, + shared tests 直接断言 `HandledStop` / `NotHandled`。 +6. `MeshAdapterProtocolEffectExecutor` 和 platform adapters 只执行 `ProtocolEffect`。执行层可以选择 + projection policy,但不得重新决定协议语义。 + +### Accepted Runtime Extensions + +以下保留项不是 legacy adapter 逻辑,而是目前尚未纳入通用 facade API 的 MeshCore runtime extension: + +- MeshCore app ACK registration / binding / incoming ACK completion 仍通过 + `MeshCoreRuntime::trackAppAck(...)`、`bindAppAckToMessage(...)`、`handleAppAck(...)` 表达。ACK burst + frame scheduling 和 multi-ACK frame construction 仍是 adapter IO。 +- ESP32 receive-side missing-peer auto-discover 的 cooldown state 仍通过 + `prepareAutoDiscoverMissingPeer(...)`、`markAutoDiscoverMissingPeerTxResult(...)`、 + `resetAutoDiscoverState()` 表达。协议决策在 runtime,route cache 和 radio scheduling 仍在 adapter。 +- ESP32 detailed discovery result (`MeshActionResult`) 仍使用 runtime effects 后由 adapter 映射成 + platform-specific detailed result;通用 `MeshProtocolFacadeResult` 目前不承诺替代该产品级结果类型。 + +这些 extension 可以在未来提升为 facade use-case API,但在提升前不得在 adapter 中复制协议决策表。 ## Migration Rules