Handbook
Messaging consumer boundary
forge-lcdl exposes atomic, product-agnostic read operations for Outlook Web (OWA) and Microsoft Teams Web. Consumers (for example forge-cockpit-web) attach an authorized browser session, call governed tasks or connector…
Updated
LCDL must not import consumer repos or know about Cockpit workspaces, guided 10k harvest caps, UI step labels (Edge / Scan / Open), or checkpoint paths under var/data/.
Plaud voice memory is out of scope for this Playwright extension — consumers use filesystem/CLI adapters instead.
Atomic capability catalog — Outlook
| Capability | Governed task_id |
Input (summary) | Output (summary) |
|---|---|---|---|
| Mailbox health | outlook_probe_mail_health |
session_ref, optional url |
ready, list_row_count_estimate, sign_in_required, hints |
| Folder inventory | outlook_list_folders |
session_ref |
folder items |
| Visible inbox sample | outlook_list_messages_viewport |
session_ref, limit |
items[] (~viewport) |
| Large folder scroll | outlook_list_messages_scroll |
session_ref, max_scroll_rounds |
items[] |
| Chunked inbox (resume) | outlook_list_inbox_chunk |
session_ref, chunk_size, checkpoint, optional query.date_*, query.list_strategy |
items[], next_cursor, chunk.has_more |
| Inbox list (Graph REST) | outlook_list_inbox_rest |
session_ref, top, skip / next link |
items[], folder_total_count, extra.is_unread |
| Thread / reading pane bodies | outlook_read_conversation_thread |
session_ref, url, row_index, optional query.thread_id, preserve_unread |
items[] with body_text |
| Download message (.eml) | outlook_download_message_eml |
session_ref, row_index, dest_path (must end .eml) |
ok, byte_length; rejects .msg |
| Downloads poll + move | outlook_download_disk (wait_for_stable_eml_in_downloads, adopt_download_file, cleanup_owa_eml_left_in_downloads) |
Consumer staging path | Moves OWA export from Edge ~/Downloads into dest_path; see OUTLOOK-EML-EXPORT-DUAL-ARCHITECTURE.md |
| URL normalize | outlook_normalize_item_url |
url |
item_id, normalized URL |
| Layout detect | outlook_apply_read_layout |
session_ref |
layout hints (v1 detect only) |
Evaluate scripts live under src/forge_lcdl/playwright/scripts/outlook_readonly.py (for example harvest_list_rows, restore_row_unread). See Outlook read strategies and Outlook list strategies.
Read state and list strategy (decision tree)
- Unread / read flags on list rows → LCDL (
extra.is_unread, optionalstate); consumers map to localis_readonly. - Restore OWA unread after conversation open → LCDL
outlook_read_conversation_thread+preserve_unread; not consumer Playwright. - How inbox rows are listed or folder totals → LCDL list strategy (OUTLOOK-LIST-STRATEGIES.md); consumers pass
list_strategy/ env only. - SQLite badges and sync copy → consumer (
wire_item_to_cockpit_fields, progress UI).
| Task | Observes read | Mutates OWA read |
|---|---|---|
outlook_list_inbox_rest |
is_unread |
No |
outlook_list_inbox_chunk (DOM path) |
is_unread |
No |
outlook_read_conversation_thread |
before click | Yes (list_row); optional restore |
list_row_count_estimate (not a harvest plan)
outlook_probe_mail_health may return list_row_count_estimate: a viewport DOM estimate of visible list rows (often ~30–40 on a typical inbox), not total mailbox size and not a consumer download quota. When Graph REST stats succeed, prefer folder_total_count and unread_count for mailbox size. Consumers should display viewport estimate as “rows visible now” and keep harvest caps in orchestration.
Atomic capability catalog — Teams
| Capability | Governed task_id |
Input (summary) | Output (summary) |
|---|---|---|---|
| Surface health | teams_probe_health |
session_ref, optional url |
ready, hints |
| Chat list | teams_list_chats |
session_ref |
chat items |
| Channel list | teams_list_channels |
session_ref |
channel items |
| Chunked chat messages | teams_list_chat_messages_chunk |
session_ref, chunk_size, checkpoint, query.container |
items[], next_cursor, chunk.has_more |
| Chunked channel posts | teams_list_channel_posts_chunk |
session_ref, chunk_size, checkpoint |
post items |
| Thread replies | teams_read_thread_replies |
session_ref, url, thread context |
reply items |
Evaluate scripts: src/forge_lcdl/playwright/scripts/teams_readonly.py. See Teams read strategies.
Wire shape
List tasks emit ReadResult-compatible JSON: items[] as UnifiedItem wire dicts (item_id, title, body_preview, actor, url, thread_id, participants, extra, …). Consumers map via their bridge (wire_item_to_cockpit_fields in Cockpit).
What LCDL must not know
| Out of scope for LCDL | Belongs in consumer |
|---|---|
unified_message, message_channel, sync_run |
Cockpit SQLite + APIs |
| Guided 10k / channel cap 100 | Cockpit sync_service budgets |
harvest_phase, detail_status, thread file layout |
Cockpit message_storage |
Checkpoint files under var/data/outlook_chunk_* |
Cockpit outlook_sync_state |
Sync UI steps (browser, scan, detail) |
Cockpit SyncProgressPanel |
| Stable sync stat grid / heartbeat filtering | Cockpit syncProgressDisplay.ts (LCDL only supplies raw progress_message) |
Message-ID .eml paths, Downloads adopt index |
Cockpit eml_paths.py, eml_adopt_downloads.py |
| Plaud CLI, diarization, recordings | Cockpit cockpit_server/plaud/ |
Consumer responsibilities
- Session — Resolve generic Edge CDP (
forge_lcdl.playwright.edge_cdp_attach), attach with a new tab,register_playwright_page(session_ref, page). On attach, LCDL applies CDP focus emulation when bring-to-front is off so OWA/Teams lists render without stealing the operator's Edge window. See EDGE-CDP-ATTACH.md. - Composition (LEGO) — Run atoms in order (probe → chunk loop → optional detail pass).
- Mapping — Bridge converts wire → upsert kwargs (
source_message_key, folders, metadata JSON). - Orchestration — Progress heartbeats, cancel, caps, pruning, audit events.
Example consumer: forge-cockpit-web — see that repo’s docs/messaging-lcdl-boundary.md (orchestration only; no new selectors in connectors/*.py).
Migration status (forge-cockpit-web)
| Area | LCDL atoms | Cockpit status |
|---|---|---|
| Outlook routine + guided harvest | probe + inbox chunk + conversation thread | Production via lcdl_outlook_bridge |
| Outlook DOM PoC fallback | — | Off by default (COCKPIT_OUTLOOK_DOM_FALLBACK=0); legacy in outlook_dom_legacy.py |
| Teams chat harvest | probe + chat messages chunk | Production via lcdl_teams_bridge |
| Teams DOM PoC fallback | — | Off by default (COCKPIT_TEAMS_DOM_FALLBACK=0); legacy in teams_dom_legacy.py |
| Outlook People directory | outlook_people_* tasks |
Production via people_harvest.py (sole org surface; Org Explorer removed) |
| Plaud | N/A (CLI) | Cockpit plaud.py only |
Org directory boundary: ORG-DIRECTORY-CONSUMER-BOUNDARY.md.