TL;DR: Write the last-seen timestamp in the same committed transaction as the triage results, and read it back before the next run starts. The re-triage loop happens because the timestamp update is fire-and-forget, runs after the process exits, or targets a table the write never commits to. One read-back check turns a silent 5x re-triage into a visible write failure.

```text
run 5 of 5: re-triaging full backlog - last_seen in db still 2026-09-30
```

1. Query the database for the last-seen value right now and compare it against the last run's completion time. Expected: the stored value is stale or null, confirming the write never lands.
2. Find the timestamp write in the agent code and check when it executes relative to process exit, and whether its transaction commits. Expected: you find the write after the exit path, in an uncommitted transaction, or pointed at the wrong table.
3. Move the write into the main commit path: update last-seen in the same transaction that stores triage results, with a per-batch checkpoint for long runs. Expected: a crash mid-run still preserves progress up to the last completed batch.
4. Add a startup read-back: at run start, fetch last-seen and abort loudly if it is older than the previous run's start. Expected: a failed write pages someone instead of silently re-triaging.
5. Run the job twice. Expected: the second run processes only new or changed findings.

## Use this when
- the backlog gets re-triaged on every run
- last-seen timestamps are stale while runs succeed
- runs complete but the watermark never advances
- the same findings get "first seen" alerts repeatedly

## Not for this skill when
- the timestamp writes fine but the query that reads it is wrong
- findings genuinely change on every run
- the database itself is unreachable
- the watermark advances but the wrong findings get skipped (a filter bug)

## Variant phrasings
- last seen timestamp not updating
- agent reprocesses entire backlog
- triage checkpoint never saved
- watermark stuck triage loop

## Why it happens
The timestamp update sits outside the committed transaction - it fires after the process begins teardown, the connection is already closed, or an exception in cleanup swallows it. Every run therefore starts from the same stale watermark and reprocesses everything.

## Edge cases
- writing last-seen BEFORE processing a batch marks unprocessed items as seen on crash - checkpoint per completed batch, not per started batch
- concurrent agent runs racing on one watermark need row-level locking or per-run watermarks
- clock skew on the database host shifts the watermark - use the database's own clock for the timestamp
- daylight-saving changes do not matter if you store UTC - store UTC always

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_YD-jTy0wWsC8D0lW-YqK4Q
