Skip to content

feat(drive): remove read-only mirroring — every chat is write+stream - #90

Merged
Pher217 merged 1 commit into
mainfrom
claude/2026-06-27-no-readonly
Jun 27, 2026
Merged

feat(drive): remove read-only mirroring — every chat is write+stream#90
Pher217 merged 1 commit into
mainfrom
claude/2026-06-27-no-readonly

Conversation

@Pher217

@Pher217 Pher217 commented Jun 27, 2026

Copy link
Copy Markdown
Owner

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-control always 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: removed session.event/session.line ingestion handlers + dispatch. Kept the pty_*/headless_* drive handlers and the shared deliver_turn/record_turn.
  • observe/: deleted the transcript-ingestion layer (observer.py, parser.py, runtimes/, run_session_observer + close_stale_topics commands, 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 /stop observed-session special case.
  • settings/base.py: dropped ingestion-only OBSERVE_RUNTIMES/OBSERVER_RUNTIME/OBSERVE_*; kept OBSERVE_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). Deleted discovery.py + tailer.py; trimmed the daemon CLI args.

Deploy

  • Removed the session-observer docker-compose service and the OBSERVE_RUNTIMES plumbing from run-daemon.sh + env example; updated READMEs.

Approach / consequence

  • /openremote-control already dispatched a driveable headless session (PR feat(connectors): /openremote-control dispatches a driveable chat (write+stream) #87) — unchanged. It streams clean turns via session.headless_reply.
  • A raw orc run TUI 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_reply auth gate (from_user_id allowlist) fires before any lookup; the non-driveable guard ordering is unchanged. Removing the informational bounce weakens no auth invariant.

Test summary

  • Backend full suite: 520 passed, 3 skipped (uv run pytest). Ruff clean on changed files.
  • Host-agent: drive E2E + suite green except 2 pre-existing test_wsclient.py failures (byte-identical to main — the known flaky host-agent test, not introduced here).
  • Updated/deleted tests that exercised removed read-only behavior; rewrote the kept delivery tests to build fixtures directly.

Follow-ups (not in this PR)

  • Optional: rename the observe app → a neutral name (it now holds only the shared delivery engine).
  • Extend write+stream to Codex / OpenCode (per the "one by one" plan).
  • Windows daemon supervisor.

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.
@Pher217
Pher217 merged commit 4ad601d into main Jun 27, 2026
1 check passed
@Pher217
Pher217 deleted the claude/2026-06-27-no-readonly branch June 27, 2026 22:56
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