HarperFast / HarperFast/harper
MQTT subscription publish drops the delete/put `type` discriminator — deletes arrive as a bare null
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
## Summary
The MQTT publish path drops the `type` discriminator that the subscription listener already computed, so an MQTT subscriber cannot distinguish a **delete** from an **update whose value is null** — both arrive as a bare `null` payload. SSE and the REST WebSocket path forward the full envelope and are correct; the loss is MQTT-only.
## Mechanism (verified on `origin/main` @ `fb762a365`)
`server/DurableSubscriptionsSession.ts:352-358` forwards only `update.value` (plus `update.version` separately) to the MQTT wire listener:
```js
const result = await this.listener(
resourcePath + '/' + path,
update.value, // <-- update.type is never passed
messageId,
subscriptionRequest,
update.version
);
```
`resources/Table.ts`'s `subscribe()` listener has already computed `type` (`put` / `delete` / `invalidate`) by this point — it is discarded here rather than being unavailable.
Contrast `server/REST.ts:504-508` (the WebSocket path), which passes the whole `result.value` envelope — `type` included — into `serializeMessage()`.
## Blast radius
MQTT subscribers only. Every delete on every subscribed table. A subscriber maintaining a local materialized view has no way to tell "this record was deleted" from "this record's value is now null", so it either keeps deleted records or drops null-valued ones.
## Suggested fix
Forward the discriminator on the MQTT path the way the WS path does, rather than reconstructing it from a null value at the client. The fix site is the single call above.
*Found by the qa-explorer QA loop (finding F-181); dup-searched across `harper` and `harper-pro` in all states with six phrasings — nothing tracks this today.*
Contributor guide
Research direction
Start in server/DurableSubscriptionsSession.ts:352-358, then inspect the subscription listener in resources/Table.ts and the contrasting WebSocket path in server/REST.ts:504-508. Verify the MQTT publish path preserves the computed put/delete/invalidate discriminator, and test that deletes remain distinguishable from updates whose value is null.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nodejs
- Domain
- backend, distributed-systems
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 90/100