{ "planPath": "docs/plans/2026-07-24-mqtt-sparkplug-driver.md", "note": "Wave 2 MQTT/Sparkplug B driver. Two phases both in-scope: P1 (plain MQTT, Tasks 0-14) is a complete shippable milestone; P2 (Sparkplug B ingest, Tasks 15-26) builds on it and every P2 task chains back to Task 14. Write-through (NCMD/DCMD/plain publish) is DEFERRED (design P3). Hard rules: Task 0 (MQTTnet-5/net10 restore+build under central pinning with transitive pinning OFF) is the mandatory FIRST gate. Hand-roll Tahu proto over ONE MQTTnet-5 client (NOT SparkplugNet \u2014 its MQTTnet-4.x transitive pin collides with the repo's deliberately-OFF CentralPackageTransitivePinning). DriverType string is 'Mqtt' everywhere (grep-enforced). Enum-serialization trap: every JSON seam uses shared JsonSerializerOptions with JsonStringEnumConverter. Fixtures enforce TLS+auth, never anonymous/public-broker. Google.Protobuf (3.34.1) + Grpc.Tools (2.76.0) already pinned; MQTTnet is the one new pin. High-risk tasks: 4 (hand-rolled reconnect loop), 18/19 (alias/seq state-machine components), 21 (the full 3.6 ingest matrix).", "tasks": [ { "id": 0, "subject": "Task 0 (P1): Dependency-validation spike \u2014 MQTTnet-5 pin + net10 restore/build under central pinning", "status": "completed", "classification": "standard", "parallelizableWith": [] }, { "id": 1, "subject": "Task 1 (P1): .Contracts enums + MqttDriverOptions DTO (name-serialized)", "status": "completed", "classification": "small", "parallelizableWith": [], "blockedBy": [ 0 ] }, { "id": 2, "subject": "Task 2 (P1): .Contracts MqttTagDefinition + MqttEquipmentTagParser (plain, strict enum)", "status": "completed", "classification": "standard", "parallelizableWith": [], "blockedBy": [ 1 ] }, { "id": 3, "subject": "Task 3 (P1): .Driver + MqttConnection connect/TLS/auth (bounded deadline)", "status": "completed", "classification": "standard", "parallelizableWith": [], "blockedBy": [ 1 ] }, { "id": 4, "subject": "Task 4 (P1): MqttConnection hand-rolled reconnect loop (backoff + resubscribe)", "status": "completed", "classification": "high-risk", "parallelizableWith": [], "blockedBy": [ 3 ] }, { "id": 5, "subject": "Task 5 (P1): LastValueCache + IReadable (per-ref no-data)", "status": "completed", "classification": "small", "parallelizableWith": [ 6 ], "blockedBy": [ 3 ] }, { "id": 6, "subject": "Task 6 (P1): ISubscribable plain topic subscribe\u2192OnDataChange + retained seed", "status": "completed", "classification": "standard", "parallelizableWith": [ 5 ], "blockedBy": [ 2, 4 ] }, { "id": 7, "subject": "Task 7 (P1): MqttDriver shell \u2014 IDriver + authored-only ITagDiscovery (Once) + probe interface", "status": "completed", "classification": "standard", "parallelizableWith": [], "blockedBy": [ 5, 6 ] }, { "id": 8, "subject": "Task 8 (P1): MqttDriverProbe CONNECT handshake", "status": "completed", "classification": "small", "parallelizableWith": [ 10 ], "blockedBy": [ 3 ] }, { "id": 9, "subject": "Task 9 (P1): Factory + DriverTypeNames.Mqtt + Host factory/probe registration", "status": "completed", "classification": "standard", "parallelizableWith": [], "blockedBy": [ 7, 8 ] }, { "id": 10, "subject": "Task 10 (P1): .Browser bespoke #-observation browser (passive)", "status": "completed", "classification": "standard", "parallelizableWith": [ 8 ], "blockedBy": [ 2 ] }, { "id": 11, "subject": "Task 11 (P1): Register MqttDriverBrowser (bespoke-first for Mqtt)", "status": "completed", "classification": "trivial", "parallelizableWith": [ 12 ], "blockedBy": [ 10 ] }, { "id": 12, "subject": "Task 12 (P1): Typed AdminUI editor + validator (plain shape)", "status": "completed", "classification": "standard", "parallelizableWith": [ 11 ], "blockedBy": [ 2 ] }, { "id": 13, "subject": "Task 13 (P1): Mosquitto+JSON-publisher fixture (TLS+auth) + env-gated live suite", "status": "completed", "classification": "standard", "parallelizableWith": [], "blockedBy": [ 9 ] }, { "id": 14, "subject": "Task 14 (P1): Live /run verify on docker-dev \u2014 P1 MILESTONE COMPLETE", "status": "completed", "classification": "small", "parallelizableWith": [], "blockedBy": [ 9, 11, 12, 13 ] }, { "id": 15, "subject": "Task 15 (P2): Vendor Tahu sparkplug_b.proto + Grpc.Tools codegen", "status": "completed", "classification": "standard", "parallelizableWith": [], "blockedBy": [ 14 ] }, { "id": 16, "subject": "Task 16 (P2): SparkplugCodec decode + golden payload vectors", "status": "completed", "classification": "standard", "parallelizableWith": [ 17 ], "blockedBy": [ 15 ] }, { "id": 17, "subject": "Task 17 (P2): SparkplugTopic parse/format + SparkplugDataType.ToDriverDataType map", "status": "completed", "classification": "small", "parallelizableWith": [ 16 ], "blockedBy": [ 15 ] }, { "id": 18, "subject": "Task 18 (P2): BirthCache + AliasTable \u2014 bind-by-name, rebuild-per-birth", "status": "completed", "classification": "high-risk", "parallelizableWith": [ 19 ], "blockedBy": [ 16, 17 ] }, { "id": 19, "subject": "Task 19 (P2): SequenceTracker seq-gap + bdSeq death-tie", "status": "completed", "classification": "high-risk", "parallelizableWith": [ 18 ], "blockedBy": [ 16, 17 ] }, { "id": 20, "subject": "Task 20 (P2): RebirthRequester NCMD encode", "status": "completed", "classification": "small", "parallelizableWith": [], "blockedBy": [ 16 ] }, { "id": 21, "subject": "Task 21 (P2): Sparkplug ingest state machine \u2014 the 3.6 matrix", "status": "completed", "classification": "high-risk", "parallelizableWith": [], "blockedBy": [ 18, 19, 20 ] }, { "id": 22, "subject": "Task 22 (P2): ITagDiscovery UntilStable + IRediscoverable (rediscover-on-DBIRTH)", "status": "completed", "classification": "standard", "parallelizableWith": [], "blockedBy": [ 21 ] }, { "id": 23, "subject": "Task 23 (P2): Sparkplug browser tree + AttributesAsync + scoped RequestRebirthAsync", "status": "completed", "classification": "standard", "parallelizableWith": [ 24 ], "blockedBy": [ 20, 21 ] }, { "id": 24, "subject": "Task 24 (P2): AdminUI editor Sparkplug mode shape + validator", "status": "completed", "classification": "small", "parallelizableWith": [ 23 ], "blockedBy": [ 12, 17 ] }, { "id": 25, "subject": "Task 25 (P2): C# edge-node simulator fixture + 3.6 live matrix", "status": "completed", "classification": "standard", "parallelizableWith": [], "blockedBy": [ 21, 22, 23 ] }, { "id": 26, "subject": "Task 26 (P2): Live /run verify Sparkplug \u2014 P2 COMPLETE", "status": "completed", "classification": "small", "parallelizableWith": [], "blockedBy": [ 23, 24, 25 ] } ], "lastUpdated": "2026-07-27", "closureNote": "All 27 tasks shipped, both phases live-gated. Marked complete 2026-07-27 (deferment.md \u00a78.5)." }