feat(drive): remove read-only mirroring — every chat is write+stream - #90
Merged
Conversation
Read-only observe/mirror topics violated the core requirement that every chat surface be driveable. A VSCode-extension session can't be injected into, so observing it could only ever produce a read-only Telegram topic. This removes the entire read-only ingestion path and keeps only the driveable headless/PTY drive path, so /openremote-control always hands you a write+stream session. Backend: - hostlink/consumers.py: drop session.event/session.line handlers + dispatch; keep pty_*/headless_* drive handlers and the shared deliver_turn/record_turn. - observe/service.py: keep record_turn (shared by the headless drive path); remove get_or_create_observed_thread/apply_session_meta. - observe/: delete the transcript-ingestion layer (observer, parser, runtime adapters, run_session_observer + close_stale_topics commands, their tests). Keep the streaming/delivery engine (delivery.py, formatting.py, validators). - telegram/service.py: replace the read-only "bounce" (which listed other driveable topics) with a single "doesn't accept typed input" message; drop the /stop observed-session special case. - settings: drop ingestion-only OBSERVE_RUNTIMES/OBSERVER_RUNTIME/OBSERVE_* vars; keep OBSERVE_DELIVERY_MODE (drive-stream formatting). Host-agent: - daemon.py: remove the JSONL poll loop (the read-only producer); the daemon now only runs the ws drive loop (headless.prompt / pty.inject / session.start). - delete discovery.py + tailer.py (poll-loop only); trim cli daemon args. Deploy: - remove the session-observer docker-compose service and the OBSERVE_RUNTIMES plumbing from run-daemon.sh and the env example; update READMEs. Auth gate and the non-driveable guard ordering in handle_forum_reply are unchanged. Backend suite 520 passed; host-agent drive E2E green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Goal
Enforce the hard requirement that every chat surface is driveable (write + stream) — never read-only. Read-only mirror topics existed only because a VSCode-extension session can't be injected into, so observing it could produce nothing but a read-only Telegram topic. This rips out the entire read-only ingestion path and keeps only the driveable headless/PTY drive path, so
/openremote-controlalways hands you a write+stream session.Scope: Claude only for now (per direction — get the basic write+stream solid first, then extend Codex/OpenCode one by one).
What changed
Backend
hostlink/consumers.py: removedsession.event/session.lineingestion handlers + dispatch. Kept thepty_*/headless_*drive handlers and the shareddeliver_turn/record_turn.observe/: deleted the transcript-ingestion layer (observer.py,parser.py,runtimes/,run_session_observer+close_stale_topicscommands, and their tests). Kept the shared streaming/delivery engine (delivery.py,formatting.py,validators.py,service.record_turn) — the headless drive path streams through it.telegram/service.py: replaced the read-only "bounce" (_list_drivable_topics/_topic_link) with a single "doesn't accept typed input" message; dropped the/stopobserved-session special case.settings/base.py: dropped ingestion-onlyOBSERVE_RUNTIMES/OBSERVER_RUNTIME/OBSERVE_*; keptOBSERVE_DELIVERY_MODE(drive-stream formatting).Host-agent
daemon.py: removed the JSONL poll loop (the read-only producer). The daemon now only runs the ws drive loop (headless.prompt/pty.inject/session.start). Deleteddiscovery.py+tailer.py; trimmed thedaemonCLI args.Deploy
session-observerdocker-compose service and theOBSERVE_RUNTIMESplumbing fromrun-daemon.sh+ env example; updated READMEs.Approach / consequence
/openremote-controlalready dispatched a driveable headless session (PR feat(connectors): /openremote-control dispatches a driveable chat (write+stream) #87) — unchanged. It streams clean turns viasession.headless_reply.orc runTUI session no longer has a clean-output stream (its turns previously came from JSONL observation). Drive via/openremote-control(headless) for write+stream. This matches the documented headless-is-the-default decision.Security
Independent review (reviewer agent): no HIGH/security findings, drive path intact. The
handle_forum_replyauth gate (from_user_idallowlist) fires before any lookup; the non-driveable guard ordering is unchanged. Removing the informational bounce weakens no auth invariant.Test summary
uv run pytest). Ruff clean on changed files.test_wsclient.pyfailures (byte-identical to main — the known flaky host-agent test, not introduced here).Follow-ups (not in this PR)
observeapp → a neutral name (it now holds only the shared delivery engine).