From 9861a33fd559f5fe29ded52a715c020ac809db32 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 21 Sep 2026 07:35:07 +0000 Subject: [PATCH] docs: add no-BS Debate Workspace replacement plan --- .../2026-09-21-debate-workspace-no-bs.md | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 .hermes/plans/2026-09-21-debate-workspace-no-bs.md diff --git a/.hermes/plans/2026-09-21-debate-workspace-no-bs.md b/.hermes/plans/2026-09-21-debate-workspace-no-bs.md new file mode 100644 index 000000000..badcabd4f --- /dev/null +++ b/.hermes/plans/2026-09-21-debate-workspace-no-bs.md @@ -0,0 +1,145 @@ +# Debate Workspace: no-BS replacement plan + +Adam's first working journey: sign in, enter a motion once, land in Intro, invite a teammate by code, assign R1, and write in the same paragraph from two browsers. Switch between A favor and En contra, disconnect one browser, reconnect, reload, and reopen the workspace tomorrow with the same text and last-opened file. Create another workspace with different collaborators and still find the first in the archive. + +**First-milestone done means** that journey works against real local Auth, PostgreSQL and the collaboration service through a private Tailscale URL. Saved survives a service restart; undo leaves the teammate's work intact; removal blocks subsequent reads and writes. File controls, history/restore, human suggestions and comments work on desktop and mobile. A shell, mocked session, or passing spike is not this deliverable. + +This supersedes the old 20-slice execution plan in `.hermes/plans/2026-09-20_131317-debate-workspace.md`. Keep that file as evidence. There are **three vertical milestones and three new PRs**, plus finishing the already-open #55, for four PRs total. No additional spike, research milestone, component PR, or follow-on planning document. + +This file contains future implementation instructions. This planning task authorizes only this Markdown file. No implementation, PR mutation, commit, push, merge, deployment, migration, credential configuration, or staging/production operation was performed. + +## Verified starting point + +Inspected on 2026-09-21 at approximately 05:05 UTC, in `/home/codex/.t3-v2/worktrees/DebateLab/debate-workspace-no-bs-plan-20260921`, branch `plan/debate-workspace-no-bs-20260921`. Initial working tree was clean. HEAD and live `origin/main` were both `b7c848008aab73a986505de4e76c2bbc412a2b5b`; live origin was checked with `git ls-remote`, without changing this checkout. + +- [#52](https://github.com/Programcode/DebateLab/pull/52) merged as `7b1e20c`; [#53](https://github.com/Programcode/DebateLab/pull/53) as `483f47c`; [#54](https://github.com/Programcode/DebateLab/pull/54) as `b7c8480`. Reuse them. +- [#55](https://github.com/Programcode/DebateLab/pull/55) is OPEN and mergeable at `7b8c1e09f1d92cbb64a0dd915deb48fac3f8e388`. [Run 35530461129](https://github.com/Programcode/DebateLab/actions/runs/35530461129) passed quality, database-tests and browser-tests. Docker was skipped, as expected for a PR. CodeRabbit is green but its comment explicitly says Free-plan summary/walkthrough only. Copilot could not review because of quota. Neither is substantive review. The PR body records local Terra approval of this head; recover that evidence or obtain a fresh review during execution. +- Vercel automatically deployed #55 to Preview, deployment `6556422066`, and `b7c8480` to Production, deployment `6556073910`; both GitHub deployment statuses report success. These are frontend deployment records, not proof of workspace backend rollout. No workspace route exists in `src/App.tsx` or `src/lib/route-modules.ts`. +- [Protected staging run 35528795048](https://github.com/Programcode/DebateLab/actions/runs/35528795048) at `b7c8480` passed its existing staging browser step. Later runs `35529337933` and `35531398398` were skipped. That existing suite checks login/reload, a source and bounded AI, not Debate Workspace. No staging or production endpoint/database was exercised by this planning task. Actual remote workspace migrations, invitation configuration and collaboration hosting remain unverified. + +Source evidence that changes implementation: + +- `services/collaboration/src/server.ts` already intercepts live and reconnect updates before persistence/broadcast and emits durable acknowledgements. Its storage/auth are injected and its locks are in-memory. `services/collaboration/README.md` explicitly calls it a spike. Supply real adapters and revocation handling; do not redo the protocol experiment. +- `src/lib/workspace/{schema,editor-schema,review,document-conversion}.ts` supplies the editor schema, collaboration binding, relative anchors, proposal operations and conversion fixtures. Review is currently an in-process model, not a secure persisted decision API or a complete suggestion-mode UI. Conversion supports a limited corpus; ordered lists and other schema nodes can lose semantics, and PDF fidelity needs work. Reuse the tested pieces without claiming they are finished. +- #54 supplies workspace/member/document metadata, RLS and transactional RPCs: `create_workspace`, `update_workspace_document`, `change_workspace_access`, `change_workspace_state`. Custom document creation and content duplication still need an operation. #55 adds the invitation handler, secure redemption and retryable delivery. Its code needs `WORKSPACE_INVITE_SECRET`; actual email needs an approved relay. +- `src/lib/supabase/env.ts` currently chooses hardcoded production/staging endpoints and defaults to production. A Vite environment variable alone does not select local Supabase. Fix the explicit local preview boundary in milestone 1 before opening any trial link. +- `src/lib/ai/client.ts` gathers personal sources and autosaves discovered sources into the personal library. Existing practice/map pages use personal stores. Workspace context must reach both server authorization and result persistence. +- Existing Google export uses `drive.file` and the app sign-in provider token. `AuthProvider.tsx` still retains that access token in sessionStorage. This is not a multi-account connector. Replace this coupling for workspace Google operations in milestone 3; reuse export formatting and granted scope. + +The complete supplied skill catalog and applicable global, parent and repository instructions were inspected. `plan`, `superpowers:writing-plans`, `unslop` and `ponytail` guide this document; TypeScript, Supabase, Tailscale and Context7 guidance informed source/operational checks. Adam's explicit instructions override microtasks, giant code samples, extra plan files and repeated review ceremonies. + +## Architecture to implement + +Keep React 18, Node 22, npm and the committed lockfile. Use the installed Tiptap 3.31.3, Hocuspocus 4.7.0 and Yjs 13.6.32. Supabase remains the authority for identity, current membership, metadata and durable state. One Node collaboration process owns live documents; enforce one writer per document with a dedicated PostgreSQL session advisory lock, and close writers if the lock connection dies. No Redis, second account system, managed collaboration purchase or new editor evaluation. + +The canonical document is the existing structured Yjs document. JSON/text is a projection at a known durable sequence, not another writable copy. Keep binary updates, snapshots and versions in private storage behind authorized operations. Serialize append, restore and proposal acceptance. Persist before Saved or broadcast. Recheck current membership for incoming operations and outgoing content, including awareness. Coordinate access changes with the gateway so successful removal acknowledgement comes only after queued delivery and subscriptions are fenced. A stale JWT or successful channel join is insufficient. + +Use existing RPCs for metadata, with their expected workspace revision and request IDs. Add only data used by the current milestone. New workspace children need workspace-scoped foreign keys and membership RLS; private snippets and Google connections need account ownership. Never move personal rows into shared scope implicitly. Keep TanStack Query for server metadata and account-scoped IndexedDB for local document recovery. Add editor bindings or a persistence package only where the installed APIs leave a concrete gap. + +Retain the existing app style and controls. Lazy-load workspace/editor/converters. Desktop gets files, document and a collapsible working panel; phones show one pane at a time. Each milestone includes English, Spanish and Hungarian copy, keyboard operation, visible focus, labelled controls, non-color-only diffs/cursors, 200% zoom, IME and touch editing. No redesign project. + +## Milestone 1: a squad can write and review a case + +Outcome: the first journey above, with an actual editor and safe recovery. This is the first usable collaborative release, even before AI and Google are connected. + +- [ ] Recheck #55's exact head, CI and review record. Finish its existing review/fix/merge cycle under execution authorization; do not recreate invitations. If its head changes, discard stale approval and rerun affected checks. This is a dependency inside this milestone, not another featureless milestone. Build from the resulting main head. +- [ ] Wire `/app/workspaces`, `/app/workspaces/:workspaceId` and the invitation route into navigation. Creation opens Intro immediately and creates Intro, R1, R2 and Conclusion on each side, using the existing eight-file transaction. Show a clear localized A favor/En contra switch, assignee beside each file and in the overview, archive/all views and per-user last-file/side preference. One squad prepares both sides; no Team entity or opponents. Creating another workspace leaves old memberships intact. Explicit removal revokes access to old content too; archive never does. +- [ ] Add code copy/paste, email invite/retry, redemption after login, rotate/revoke and member removal using #55. Preserve its code strength and add readable grouping rather than inventing a weak PIN. All members can edit/invite/remove non-owners; only the owner deletes. Expose existing transfer safeguards where needed to leave/delete an owner account. Scrub invitation tokens from URLs after redemption and from telemetry. +- [ ] Add custom file creation, rename, reorder with keyboard controls, assignment, archive, trash/restore and duplication. Duplicate accepted content and citations atomically into a new file; do not copy live proposals/history as if they happened there. Preserve #54 revision checks and recovery windows. No permanent purge job in this release. +- [ ] Mount the real editor with formatting, headings, lists, links, tables, Markdown shortcuts/paste, collaboration-aware undo/redo, presence and authenticated cursor labels. Use one active editor per tab. Finish PostgreSQL persistence, access fencing, schema/size limits and restart recovery in the same PR. Keep the spike's 2 MiB ceiling explicit until a real document requires more; reject oversized work before losing it. +- [ ] Persist account/workspace/document/epoch drafts and pending acknowledgements. Show Saving, Saved, Offline and Recovery honestly. Reconnect rechecks membership and epoch before sending. Logout/account switch must not reveal another user's draft; offer recovery/export before discarding unsent work. Storage failure must warn. No offline metadata deletion, invitation, restore or proposal acceptance. +- [ ] Add named and automatic durable versions, browse/preview and restore. Snapshot changed content on idle and before restore/accepted proposals. Restore compares the previewed sequence, starts a new epoch and preserves the pre-restore version. Old offline edits become a recoverable draft, never silently merge into the restored epoch. Keep versions during the trial; defer pruning, not history. +- [ ] Finish human Suggesting mode, inline proposed insertions/deletions/replacements/format changes, per-change accept/reject, inline comments, replies, resolution and member-only @mentions with in-app notifications. Reuse `review.ts` anchors and stale checks. Suggesting must leave accepted content unchanged until a decision. Persist content update and decision atomically under the document lock; concurrent accepts apply once. Detached comments keep their quote. Test real composition/paste, not only the spike's composition-state helper. + +Likely ownership: coordinator owns `src/App.tsx`, `src/lib/route-modules.ts`, `src/components/layout/AppLayout.tsx`, new `src/pages/WorkspacesPage.tsx` and `WorkspacePage.tsx`, `src/components/features/workspace/`, `src/lib/workspace/`, `src/lib/supabase/env.ts`, `src/lib/supabase/AuthProvider.tsx`, `services/collaboration/src/`, additive `supabase/migrations/`, `supabase/tests/`, all three locale files and the existing database wrapper. Update `docs/testing.md`, `docs/development.md` and service README with runnable local commands. Do not generate a component-per-PR inventory. + +Smallest meaningful red tests: new `src/test/workspace/navigation.test.tsx` creates/reopens two workspaces and exposes assignment without restricting edits. Extend real provider/service tests for PostgreSQL append failure, restart after Saved, lock loss, stale restore epoch and connected-member revocation during a queued send. Extend `review.test.ts` for actual editor transaction interception and concurrent persisted accept rollback. Extend lifecycle pgTAP for custom-file duplication, outsider access and cross-workspace IDs. New `e2e/workspace-collaboration.spec.ts` must use two real local Auth users, not session stubs. + +Focused gates: those Vitest files, `npm run test:collaboration`, invitation handler tests if changed, and `./.superset/db-gate.sh`. Extend that wrapper in this PR to hold the same lock through full local Auth/API/service browser integration and guaranteed cleanup; its current database-only run is insufficient. Broader gates: the shared completion gate below, including desktop/mobile Chromium and WebKit. + +Exact browser journey: create “Schools should ban phones”; join from a second account by code and a third by a locally captured email invitation; assign R1 to the second account; both edit R1, format a link, undo/redo and see each other's cursors. Rename/reorder/duplicate a custom file, trash and restore it. Suggest a replacement, reply and mention, accept one change and reject another. Disconnect browser two for 30 seconds, type, reconnect and reload. Save a version, restore while browser two is offline, and recover its old draft without resurrecting it. Restart the gateway after Saved. Remove browser two and prove it receives neither new text nor accepted writes. Create a second motion and reopen the first from the archive. Repeat essential editing/review on a phone viewport and with keyboard only. + +Finish with an explicit-file commit and PR titled `feat(workspace): collaborative case writing`, one fresh substantive review focused on durability/revocation, exact-head CI and CodeRabbit babysitting, then authorized merge. Run the merged build through the private trial procedure below and give Adam its URL and this journey. Cleanup belongs to the coordinator. + +## Milestone 2: practise from the case and keep the results + +Outcome: Adam selects an argument, sends it into Attack, Crossfire or Sparring without retyping the motion, reviews feedback, and returns later to the same case, sources, chats and results. Personal improvement drives the workspace; team preparation shares the same evidence and writing. + +- [ ] Add workspace sources with provenance, search, quoted evidence and citation insertion. Explicit Copy to workspace preserves attribution without exposing the private library. Citations retain bibliographic snapshots when a source changes or disappears. Reuse existing source import/quality UI; shared selection must not change every member's AI context. +- [ ] Add named shared chat threads. Outside Ask chats stay private. Persist messages/request IDs and assistant progress or interrupted status so reload never loses a turn. Serialize generation per thread, deduplicate retries, and charge existing quotas to the requester. +- [ ] Add an explicit private-or-workspace context at the shared AI boundary. Server loads authorized durable documents, shared sources and selected threads; reject foreign IDs, bound context and show included/omitted material. Recheck membership before returning or storing results. Imported text cannot authorize operations. No new vector service. +- [ ] Reuse the milestone 1 proposal path for AI edits. Additions, deletions, replacements, formatting and citation changes have clear diffs. Generation/rejection leaves the document byte-for-byte unchanged. Only a human decision can mutate it; stale or overlapping targets require regeneration. The model never receives a direct editing capability. +- [ ] Connect Generate/case, Factoids/evidence, Brainstorm/questions, Attack, Ask, Flowchart, Crossfire and Sparring using explicit workspace/document/version/side context and a Return to workspace action. Reuse existing features, not entire store-dependent pages embedded inside another page. Persist validated result snapshots, practice progress/checkpoints and feedback against the originating workspace and pinned document version. Late results after navigation must not land in another workspace. Flowchart edits remain resumable. Existing private routes keep their private persistence. +- [ ] Workspace-started sessions clearly announce shared scope; their results remain attached automatically. Personal practice stays private, with explicit copy/share into the workspace. Do not silently share historical private sessions or use teammates' work for personal coaching. + +Likely ownership: `src/lib/ai/client.ts`, `src/lib/workspace/`, workspace panels, the existing Generate/Attack/Brainstorm/Factoids/Ask/Flowchart/Crossfire/Sparring pages and their stores, `supabase/functions/{ai,ai-stream}/`, `_shared/` context handling, additive workspace source/thread/artifact migrations and locales. Extract only the context/persistence seams each flow needs. + +Smallest meaningful red tests: new `src/test/workspace/tool-context.test.ts` proves each tool preserves motion/side/version and saves once to its origin after a route switch. New Edge context tests deny private/foreign content and a revoked requester during generation. Extend proposal tests to reject stale AI changes and forged citations. Database tests prove workspace sharing versus private Ask/library isolation. One browser test reloads a partly completed practice session and a completed result from a second member's account. + +Focused gates: workspace context/client tests, existing tests for the touched practice flows, `npm run test:edge`, `npm run test:collaboration` and the locked database/integration gate. Broader gates: shared completion gate, with old private-tool regression journeys as well as workspace tests. + +Exact browser journey: reopen milestone 1's motion, add evidence and insert its citation; start a named team thread and ask about both sides. Reject an AI rewrite, then accept one independent change in a new proposal. Select R1 and launch Attack, questions, Flowchart, Crossfire and Sparring with prefilled inputs. Interrupt/reopen practice, finish it, reload, and have a teammate open the attached feedback. Switch workspaces mid-request and prove the result stays with the original. Open ordinary Ask and verify no shared thread or source leaked into it. + +Finish with commit/PR `feat(workspace): connect evidence and practice`, fresh substantive review focused on context isolation and result durability, exact-head CI/CodeRabbit babysitting and authorized merge. Serve the merged private trial and give Adam the complete practice journey; perform the same cleanup. Live AI/provider proof requires already-authorized local provider access. A fixture response proves wiring only and must be labelled as such; do not call this milestone fully verified without a real authorized practice run. + +## Milestone 3: reuse writing and move documents in and out + +Outcome: Adam imports an existing speech, reuses a private template, prepares it with teammates, and downloads a useful PDF/Word packet. Google school/personal account choice is explicit and does not change DebateLab identity. + +- [ ] Add account-private snippets and speech templates in the existing Library, reusable across workspaces by independent copy. Save selected text or speech structure deliberately; teammates cannot browse the private library. Later template edits never rewrite inserted content. +- [ ] Finish Markdown and DOCX independent-copy import with preview, provenance and clear unsupported-content notices. Reuse `document-conversion.ts` but close silent omission paths for supported editor nodes, ordered lists, marks, links and tables. Do not use the source importer's truncated plain text for speeches. Bound input and ZIP expansion, parse time and document size; reject unsafe URLs/embeds and never mount imported HTML unsanitized. +- [ ] Export individual documents and ordered whole-workspace PDF/Word packets from a captured durable revision manifest. Include motion, sides, file order, assignees, citations/bibliography and selected practice artifacts. Default to accepted content, exclude trash/archive visibly, and offer labelled review appendices. Preserve selectable multilingual text, real links, lists and paginated tables. Reuse docx, jsPDF and existing font/export utilities. +- [ ] Add multiple Google connections separate from app login, with a visible account chooser for every import/export. Use connection IDs and server-side ownership checks. Keep refresh tokens encrypted and server-only; short-lived Picker tokens stay in memory. Bind one-use state/PKCE/nonce and exact callbacks to the app user, serialize refresh, preserve omitted refresh tokens and clear local custody on disconnect. Remove sessionStorage token dependence from the migrated export path without breaking legacy export. +- [ ] Reuse the existing `drive.file` grant and export-gdoc formatting. Import user-selected Google Docs as independent copies with source URL/account label/revision provenance and preview all supported tabs. Never sync back. Export creates a new Google document in the chosen account; retries return/finish the same provider file. No automatic Drive sharing. Missing file grants require Picker selection, not broader scope. + +Likely ownership: existing `src/lib/workspace/document-conversion.ts`, `src/lib/export-utils.ts`, `src/lib/pdf-export-utils.ts`, `src/pages/LibraryPage.tsx`, export/workspace dialogs, `src/lib/supabase/AuthProvider.tsx`, `supabase/functions/export-gdoc/index.ts`, new import/connection handlers, private OAuth tables and owner-only template policies. Update `supabase/README.md` with configuration names only. + +Smallest meaningful red tests: extend `document-conversion.test.ts` with numbered lists, nested marks, long tables, hyperlinks and Hungarian accents plus unsupported-input notices and decompression limits. Test export while another member edits, preserving one revision manifest. New template RLS tests deny teammates and prove copied content independence. Native OAuth handler tests cover wrong connection owner, callback replay, refresh races, omitted refresh token, disconnect, school denial and timeout after remote file creation. Keep existing Google export tests passing. + +Focused gates: conversion/template/Google tests, native Edge handlers, locked database gate and rendered PDF/DOCX inspection in a real reader. Broader gates: shared completion gate, mobile/WebKit import/download/account-picker paths and regression of all earlier workspace journeys. + +Exact browser journey: save an R1 paragraph as a private snippet and a speech as a template; insert both into a second workspace, modify the originals and verify the copies stay unchanged. Import Markdown and Word speeches, inspect previews and edit the imported copies. Select a school Google account to import a Doc, then a personal account for export, without changing app identity or the original Doc. Export one speech and both-side PDF/Word packets; inspect headings, page breaks, tables, links, citations and accented text. A teammate must be denied access to the template library and either connection. + +Finish with commit/PR `feat(workspace): document portability and private reuse`, fresh substantive review focused on token custody and fidelity, exact-head CI/CodeRabbit babysitting and authorized merge. Serve the merged private trial, supply the packet/reuse journey and clean up. The narrow decision gate here is existing Google application/callback/key availability. If it requires new credentials, callback configuration, encryption-key provisioning, school approval or broader scopes, stop that integration for concrete authorization. Continue local imports/exports; mark Google live proof blocked, never complete the full contract on mocked OAuth. + +## One completion procedure for every milestone + +The implementation coordinator owns coding, tests, commits, PRs, reviews, fixes, CI monitoring, merge, preview verification and cleanup. Adam tries the link and gives product feedback. Do not ask him to run commands or manage routine handoffs. The steps below are part of each milestone's exit, not another milestone. + +1. On an authorized implementation branch, confirm root/branch/status/base, use Node 22 and `npm ci`, preserve unrelated changes, write the named failing behavior test, observe the intended failure, then implement the journey. Keep infrastructure in the PR that uses it. Use coherent commits with explicit staged paths. +2. Run focused tests while editing. At final head run `npm run verify` once. It already includes docs:check, lint, typecheck, unit coverage, test:collaboration, test:edge, build and desktop/mobile Chromium. Also run `./.superset/db-gate.sh`, the new locked real-Auth collaboration journey, and `CI=1 PORT=18080 PLAYWRIGHT_BASE_URL=http://127.0.0.1:18080 npm run test:e2e:webkit`. Install WebKit if absent. Do not reuse an unrelated dev server. Repeat only affected checks after fixes, then invalidate any broader result affected by those fixes. +3. Review the final diff yourself, then obtain one fresh substantive branch review. Use configured Terra reviewer for normal reviews and Sol high for security-sensitive credential/access work. Require concrete findings about the milestone's failure cases and record the reviewed SHA. Fix valid findings and re-review changed risk areas. No separate spec-review ceremony for every component. +4. Under explicit execution/PR authorization, push the feature branch and open one PR with outcome, tested journey, current SHA and environment limitations. Monitor `gh pr view` and `gh pr checks` for that exact head, read CodeRabbit comments/threads and resolve substantive findings. Recheck after every push. CodeRabbit Free summary is recorded as summary-only, with the fresh substantive review supplying the actual review; do not buy a plan or pretend a walkthrough approved code. Pending, failed or rate-limited review is not a pass. +5. Merge only after current-head required checks and substantive review pass and no actionable findings remain. Do not weaken protection. If Actions minutes are the sole blocker, use the standing exception only with satisfactory local evidence, CodeRabbit disposition, explicit exception reporting and protection permitting merge. Otherwise keep fixing or report the genuine blocker. Verify merge SHA and base ancestry, update the execution checkout safely, then build and verify that merged SHA for the private link. +6. Record local, exact-head CI, private Tailscale, existing staging and production evidence separately. A normal stub browser pass is rendering proof only. Report verified, partially_verified or blocked, including unavailable live AI/Google/email checks. No staging run, backend deployment or remote migration is authorized by this plan. + +## Private Tailscale trial: minimum safe procedure + +Implement the missing local integration/preview support in milestone 1, reuse it thereafter. Tailscale CLI help and [official Serve guidance](https://tailscale.com/docs/use-cases/application-testing/share-local-dev-server-with-team) were checked. Existing `svc:debate` already points at port 8080, and host ports 443, 3773 and 3873 already serve other tools. Never reuse, stop or reconfigure those services. + +1. Inspect `tailscale status`, `tailscale serve status --json`, Funnel status and listener/process ownership. Choose unused loopback and tailnet HTTPS ports, recording actual values and the merged SHA. Candidate ports are 18480 for the app proxy and 18443 for Serve; verify before binding. Missing tailnet login, HTTPS capability or permission is a specific blocker, not permission to change tailnet policy. +2. Extend the existing `.superset/db-gate.sh` lifecycle to hold the shared database lock for full local integration and a bounded trial session. Use full local Supabase Auth/API plus the collaboration process, not only `supabase db start`. Record that the active preview owns the stack and lock. Do not attach to someone else's containers. Use isolated test accounts and a controlled invitation mail sink, never send external trial mail. Use test-only fixture values; no operational credential creation or rotation. If required local credentials cannot be supplied within authorized test setup, stop before serving. +3. Add an explicit local-preview mode to `src/lib/supabase/env.ts` with a separate auth storage key, local URLs and a fail-closed missing-config check. Ignore the production/staging localStorage toggle in that mode. Build static assets from the merged SHA. Use the existing Nginx pattern as a task-owned loopback proxy for those assets, only the required local Supabase Auth/REST/Functions paths and the collaboration WebSocket. Never serve the repository directory, `.env`, database port, Studio, mail inbox admin UI or service-role API. Reject unexpected hosts/origins; configure the exact preview origin in local handlers and WebSocket validation. No broad CORS wildcard. +4. Browser traffic must use the private HTTPS origin and WSS, not the viewer's localhost. Preserve app authentication and RLS behind Tailscale. Disable session replay/content logging and keep secrets server-side; only the local public client key may enter the bundle. Validate that missing preview configuration aborts and that no request reaches known production/staging endpoints. Verify unauthorized document and sensitive-path requests fail before sharing. +5. Start only the recorded route with `tailscale serve --bg --https=18443 http://127.0.0.1:18480`, substituting the verified unused ports. Never use Funnel. Read back Serve configuration and verify it still targets the owned proxy. From an authenticated tailnet browser run the milestone's real two-user journey, reload and WebSocket reconnect; confirm non-tailnet access fails. Record actual URL, merged SHA, trial lifetime and which external providers are real or unavailable. Give Adam that verified URL, never an invented hostname or a link to the existing port-8080 service. +6. Keep the preview available for the stated trial window under an active owning task. Preserve trial writing in a local protected snapshot/export before cleanup and prove it can be restored for the next milestone; do not silently erase it with `--no-backup`. No snapshot, credential or authenticated capture belongs in Git. At trial end disable only the allocated Serve endpoint, using the installed CLI's targeted `--https=18443 off` form after confirming syntax, never `serve reset`. Stop only owned PIDs/containers, stop the local Supabase stack through the wrapper and release its lock. Verify the trial URL is unavailable and pre-existing Serve entries/listeners are unchanged. On failure run the same cleanup; report any preservation failure before deleting data. + +## Authorization and stop conditions + +Planning authorizes none of the execution above. Future implementation authorization must include the intended PR/babysitting operations and local private trial. A known complication must be settled before an external write: PR pushes trigger Vercel Preview, and merges to main have triggered Vercel Production and protected staging automation. If the future instruction still prohibits those side effects, do not push or merge until Adam authorizes the exact automatic effects or an authorized operator provides an isolated non-deploying path. Do not silently disable CI or change hosting configuration. Complete local work and its trial preparation while that specific gate is pending. + +Stop the affected operation for new provider/sender/OAuth credentials, encryption keys, broader OAuth scopes, redirect/consent configuration, purchases/billing, a production WebSocket hosting target, tailnet policy changes, remote migrations/functions, or destructive data operations. Present the exact target/change and reversible path. Never use production as a substitute. An execution merge instruction does not override this plan's explicit no-staging/no-production boundary without a concrete later authorization. + +Data-loss or authorization failures block the milestone itself: acknowledged text lost, revoked users receiving new content, foreign workspace access, partial proposal commit, cross-account draft/token leakage, or silently dropped exported content. Preserve failing cases and fix them; do not downgrade the required behavior. + +## Requirement coverage and deliberate deferrals + +- Milestone 1 owns motion-once persistence, changing squads without a permanent team/roster, both sides, eight defaults, custom-file lifecycle, assignments without editing restrictions, code/email joins, member-managed access, owner-only deletion and retained old workspaces except explicit revocation/deletion. +- Milestone 1 owns real character collaboration, rich text/Markdown authoring, links, local undo/redo, presence/cursors, offline/reconnect recovery, saved history/restore, human suggestion mode, inline comments, replies and @mentions. Production email delivery is a named operational deferral until an approved sender exists; the handler and local email journey are not deferred. +- Milestone 2 owns shared evidence/provenance/citations, shared named workspace chats versus private outside chats, authorized AI context across documents/sources/chats, reviewable AI diffs and human-only acceptance, and the complete case/evidence/Attack/questions/Flowchart/Crossfire/Sparring return-and-resume loop. +- Milestone 3 owns the account-private snippet/template library, independent Markdown/DOCX/Google imports, Google provenance without sync, useful document/whole-workspace PDF and Word export, multiple Google accounts and explicit per-operation choice, narrow-scope reuse and private token custody. Live Google consent is an operational deferral only when the named authorization gate blocks it; it remains a required feature, not a removed requirement. +- All milestones own aligned English/Spanish/Hungarian copy, mobile/responsive behavior, accessibility, authentication/RLS, revocation races and data-loss prevention. Security and recovery are never deferred to a hardening PR. +- **Coach Memory is explicitly deferred until the core workspace is usable and the three journeys pass.** Its next deliverable is private, evidence-linked recurring weaknesses across the user's own practice sessions and one recommended next drill. It may consume milestone 2's attributed practice records with explicit user control. No teammate profiling, scoring framework, memory schema or background extraction in these three PRs. +- Defer permanent teams, opponent access and two-way Google sync because they contradict the product contract. Defer a new design system, broad personal-store rewrite, vector database, multi-node collaboration, paid editor, managed hosting, automatic history pruning and permanent purge because the first release does not need them. Keep existing retention behavior truthful; never advertise deletion that has not occurred. + +Plan self-check: every locked decision above has an owner or named deferral. The old plan's merged work is reused, unproven spike boundaries are identified, each milestone ends in a working trial, and no implementation was performed to produce this file.