docs(protocol): record runtime architecture conformance

This commit is contained in:
liu weikai
2026-06-14 12:41:44 +08:00
parent 6e8254c18b
commit c43eaaf107
2 changed files with 82 additions and 22 deletions
+24 -6
View File
@@ -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
@@ -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 进入
runtimeESP32 与 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()` 表达。协议决策在 runtimeroute 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