Surfaced by an automated review sweep over the claude telemetry plugin. Not yet verified by the reconcile loop: treat the claim below as a lead with file:line evidence, and reproduce it with a failing-then-passing test before fixing. If it does not reproduce, say so and close.
The claim
hypaware-core/plugins-workspace/claude/src/telemetry/source.js:688 rethrows on a write failure so the exporter retries. The retry is claimed not to recover the batch:
deps.gateway.recordProjectedExchange reaches exchange_writer.js record(), which calls aiGatewayRowsFromProjectedExchange.
- That adds every message's
identity.messageId to the writer's process-lifetime state.seenMessages while building rows (message_projector.js:773), i.e. before storage.appendRows runs.
- If
appendRows throws (disk full, transient Iceberg/spool write error), source.js rethrows, the listener answers HTTP 500, and Claude Code re-POSTs the same batch.
- The second
record() now hits state.seenMessages.has(...) for every message, continues, and returns rows.length === 0.
Claimed consequence: the batch's user prompts and assistant responses are permanently lost (only transcript backfill can recover them), while rowsWritten reports 0 as though there had been nothing to write - a silent loss, not an error.
Acceptance
A regression test that fails on current master: force appendRows to throw once, re-deliver the identical batch, and assert the rows land on the retry. Today they should be dropped. If the dedupe set is only populated after a successful append, the claim is wrong and this issue should be closed with that evidence.
Surfaced by an automated review sweep over the claude telemetry plugin. Not yet verified by the reconcile loop: treat the claim below as a lead with file:line evidence, and reproduce it with a failing-then-passing test before fixing. If it does not reproduce, say so and close.
The claim
hypaware-core/plugins-workspace/claude/src/telemetry/source.js:688rethrows on a write failure so the exporter retries. The retry is claimed not to recover the batch:deps.gateway.recordProjectedExchangereachesexchange_writer.jsrecord(), which callsaiGatewayRowsFromProjectedExchange.identity.messageIdto the writer's process-lifetimestate.seenMessageswhile building rows (message_projector.js:773), i.e. beforestorage.appendRowsruns.appendRowsthrows (disk full, transient Iceberg/spool write error),source.jsrethrows, the listener answers HTTP 500, and Claude Code re-POSTs the same batch.record()now hitsstate.seenMessages.has(...)for every message,continues, and returnsrows.length === 0.Claimed consequence: the batch's user prompts and assistant responses are permanently lost (only transcript backfill can recover them), while
rowsWrittenreports 0 as though there had been nothing to write - a silent loss, not an error.Acceptance
A regression test that fails on current
master: forceappendRowsto throw once, re-deliver the identical batch, and assert the rows land on the retry. Today they should be dropped. If the dedupe set is only populated after a successful append, the claim is wrong and this issue should be closed with that evidence.