Every Slack message shows a zero timestamp of 0001-01-01T00:00:00Z #257

Closed
opened 2026-08-24 00:58:32 +02:00 by clawbot · 2 comments
Collaborator

Verified during the deployability audit by inspecting the raw payload posted to a sink. Every Slack delivery contains:

*Timestamp:* `0001-01-01T00:00:00Z`

while the stored event's created_at is correct.

Cause: buildEventFromTask never populates CreatedAt, and FormatSlackMessage formats the resulting zero value.

Slack is the target type whose output a human actually reads, so this is visible in every notification the product sends through it.

Definition of done:

  • buildEventFromTask populates CreatedAt from the stored event.
  • The Slack payload renders the event's real receipt time.
  • A test asserting a non-zero, correct timestamp in the formatted message. Audit the other fields buildEventFromTask populates while there — check whether anything else is silently zero.
Verified during the deployability audit by inspecting the raw payload posted to a sink. Every Slack delivery contains: ``` *Timestamp:* `0001-01-01T00:00:00Z` ``` while the stored event's `created_at` is correct. Cause: `buildEventFromTask` never populates `CreatedAt`, and `FormatSlackMessage` formats the resulting zero value. Slack is the target type whose output a human actually reads, so this is visible in every notification the product sends through it. Definition of done: - `buildEventFromTask` populates `CreatedAt` from the stored event. - The Slack payload renders the event's real receipt time. - A test asserting a non-zero, correct timestamp in the formatted message. Audit the other fields `buildEventFromTask` populates while there — check whether anything else is silently zero.
clawbot added this to the 1.0.0 milestone 2026-08-24 00:58:36 +02:00
Author
Collaborator

Plan.

Task carries no receipt time, and it is built in three places — two of them in internal/handlers, which is outside this unit's scope. Widening Task would therefore only fix the recovery path and leave the live first-attempt and retry paths still rendering the zero time, so the fix goes in the engine instead: resolveEventBody becomes hydrateEvent and reads created_at alongside body from the stored event row. That covers every path, since both processNewTask and processRetryTask already call it, and it keeps the stored row as the single source of truth.

One behavioural note: a task that inlined its body previously never read the event row at all. It does now, so a read failure gets a fallback — deliver the inlined body with the timestamp unset rather than drop the event, since the row can be reaped by retention while a queued delivery still holds its body.

Field audit and verification go in the PR body.

Plan. `Task` carries no receipt time, and it is built in three places — two of them in `internal/handlers`, which is outside this unit's scope. Widening `Task` would therefore only fix the recovery path and leave the live first-attempt and retry paths still rendering the zero time, so the fix goes in the engine instead: `resolveEventBody` becomes `hydrateEvent` and reads `created_at` alongside `body` from the stored event row. That covers every path, since both `processNewTask` and `processRetryTask` already call it, and it keeps the stored row as the single source of truth. One behavioural note: a task that inlined its body previously never read the event row at all. It does now, so a read failure gets a fallback — deliver the inlined body with the timestamp unset rather than drop the event, since the row can be reaped by retention while a queued delivery still holds its body. Field audit and verification go in the PR body.
Author
Collaborator

Done in #297 (branch issue-257-slack-timestamp, base next). Rationale and the full field audit are in the PR body.

Verified by reproducing first on unmodified next: a slack target pointed at a sink, raw payload captured, *Timestamp:* `0001-01-01T00:00:00Z` on all three paths — first attempt with the body inlined, first attempt with the body read back from the row, and retry. After the fix the same sink receives *Timestamp:* `2026-03-04T05:06:07Z`, the event's seeded created_at, on all three.

Five new tests in internal/delivery/event_timestamp_test.go, one of them asserting directly on FormatSlackMessage output over a Task-reconstructed event. Mutation-verified: removing the CreatedAt population fails four of the five, and the fifth is the reap-fallback test that correctly still passes.

make check green with GOFLAGS=-count=1; 21 packages ok with no cached results, and lint executed in Docker at 0 issues. rather than replaying a cached stage.

Done in https://git.eeqj.de/sneak/webhooker/pulls/297 (branch `issue-257-slack-timestamp`, base `next`). Rationale and the full field audit are in the PR body. Verified by reproducing first on unmodified `next`: a slack target pointed at a sink, raw payload captured, `*Timestamp:* `0001-01-01T00:00:00Z`` on all three paths — first attempt with the body inlined, first attempt with the body read back from the row, and retry. After the fix the same sink receives `*Timestamp:* `2026-03-04T05:06:07Z``, the event's seeded `created_at`, on all three. Five new tests in `internal/delivery/event_timestamp_test.go`, one of them asserting directly on `FormatSlackMessage` output over a Task-reconstructed event. Mutation-verified: removing the `CreatedAt` population fails four of the five, and the fifth is the reap-fallback test that correctly still passes. `make check` green with `GOFLAGS=-count=1`; 21 packages `ok` with no cached results, and lint executed in Docker at `0 issues.` rather than replaying a cached stage.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#257