Skip to content

feat(telegram): add capture ingress queue with atomic ledger persistence (Slice 1.1R-B) - #80671

Closed
danielbeckinho wants to merge 3 commits into
NousResearch:mainfrom
danielbeckinho:feat/slice-1.1r-b-capture-ingress
Closed

feat(telegram): add capture ingress queue with atomic ledger persistence (Slice 1.1R-B)#80671
danielbeckinho wants to merge 3 commits into
NousResearch:mainfrom
danielbeckinho:feat/slice-1.1r-b-capture-ingress

Conversation

@danielbeckinho

Copy link
Copy Markdown

Slice 1.1R-B — Capture-first intake

Installs a capture-aware asyncio.Queue at PTB ApplicationBuilder(...).update_queue(...) that intercepts message-like Telegram updates before PTB dispatch, guaranteeing durable pre-acknowledgment recording for capture-only routes.

What changed

  • capture_ingress.py (new) — CaptureIngressQueue(asyncio.Queue) intercepts put(), routes by (chat_id, thread_id), atomically persists ledger row + companion payload, terminal deny on capture_only routes
  • adapter.py — immutable profile field set at construction, _build_capture_queue() reads capture_routes config and wires queue via builder.update_queue(), extracted _register_handlers() helper
  • gateway/run.py — profile stamped on adapter construction
  • capture_ingest.py (live file, not in repo) — CAPTURE_OK:0 output on zero-capture runs

Test evidence

Suite Tests Result
capture ingress 25 PASS
group gating (regression) 23 PASS
multiplex adapter 16 PASS
Total 64 PASS

Architecture

Update queued → CaptureIngressQueue.put()
  ├─ sentinel / non-message-like → super().put() (pass through)
  ├─ route not found / mode=drop → super().put() (unchanged dispatch)
  ├─ mode=capture_only → atomic persist, consume silently (terminal deny)
  └─ mode=agent → atomic persist, then super().put() (delegate)

Non-goals (not in this PR)

  • No live deployment, config, cron, or service changes
  • No media byte download/storage
  • No change to drop_pending_updates cold-start behavior

Authorization

Hermes control-plane Slice 1.1R-B, PRs #19 (plan) and #20 (authorization record, merged 2026-08-06T20:55Z). Scoped TDD implementation authorized; merge remains owner-gated.

Closes: control-plane envelope 1.1R-B

danielbeckinho and others added 3 commits August 7, 2026 00:11
…ice 1.1R-B)

capture_ingress.py: CaptureIngressQueue (asyncio.Queue subclass)
- Intercepts PTB Updates before dispatch via super().put() for pass-through
- Idempotent pre-ack ledger insert with atomic companion payload
- Terminal capture-only dispatch deny (commands → inert text)
- Agent routes: persist-then-delegate pattern
- build_event_id(profile, account_id, update_id) matches ingress-ledger schema
- canonicalize_payload() for deterministic sorted-key UTF-8 JSON

20 tests: event_id, canonicalization, atomic insert, idempotency,
conflict-rejection, text/command/media/location capture, pass-through,
agent delegation, drop route, route matching, sentinel pass-through
…asks 10-11)

TelegramAdapter takes an optional keyword-only `profile`, exposed read-only
via `.profile`; unset falls back to the process-active profile. gateway/run.py
stamps it in `_configure_profile_adapter` (the existing per-profile hook —
adapters are built by the plugin registry factory, not constructed in run.py).

Capture ingress keys ledger event_ids on this value, so it must not drift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…Slice 1.1R-B Task 12)

- capture_ingress: add CAPTURE_SCHEMA + open_capture_db() so the ledger DDL
  lives with the module that owns the tables (it previously existed only
  inline in the test file).
- capture_ingress: normalize telegram.Update objects via to_dict() before
  inspection, delegating the original object. PTB enqueues Update instances,
  not dicts, so without this the queue passed every real update straight
  through and captured nothing.
- adapter: _build_capture_queue() reads platforms.telegram.extra.capture_routes
  and installs the queue via builder.update_queue() at both build sites.
  Returns None when no routes are configured — a queue with an empty route map
  is a pure passthrough, so we skip it and skip creating the DB file.
- adapter: extract the duplicated handler registration into _register_handlers().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@danielbeckinho
danielbeckinho deleted the feat/slice-1.1r-b-capture-ingress branch August 7, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant