Let several readers share a room without sharing one unread cursor.
A local coordination backend for keeping messages, room policy and each consumer's reading position across restarts. It grew from the historical Python workbench's multi-participant rooms: a rendered chat view and a model's context window both needed to be replaceable without losing the conversation's records. This extraction keeps the file-backed backend and CLI; the original UI and model-process connectors are outside the package. Origin.
Use it as a storage layer for your own reader, worker or context builder. It never contacts a model or messaging service.
Python 3.11 or newer, from the checkout:
python -m pip install -e .
python -m examples.walkthroughWalkthrough source · Captured synthetic output
Two synthetic messages arrive for Analyst and Reviewer. Analyst acknowledges message 2; a new service instance reports Analyst has 0 unread while Reviewer still has 2. Excluding message 1 produces visible IDs [2] while canonical IDs remain [1, 2].
The example then deliberately breaks the compiled view. A canonical lens still returns message 2, compile_chat rebuilds the visible file, and Reviewer's cursor stays unchanged until an explicit acknowledgement. Final verification reports no structural issues. The demonstration is offline and uses a disposable temporary directory.
from pathlib import Path
from agent_chatroom_ledger.service import ChatroomService
service = ChatroomService(Path("./my-rooms"))
room = service.create_chat("Synthetic Review", participants=["Reviewer"])
room_id = room["state"]["chat_id"]
service.post_message(room_id, "Reviewer", "A synthetic observation.")
lens = service.render_chat_lens(room_id)
# Process or display lens["messages"] successfully before acknowledging.
service.mark_seen("Reviewer", Path("./reviewer.cursor.json"), room_id,
message_id=lens["last_message_id"])The recipe creates local files. Keep one cursor file per independent consumer. Reading or rendering does not mark anything seen; advance a cursor only after your consumer has handled the intended range. The example acknowledges the latest canonical position intentionally, including excluded records; if that is unsuitable for your application, select the acknowledged ID explicitly.
| State | Responsibility and entry point |
|---|---|
| Canonical message records | ChatroomStore appends records and allocates message IDs under its room lock. load_chat reads these records. |
| Visible room file | compile_chat builds a projection from messages plus compile policy. It is replaceable. |
| Context selection | exclude_messages, include_messages and stand-ins change the compiled view; exclusion does not erase the original message. |
| Independent progress | scanner compares room state with consumer cursors; mark_seen persists acknowledgement. |
| Application boundary | ChatroomService exposes the operations; your application supplies transport, scheduling and model execution. |
The CLI reference covers room creation, archive/restore, placement, message versions and verification. Administrative CLI operations default to a dry run and need --apply to write. Python service calls are direct operations; they are not implicitly dry runs.
Participant gating selects which rooms a consumer sees; it is not user authentication. Messages and cursors are plaintext. Use trusted callers and an access-controlled directory. A lock coordinates writers, but the room consists of several files; this is not a transactional database or a network replication protocol. A broken compiled view can be rebuilt only while canonical messages and state remain intact.
A useful next integration would adapt one real consumer to read a lens, process it, and acknowledge only on success. That adapter and live model connectors are not included or demonstrated. Message Version Bundle Builder isolates the related numbered-context-view mechanism if you only need that part.
python -m pip install pytest
python -m pytest -qExcluded historical host-connector tests remain explicitly skipped; backend checks and the runnable example use synthetic local data. MIT license · Security boundary