Files
brain-of-reese/.agent/phases/complete/51_share_chat/00_phase.md
T
ducoterra 4971e2859d
Build and Push Containers / build-and-push-app (push) Successful in 12s
Build and Push Containers / build-and-push-db (push) Successful in 10s
chore(agent): track .agent/ planning tree in git
Remove the blanket .agent/ gitignore so the phase roadmap, user
stories, reports, and PLAN.md are versioned with the code. Only
runtime artifacts (.agent/phase-sessions/, .agent/pipeline.log)
remain ignored. Update AGENTS.md git protocol rule to match.
2026-09-01 10:18:22 -04:00

4.0 KiB

Phase 51 — Share a Chat by Link (Anonymous View)

Source: TODO.md L6 — "Need a way to share a chat with a link so others can see it anonymously." Story: n/a (TODO-derived — owner roadmap confirmation 2026-08-29) Context: Builds on phase 50's saved_chats rows: sharing is a token on a saved chat. The public surface is a new anonymous page /shared/<token> (a real route — the static mount cannot serve a dynamic path) rendering a read-only copy of the conversation through the same record shape (thinking block, tool lines, source chips, stopped note) — no composer, no controls. The documents API is admin-only (phase 16), so a guest's source chips are plain text (owner-locked). The cache-busting middleware treats known HTML paths (the HTML_PAGES tuple in app/core/caching.py) as revalidate + ?v=-rewrite pages; the shared page joins that contract by path prefix.

Objective

The owner can turn a saved chat into a public link (/shared/<token>); anyone with the link sees the conversation read-only, anonymously; unsharing revokes it.

Dependencies

  • 50_chat_history (todo) — the saved_chats row, the Save flow, and the History table the share actions extend.

Tasks

  1. 01_share_token.md — migration 0009_saved_chat_share_token + the share/unshare/public-read API + the /shared/<token> page route + the middleware prefix.
  2. 02_share_ui.md — the Share button (chat page, save-then-share in one action) + the History table's share column (create/copy/unshare).
  3. 03_shared_page.md — shared.html + shared.js: the anonymous read-only rendering.
  4. 04_e2e_share_chat.md — the story Playwright suite + regressions + commit.

Testing & Quality

  • Integration: share/unshare/public-read contract (token shape, idempotent share, unshare revokes, a wrong token 404s, no admin needed to read, updated_at untouched by share/unshare) + tests/integration/test_migration_0009.py.
  • Coverage: >90% on app/.
  • E2E (mandatory, A16): tests/e2e/test_share_chat.py, run in isolation.

Completion Criteria

  • Admin: the Share button on the chat page (saved or unsaved conversation) and the History row both produce/copy the /shared/<token> link; an unsaved conversation is saved + shared in one action.
  • A fresh anonymous context opening /shared/<token> sees the full conversation read-only (thinking collapsed, tool lines, the stopped note where present, source chips as plain text) with no composer, Save, Share, Tune, or Retry anywhere; a wrong/revoked token shows the "invalid or revoked" state.
  • Unshare revokes: the same URL shows the invalid state afterwards; the served shared page carries the cache-busting contract (no-cache + ?v= rewrite).
  • uv run pytest green; coverage TOTAL >90%.
  • uv run pytest tests/e2e/test_share_chat.py -v --no-cov green in isolation (DB up).
  • Regression E2E suites green in isolation: test_chat_history.py, test_chat_persistence.py, test_smoke.py, test_cache_busting.py.
  • uv run ruff check . && uv run pyright clean.
  • One --no-gpg-sign commit; phase dir moved to .agent/phases/complete/.

Locked decisions

  • Owner-locked (2026-08-29, roadmap confirmation): share links are public by design (128-bit uuid4 token; unshare revokes); the shared page renders the full conversation read-only (thinking collapsed) with zero interactive controls; source chips are plain text (guests cannot open documents — the documents API is admin-only); clipboard copy with an inline-link fallback (a homelab http origin may not be a secure context).
  • The A10 extension (phase 50, owner permission 2026-08-29) unchanged — sharing reuses the already-stored row; no new storage beyond the token column.
  • A16/A17 honoured — one story E2E suite, one atomic commit.

Commit

git add -A .agent/ app/ alembic/versions/ frontend/ tests/ && git commit --no-gpg-sign -m "feat(chat): share a chat by link — anonymous read-only /shared/<token> page, share/unshare"