Prompt: Teams live read tests (1 message vs 50) — Edge CDP

Use this prompt in a new Cursor chat to implement and run governed LCDL + Cockpit tests for Microsoft Teams (chats, channels, thread structure), mirroring the Outlook inbox chunk work.

Updated


Goal

Add read-only Playwright coverage for Teams on a pre-authorized Edge session (CDP), with scenario tests that:

  1. Read exactly 1 message/post (newest or first visible in the target list).
  2. Read up to 50 messages/posts in one chunk (scroll-harvest), with the same field parity as the 1-message case for the top row.
  3. Map results through Cockpit using the same bridge pattern as cockpit_server/lcdl_outlook_bridge.pyharvest_inbox_chunk_via_lcdl / run_outlook_sync (Teams equivalent).

Do not use Graph APIs; use OWA/Teams web UI only.


Environment (operator)

  • Edge with remote debugging, signed into Teams and Outlook if needed.
  • Default CDP: http://127.0.0.1:9223 (adjust if different).
  • forge-lcdl: pip install -e ".[dev,playwright]" in .venv.
  • forge-cockpit-web: pip install -e . with forge-lcdl on path.
export FORGE_LCDL_PLAYWRIGHT_LIVE=1
export FORGE_LCDL_PLAYWRIGHT_CDP_URL=http://127.0.0.1:9223
export COCKPIT_OUTLOOK_LIVE=1   # reuse live flag pattern or add COCKPIT_TEAMS_LIVE=1
export COCKPIT_CDP_URL=http://127.0.0.1:9223

Existing code to extend (do not reinvent)

Area Location
Teams connector (HTML snapshot parsers) forge-lcdl/src/forge_lcdl/playwright/connectors/teams.py
Outlook chunk + merge pattern outlook_chunk.py, outlook_list_row.py, outlook_readonly.py
Governed task pattern tasks/outlook_read_v1.py, tasks/packs/install.py
Cockpit bridge forge-cockpit-web/cockpit_server/lcdl_outlook_bridge.py
Live CDP fixture tests/integration/ms_playwright.py, test_outlook_inbox_chunk_cdp_live.py
Field compare script scripts/compare-outlook-chunk-message-fields.py

Deliverables

1. LCDL Teams read tasks (v1)

Implement governed tasks (contracts + run_*_v1) analogous to Outlook:

Task Purpose
teams_probe_health Teams app loaded, signed in, list surface visible
teams_list_chats 1:1/group chat list inventory
teams_list_channels Team/channel tree or channel list for a team
teams_list_chat_messages_chunk Chunked harvest in a chat (chunk_size 1 / 50)
teams_list_channel_posts_chunk Chunked harvest in a channel
teams_read_thread_replies Replies under a post (structure for threading)

Use evaluate_readonly scripts (new teams_readonly.py) where DOM is stable; keep HTML snapshot parsers as fallback.

2. UnifiedItem fields (minimum per message/post)

Ensure wire + Cockpit mapping includes consistently:

  • item_id, thread_id / reply_to_id (if visible)
  • title (subject or channel post title)
  • body_preview (list row); optional body_text after thread open
  • actor (author display name)
  • container (e.g. Chat/{name} or Channel/{team}/{channel})
  • url (deep link when extractable from DOM)
  • created_at (ISO only) + extra.received_hint for non-ISO UI dates
  • source_kind distinguishing chat vs channel vs reply

Apply merge_*_list_row semantics so chunk_size=1 and chunk_size=50 return the same top-row fields (copy outlook_list_row.py pattern).

3. Tests

Test file Mode
tests/playwright/test_teams_chunk_scenarios.py Offline fakes: 1 + 50, task + connector
tests/integration/test_teams_chunk_cdp_live.py Live CDP; skip without FORGE_LCDL_PLAYWRIGHT_LIVE=1
forge-cockpit-web/tests/test_teams_inbox_chunk_scenarios.py Bridge offline
forge-cockpit-web/tests/e2e/test_lcdl_teams_bridge_guided.py Live guided (COCKPIT_TEAMS_LIVE=1)

Parity assertion: items[0] from chunk_size=1 equals items[0] from chunk_size=50 for the same session/URL.

4. Cockpit sync

  • Add lcdl_teams_bridge.py + wire connectors/teams.py (or messaging sync) to call teams_list_*_chunk in a loop with checkpoint file under var/data/teams_chunk_checkpoints/.
  • Reuse wire_item_to_cockpit_fields or a Teams-specific mapper that shares datetime coercion (outlook_coerce_received_at → rename to shared coerce_ui_datetime if needed).

5. Scripts

  • scripts/run-teams-chunk-scenarios.sh (offline + live, .venv python, CDP 9223 default)
  • scripts/compare-teams-chunk-message-fields.py (dump JSON report like Outlook)

Acceptance criteria

  • [ ] Offline pytest green for 1 + 50 scenarios and parity test.
  • [ ] Live: 1-message test passes on signed-in Teams; 50-message test passes or skips with clear message if fewer rows visible.
  • [ ] Compare script reports no first_message.* discrepancies between chunk 1 and chunk 50 for the same top item_id.
  • [ ] Cockpit bridge parity test passes offline; live chunk e2e passes for chunk_size=1.
  • [ ] Document entry URLs in test docstrings (e.g. https://teams.microsoft.com/v2/ + chat deep link env FORGE_LCDL_TEAMS_CHAT_URL).

Run loop (agent)

Fix failures up to 10 attempts per test; after code changes run:

cd forge-lcdl
.venv/bin/python -m pytest tests/playwright/test_teams_chunk_scenarios.py -q
FORGE_LCDL_PLAYWRIGHT_LIVE=1 FORGE_LCDL_PLAYWRIGHT_CDP_URL=http://127.0.0.1:9223 \
  .venv/bin/python -m pytest tests/integration/test_teams_chunk_cdp_live.py -v --tb=short

Reference: one-message field dump (Outlook, live)

After Outlook fixes, a single inbox row exposes (LCDL wire → Cockpit):

LCDL Example / notes
item_id OWA data-convid stable id
thread_id Usually same as item_id for mail
title Subject line
actor.display_name Sender
body_preview List preview (not full MIME body)
container Mail/Inbox
url Often mailbox URL until item deep link is parsed
created_at ISO from <time datetime> when present
extra.received_hint e.g. 5/28/2026 when not ISO
Cockpit title_guess, sender_guess, body, owa_item_id_raw, received_at, received_hint Via wire_item_to_cockpit_fields

Full body requires a follow-up task (outlook_read_conversation_thread); list chunk is intentionally preview-only.


Policy (mandatory)

  • Read-only only: allowed operations are navigate, snapshot, evaluate_readonly, and scroll_readonly.
  • Forbidden: click (except future allowlisted read navigation), type, fill, send, reply, call, upload, delete, reactions, or any compose/send UI automation.
  • Tasks and contracts must set "read_only": true in capabilities.
  • Live tests must skip (not fail) when zero rows are visible — operator opens a chat with history first (FORGE_LCDL_TEAMS_CHAT_URL).
  • No secrets in logs or committed JSON reports.
  • pytest --import-mode=importlib is already in pyproject.toml to avoid tests/playwright vs pip playwright clash; keep that for new Teams tests under tests/playwright/.