* feat(convo): preserve authored timestamp from transcripts
Conversation drawers only carried `filed_at` (ingest time), so a bulk
re-mine collapsed every drawer to a single instant and the chronological
signal was lost — even though each Claude Code / Codex JSONL line already
carries an ISO-8601 `timestamp`. The recency-window fallback and any
date-aware consumer then saw ingest order, not when content was written.
- convo_miner: derive `authored_at` (per-file max line `timestamp`) and
store it as drawer metadata; falls back to `filed_at` when absent
- searcher: surface `authored_at` in search results, and break exact
hybrid-score ties toward the more recently authored drawer (ISO strings
sort chronologically; missing dates sort oldest) — benchmark-neutral as
it only reorders exact ties
- tests: cover `_extract_authored_at` (latest wins, skips/tolerates lines
without timestamps, non-jsonl/missing -> None) and the tie-break
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(search): surface authored_at in CLI + backfill for existing data
Completes the authored_at work so the field is visible end-to-end and
existing palaces can adopt it without re-mining.
- layers: CLI `search` output shows an `authored:` date line per result
(peer of the existing date; markdown drawers fall back to filed_at)
- scripts/backfill_authored_at.py: in-place migration that stamps
authored_at on convos drawers from their source transcripts — metadata
only (no re-embedding), idempotent, dry-run by default
- docs/authored-at.md: documents created_at (ingest) vs authored_at
(written) and both backfill paths (in-place / drop-and-recreate)
- tests: backfill integration tests over an ephemeral ChromaDB collection
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(search): address review — non-string timestamp guard + top-level authored_at tiebreak
Two correctness fixes from the PR review:
- _extract_authored_at: only compare when the parsed `timestamp` is a str.
A non-string timestamp on a malformed/foreign JSONL line previously raised
TypeError outside the try and could crash the mine.
- _hybrid_rank: the tie-break read `authored_at` only from nested `metadata`,
but the search_memories path (MCP / Claude Code) carries it at the top level
of each hit — so the tie-break silently no-op'd there. Read both shapes.
- tests: non-string timestamp cases, and a top-level-shape tie-break test
(which fails before this fix).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* style: apply ruff format to authored_at changes
CI ruff format --check flagged 4 files; ruff check already passed.
Formatting only — no behavior change.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Igor Lins e Silva <4753812+igorls@users.noreply.github.com>