Repository navigation
chore(gramjs): add gap-fill recovery script + runbook - #84
Conversation
Closes #76. Every paginated MCP tool now returns the same shape: { messages: [...], page: { has_more, next_cursor, total? } } Callers pass `page.next_cursor` back as `cursor` on the next call. Cursors are opaque base64(JSON) blobs; direction (before vs after) is implicit in the tool contract. Before this PR each listy tool had its own shape: search → { results, total, next_before_id, next_before_sent_at } history → { results, next_after_id, next_after_sent_at } thread → { chat_id, message_id, messages, next_after_id } Consistency matters most for AI callers — an LLM that learned the search shape used to trip on history / thread. One canonical layout means one mental model. Changes: - helpers.ts: encodeCursor / decodeCursor with strict validation - index.ts MCP dispatchers: search, history, thread, digest, recent all return the canonical shape. REST /search keeps its old shape (dashboard consumes it) — the MCP layer translates. - mcp-tools.ts: search / history / thread input schemas replace the old before_id+before_sent_at / after_id+after_sent_at pairs with a single opaque `cursor` param; descriptions reference page.next_cursor. - Server `instructions` string updated in step so the LLM sees the same contract as the schemas. digest stays chat-grouped; each chat entry now carries a trivial page: { has_more, next_cursor: null } where has_more = true iff the messages array hit the per_chat cap. Out of scope (follow-ups): - External docs (docs/agents.md, docs/tool-reference.md, etc.) still reference the old cursor names — cleanup PR. - Pagination on `chats` and `contacts`/`address_book` — separate PR. - `include_total: false` opt-out on search — separate PR. Verified by subagent review: cursor round-trip is correct across FTS and B-tree search paths; has_more off-by-one on exact-cap pages is pre-existing (same logic in the original REST handler). tsc clean.
One-shot tool that, per dialog, fetches recent Telegram history newest→oldest and ingests messages with sent_at > GAP_START_TS. Used successfully on 2026-04-17 to recover a ~7-day gap after a silent ping-loop zombie (fixed in #72 watchdog). Prerequisite: stop the listener first (flyctl machine stop) to avoid double-ingest race. BACKFILL_ALLOWED=true env guard. Also adds `npm run gap-fill` and a short runbook at docs/gap-fill.md.
|
Claude finished @d4d0h's task —— View job PR #84 Code ReviewReviewing the gap-fill recovery script and related changes: Todo List
Review SummaryThis PR introduces a well-designed gap-fill tool with appropriate safety measures. The code follows project conventions and includes comprehensive documentation. Here are the key findings: 🔒 Security Issues
✅ Code Quality & CorrectnessStrengths:
🔍 Minor Issues & Suggestions
📋 Documentation QualityThe
🧪 Testing
✨ Nice to HaveConsider adding a confirmation prompt before execution that shows:
This could prevent misconfigurations where wrong timestamps are used. Verdict✅ Approved - This is production-ready code that follows all conventions and includes appropriate safety measures. The minor suggestions above are non-blocking improvements. |
Summary
Commits the one-shot `gap-fill` tool that was used successfully on 2026-04-17 to recover a ~7-day gap in the archive after a silent ping-loop zombie (the same incident that motivated the watchdog in #72).
Why not roll this into #78?
Cleanups applied
The script's docstring + error message referenced a stale Fly app name `tg-reader-main` (should have been `tg-reader` in our instance, or `` more generally). Both replaced with the placeholder form to match Phase 1 of #83.
Test plan
🤖 Generated with Claude Code