1 Commits
Author SHA1 Message Date
f981f70a2f fix(extract): deterministic atom slug — stop cross-day + trailing-dash duplicate atoms (#2482)
extract_atoms minted duplicate atom pages two ways:

A. Trailing-dash twins. The local slugger truncated the title at 60 chars
   with no re-strip, so a cut landing on a hyphen left a trailing dash
   (`…would-`). The FS-import normalizer (slugifySegment) strips it
   (`…would`), so the same atom persisted under two slugs and the
   dedup-by-slug check never collapsed them.

B. Cross-day re-mint. The slug used the run date (todayDate()), while the
   idempotency guard keys on the whole-file source_hash. Append-only
   sources (chat/transcript exports) grow daily, so the file hash changes,
   the guard never matches, the source is re-extracted, and each re-mint
   lands under a new date prefix → a new slug → no upsert → a duplicate.

Fix: make the atom slug a deterministic function of stable inputs —
`atoms/<source-date>/<stem>-<title-hash>`:
- source date is parsed from the source ref (transcript filename / page
  slug), not the run date, so re-extraction converges on the same slug and
  putPage upserts instead of duplicating;
- the 6-char title hash keeps two atoms whose titles share the first 60
  chars on distinct slugs (no silent clobber of a different atom);
- the stem routes through the canonical slugifySegment and re-strips a
  trailing dash, so the two write paths can no longer disagree.

The whole-file source_hash batch check is retained only as a cost fast-path
(skip re-running the model on an unchanged source); correctness no longer
depends on it.

Adds a hermetic PGLite regression test (no DATABASE_URL) asserting the
source-dated prefix, title-hash suffix, trailing-dash strip, and upsert on
re-extraction of a grown append-only transcript.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 19:53:13 -07:00