fix(news): run fetcher in-process + harden dedup; alt-chord CLI keys#81
fix(news): run fetcher in-process + harden dedup; alt-chord CLI keys#81distroinfinity wants to merge 4 commits into
Conversation
…ord cli keys - api: extract crons into scheduler.ts, run in-process from index.ts (RUN_INPROCESS_WORKER, default on). the standalone worker was never deployed, so runFetchTick never ran and news_items stayed empty - api: selection hard-excludes a short-lived "recently offered" set (marked at selection, not impression) and prefers fresh over padding with seen items — fixes the repeating-handful symptom - api: news-health sweep + GET /admin/news-health surface stale/errored sources instead of failing silently - cli: key capture only claims alt/option (meta) chords; bare letters pass through to claude code so typing isn't hijacked; footers, demo hint, docs updated
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Review findingsI reviewed the feature end-to-end: the target is to make news fetch in prod without a separate worker, reduce repeated news batches, surface pipeline health, and stop CLI action keys from colliding with Claude typing. The scheduler extraction and in-process boot path are the right general shape for the single-Railway-service deployment, but I found a few issues that should be fixed before merge.
Verification run locally:
No code changes made. |
…ging addresses the three PR review findings: - cli (#1): add `dtv run -- claude` — runs the child in a Distro-owned PTY so Distro is the sole reader of the terminal and forwards every key except its ⌥ chords to the child. true separation, no byte loss. the daemon skips its own (racy) tty capture for wrapped sessions: DISTRO_PTY=1 in the child env → hooks send idle-start wrapped=true → orchestrator gates keyCapture.start. slot rendering is unchanged (daemon writes to the pty slave, wrapper mirrors it to the real screen). - api (#2): scheduler runs each tick behind a per-bucket redis leader lock, so the in-process scheduler in multiple api instances can't double-fire alerts (fanout had no per-row uniqueness). - api (#3): news:offered is now a ZSET scored by offered-at and pruned per-item, so an active device no longer keeps older offers suppressed until the whole set is cleared — matches the documented ~4h per-item aging. node-pty added as a native dep (shipped like better-sqlite3); `save` added to the socket action vocabulary; docs updated.
# Conflicts: # packages/cli/src/commands/hook.ts
|
Thanks for the review — all three addressed. Pushed in #1 — ignored tty bytes are still consumed (true input separation). You're right that mapper-only filtering can't hand a byte back to Claude. Rather than a half-measure, this now ships real separation via a PTY wrapper: #2 — in-process scheduler can duplicate alert fanout. Each cron tick now runs behind a per-bucket Redis leader lock ( #3 — Also: |
Re-review findingsI re-reviewed PR #81 at
Verification run locally on the updated head:
No code changes made. |
…d save/dismiss re-review follow-ups on the pty/action path: - run (#1): send the child PTY's tty on action events (resolveTtyForPid) so ⌥ chords target that session — two `dtv run` windows no longer cross-fire open/skip/kill on each other's slots. - run (#2): buffer a lone trailing ESC ~25ms, so an ⌥ chord whose ESC and letter land in separate read chunks still fires; a real Escape (no follow-up) still forwards to the child, and CSI/SS3 sequences forward verbatim. - action (#3): register `distro save` and `distro dismiss` — the docs advertise them as race-free fallbacks and the wire protocol accepts them, but the subcommands were missing.
|
Re-review addressed in #1 — actions sent without a tty (multi-window cross-fire). The wrapper now resolves the child PTY's tty ( #2 — split #3 — advertised Typecheck + lint clean; CLI 120 / API 15 tests green; |
Why
Two user-reported issues, audited end-to-end against code + the live prod DB.
news_itemstable was empty and every source hadlast_fetched_at = null— the fetcher only existed in the standaloneworker.ts, but prod only ever starts the HTTP API (node packages/api/dist/index.js). No worker service was deployed, sorunFetchTicknever ran and the CLI fell back to the offline demo slot.d/s/k/...) to actions, so admeant for Claude's prompt could firediscover.What
News pipeline
scheduler.ts; run in-process fromindex.ts(RUN_INPROCESS_WORKER, default on). One Railway service now fetches news; per-source Redis locks prevent double-fetch.news:offered:<device>set (marked at selection, not impression) and prefers fresh items over padding batches with already-seen ones — fixes the "same handful repeats" symptom.news-health.service.ts:*/15Slack sweep +GET /admin/news-healthsurface stale/errored sources instead of failing silently.CLI key capture
ESC+letter) chords trigger actions; bare letters / Enter / Space / Ctrl+C pass through to Claude. Lone ESC still dismisses. Capture already stops on the Stop hook, so next-prompt typing isn't intercepted.Test plan
typecheck+lintclean (api + cli)news_itemsfills +/me/content/nextserves live items⌥Dopens a slot URL and baredreaches Claude