Review follow-ups on the external-write mirror:
- Idle trigger: `openGroupChat` now runs `sweepExternalGroupWrites`, one
`session.resume` per stored member session whose thread the room still
shows, then the existing mirror. A Bot posting into its own room session
between rounds (the reporter's scenario) is posted the moment the user
opens the room, not only once the room next drives that member and it
happens to be a responder. No polling; a room mid-round is left to the
round, which sweeps its responders itself.
- Classifier: the single `[System:` skip becomes the canonical synthetic
user-row set (mirrors `agent/context_compressor.py::
_SYNTHETIC_USER_ROW_PREFIXES`, comment links both) plus the gateway's
`display_kind` on typed scaffolding rows. A compaction handoff, cron
delivery, delegation result or steer marker is never mirrored as member
speech, and the assistant row reacting to it closes the exchange instead
of inheriting the previous writer's origin.
- Stranded harvest picks the FIRST substantive assistant row after the
header-prefixed prompt and stops at the next outside user row, so a CLI
answer written after the late reply is no longer posted as the turn reply
and then mirrored again.
- Cursor edges (documented in the module header): first sight of a session
seeds the cursor at the transcript's current length — a room hydrated from
the gateway mirror (which carries no cursors) on a second Desktop does not
re-post its history; "late, never lost" holds from that moment on, at the
cost of no history replay. A cursor past the end after compaction is
reset to the end. A missing snapshot leaves the cursor alone.
Tests stay at 2 in group-external-writes.test.ts: the negative control now
also opens the room without a drive and asserts the peer exchange arrives
while the compaction/cron/auto-continue rows and their answers do not. Red
with the `openGroupChat` call removed and with the prefix set reverted.