From 9594c0d2a8f4c91426176fe735e02ffd5afc96be Mon Sep 17 00:00:00 2001 From: AAA <35992542+TNTcraftHIM@users.noreply.github.com> Date: Tue, 18 Aug 2026 14:15:25 +0800 Subject: [PATCH] docs(project): establish research and governance baseline (#1) --- .codex/README.md | 12 + .codex/config.toml | 8 + .gitattributes | 21 +- .githooks/pre-commit | 5 + .github/pull_request_template.md | 20 ++ .github/workflows/repository-hygiene.yml | 23 ++ .gitignore | 22 ++ AGENTS.md | 65 +++++ CLAUDE.md | 5 + CONTRIBUTING.md | 44 ++++ README.md | 15 ++ docs/README.md | 11 + docs/adr/0001-p2p-first-media-topology.md | 52 ++++ docs/maintenance.md | 54 ++++ docs/project-memory.md | 57 +++++ docs/research/agent-context-governance.md | 55 +++++ docs/research/webrtc-p2p-screen-sharing.md | 272 +++++++++++++++++++++ docs/status.md | 32 +++ docs/需求理解.md | 99 ++++++++ scripts/check-project-state.ps1 | 51 ++++ scripts/check-project-state.sh | 51 ++++ scripts/install-hooks.ps1 | 15 ++ scripts/install-hooks.sh | 7 + scripts/required-project-paths.txt | 24 ++ 24 files changed, 1018 insertions(+), 2 deletions(-) create mode 100644 .codex/README.md create mode 100644 .codex/config.toml create mode 100755 .githooks/pre-commit create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/repository-hygiene.yml create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 CONTRIBUTING.md create mode 100644 README.md create mode 100644 docs/README.md create mode 100644 docs/adr/0001-p2p-first-media-topology.md create mode 100644 docs/maintenance.md create mode 100644 docs/project-memory.md create mode 100644 docs/research/agent-context-governance.md create mode 100644 docs/research/webrtc-p2p-screen-sharing.md create mode 100644 docs/status.md create mode 100644 docs/需求理解.md create mode 100644 scripts/check-project-state.ps1 create mode 100755 scripts/check-project-state.sh create mode 100644 scripts/install-hooks.ps1 create mode 100755 scripts/install-hooks.sh create mode 100644 scripts/required-project-paths.txt diff --git a/.codex/README.md b/.codex/README.md new file mode 100644 index 0000000..65c70ed --- /dev/null +++ b/.codex/README.md @@ -0,0 +1,12 @@ +# Project Codex Configuration + +This directory contains portable, project-scoped Codex configuration. It is intentionally tracked with the repository so contributors receive the same project behavior across machines. + +Codex loads `.codex/config.toml` only after the repository is trusted. The root `AGENTS.md` contains the durable project instructions. Project research and decisions live under `docs/`. + +Do not place provider credentials, tokens, private endpoints, personal preferences, or machine-specific permission settings here. Those belong in the user's local Codex configuration or environment. + +Official references: + +- [Custom instructions with AGENTS.md](https://learn.chatgpt.com/docs/agent-configuration/agents-md) +- [Project-scoped Codex configuration](https://learn.chatgpt.com/docs/config-file/config-basic) diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..59e05f1 --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,8 @@ +#:schema https://developers.openai.com/codex/config-schema.json + +# Bound always-loaded project instructions; move detailed knowledge into on-demand docs. +project_doc_max_bytes = 32768 + +[agents] +enabled = true +max_concurrent_threads_per_session = 3 diff --git a/.gitattributes b/.gitattributes index dfe0770..77c86f4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,19 @@ -# Auto detect text files and perform LF normalization -* text=auto +# Normalize repository text for cross-platform development. +* text=auto eol=lf + +# Windows command scripts are consumed most reliably with CRLF. +*.bat text eol=crlf +*.cmd text eol=crlf +*.sh text eol=lf +*.ps1 text eol=lf + +# Never apply text conversion to common binary assets. +*.avif binary +*.gif binary +*.ico binary +*.jpg binary +*.jpeg binary +*.png binary +*.webp binary +*.woff binary +*.woff2 binary diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..fce853a --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,5 @@ +#!/bin/sh +set -eu + +repo_root=$(git rev-parse --show-toplevel) +exec "$repo_root/scripts/check-project-state.sh" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..d34de54 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,20 @@ +## Summary + +Describe the problem and the outcome of this change. + +## Verification + +List automated tests, manual checks, environments, and meaningful measurements. + +## Research And Decisions + +Link the relevant requirement, research note, issue, and ADR, or explain why none is needed. + +## Checklist + +- [ ] The branch contains one coherent change and no unrelated churn. +- [ ] Tests and repository hygiene checks pass. +- [ ] Security, privacy, compatibility, and licensing effects were considered. +- [ ] Requirements, operational docs, ADRs, project memory, and status were updated where their facts changed. +- [ ] Stale or conflicting memory and documentation were removed rather than preserved as a diary. +- [ ] No credentials, personal data, machine-specific paths, or required untracked artifacts are included. diff --git a/.github/workflows/repository-hygiene.yml b/.github/workflows/repository-hygiene.yml new file mode 100644 index 0000000..34f51b7 --- /dev/null +++ b/.github/workflows/repository-hygiene.yml @@ -0,0 +1,23 @@ +name: Repository hygiene + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +jobs: + project-state: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Validate tracked project state + run: sh scripts/check-project-state.sh + env: + SCREENER_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..499130a --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Local secrets and machine-specific overrides +.env +.env.* +!.env.example +*.local +*.key +*.p12 +*.pem +*.pfx +.claude/settings.local.json + +# Dependencies and generated output +node_modules/ +build/ +coverage/ +dist/ +.cache/ + +# Editor and operating-system noise +.DS_Store +Thumbs.db +*.swp diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1d81fe2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,65 @@ +# Project Instructions + +## Product Direction + +- Build private, low-latency game screen sharing for one broadcaster and a small group of trusted friends. +- This is not a public or large-scale streaming product; users can use OBS/Twitch-class services for that workload. +- Keep the media path P2P-first. Use centralized services for rooms, authentication, signaling, STUN, observability, and TURN fallback. +- Let viewers join from a normal desktop or mobile browser without installing the sender application. +- STUN and authenticated TURN fallback are required. "P2P-first" must never mean "direct-only"; fallback is decided independently for every viewer. +- Treat a web client as the initial delivery target. A packaged desktop sender or native capture helper is a later optimization, not an assumed requirement. +- Do not silently change the product into an always-SFU conferencing system. Record any topology change as an ADR with measured justification. + +## Repository Knowledge + +- At the start of substantial work, read `docs/project-memory.md`, `docs/status.md`, and the relevant requirement, ADR, and research documents. +- Keep durable project decisions, requirements, research, code, and agent configuration inside this repository and Git-tracked. +- Do not leave required project context only in chat history, a user home directory, or an untracked scratch file. +- Treat `docs/project-memory.md` and `docs/status.md` as bounded current snapshots, not append-only journals. Replace or remove stale facts and use Git history for the timeline. +- Update project memory when a durable decision changes, before context compaction on long tasks, and at task handoff. Use `docs/adr/` for architecture decisions and tradeoffs. +- Keep `docs/README.md` current when documentation is added, moved, or superseded. + +## Research Before Changes + +- Before non-trivial design, implementation, or bug fixing, inspect the current repository and research current official documentation and established reference implementations. +- Add community issue discussions or recent papers when they materially improve the answer. Prefer primary sources, record access dates, and distinguish verified facts from inference. +- Store durable findings and links under `docs/research/`; update or supersede stale conclusions instead of accumulating contradictory advice. +- Check licenses before copying code. GPL/AGPL sources are study-only until the project license and distribution model are decided. + +## Git Workflow + +- Work on a short-lived branch for each coherent change. Use `feat/`, `fix/`, `docs/`, `refactor/`, `test/`, `chore/`, or `spike/` prefixes. +- Make focused commits, push the branch, open a pull request, pass checks, merge, and delete the branch. Direct changes to `main` require an explicit exception. +- Keep unrelated work out of a branch. Update tests, docs, ADRs, project memory, and status in the same pull request when their source facts change. +- Follow `CONTRIBUTING.md` and the pull request template. Never rewrite shared history unless the user explicitly requests it. + +## Context Hygiene + +- Keep this root file concise and stable; put specialized rules in the closest relevant directory only when that code exists. +- Never store raw transcripts, large logs, generated summaries, temporary plans, or facts that can be cheaply rediscovered in always-loaded memory. +- On a long session, checkpoint accepted decisions and current state before relying on automatic context compression. After compression or resumption, re-read the current snapshots and inspect Git state. +- When a fact changes, update it in place and remove conflicting text in the same change. Do not preserve obsolete guidance merely for history. + +## Security + +- Never commit credentials, TURN shared secrets, TLS private keys, access tokens, or real user data. +- Commit example configuration with placeholder values and document how local secrets are supplied. +- TURN credentials must be short-lived in production. Rooms must be authenticated or protected by unguessable, expiring invitations. + +## Cross-Platform Work + +- Support development from Windows, macOS, and Linux. Avoid absolute paths and OS-specific path separators in code and configuration. +- Prefer repository scripts or package-manager commands that behave consistently across platforms. +- Text files use LF unless `.gitattributes` explicitly requires otherwise. +- Use ASCII filenames for new files unless a user-facing language or an established project convention requires Unicode. + +## Engineering Workflow + +- Apply Occam's razor: choose the simplest design that satisfies the verified requirement, and stop there. Avoid over-engineering for hypothetical scale or future features. +- Keep changes scoped and preserve unrelated user work. +- Prefer the smallest proven extension point; do not add speculative frameworks, hooks, or abstractions without a current consumer. +- Add automated tests in proportion to behavioral risk. For realtime media changes, also document the manual network and browser matrix used. +- Use comments for non-obvious rationale, invariants, protocol constraints, and workarounds; do not narrate obvious code. +- Use WebRTC statistics and reproducible measurements for latency, bitrate, candidate type, packet loss, encode time, and quality limitations. Do not claim performance from assumptions alone. +- Keep repository scripts and hooks cross-platform, deterministic, fast, and runnable in CI. A hook must call a tracked script rather than hide project logic in machine-local configuration. +- Before finishing a material task, inspect `git status`, report untracked project artifacts, and stage files created for the requested work unless the user asks not to stage them. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4a7a4a6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +@AGENTS.md + +# Claude Code + +This file intentionally imports the repository-wide agent instructions instead of duplicating them. Keep durable project guidance in `AGENTS.md` and the referenced bounded project documents. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..080ed5d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,44 @@ +# Contributing + +## Normal Workflow + +1. Start from an up-to-date `main` with a clean working tree. +2. Create one short-lived branch for one coherent change. Use `feat/`, `fix/`, `docs/`, `refactor/`, `test/`, `chore/`, or `spike/` followed by a short description. +3. For non-trivial design, implementation, or bug fixing, inspect the repository and research current primary sources before changing code. Record durable findings under `docs/research/`. +4. Implement the smallest complete change. Add tests and concise rationale comments in proportion to risk. +5. Update requirements, ADRs, project memory, status, and operational docs when their source facts changed. Replace stale text instead of appending a diary. +6. Run `sh scripts/check-project-state.sh` on macOS/Linux/Git Bash or `./scripts/check-project-state.ps1` on PowerShell, plus the relevant project tests. +7. Make focused commits using `type(scope): summary` where practical, then push the branch. +8. Open a pull request using the repository template. Resolve review comments and required checks before merging. +9. Prefer squash merge for a single coherent change; preserve separate commits when they carry independently useful history. Delete the merged branch. + +Direct commits to `main` are reserved for an explicit user-approved exception. Never force-push shared branches or rewrite shared history without explicit approval. + +## Research Standard + +- Prefer specifications, official product documentation, primary source code, maintainers' design notes, and original papers. +- Use current community issues and operational reports to identify real compatibility failures, but label anecdotal evidence as such. +- Include a date and direct URLs. Separate verified facts, measurements, assumptions, and recommendations. +- Review licenses before copying implementation code. A useful reference is not automatically a compatible dependency. + +## Pull Request Scope + +A pull request should explain the problem, the chosen design, verification performed, user-visible or operational effects, and remaining risks. Keep unrelated refactors and generated churn out of the branch. + +Changes are not complete when only code is updated. Durable changes must update the relevant documentation and current-memory snapshot in the same pull request. + +## Local Hooks + +Install the tracked Git hooks once per clone: + +```sh +sh scripts/install-hooks.sh +``` + +On native PowerShell, this equivalent is available: + +```powershell +./scripts/install-hooks.ps1 +``` + +The pre-commit hook runs the POSIX repository hygiene check. The PowerShell entry implements the same checks for native Windows use, and CI runs the POSIX entry. Both read the tracked path manifest in `scripts/required-project-paths.txt`; changes to checker behavior must update and verify both in the same pull request. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f0f585 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Screener + +Screener is a private, low-latency screen-sharing project for one game player and a small group of friends. Its media path is WebRTC P2P-first, with authenticated TURN fallback for network pairs that cannot connect directly. Viewers should be able to join from a desktop or mobile browser without installing the sender application. + +The repository is currently in discovery and technical-design preparation. No application runtime has been selected or implemented yet. + +Start here: + +- [Requirements](./docs/需求理解.md) +- [Feasibility research](./docs/research/webrtc-p2p-screen-sharing.md) +- [Current project memory](./docs/project-memory.md) +- [Current status](./docs/status.md) +- [Architecture decision](./docs/adr/0001-p2p-first-media-topology.md) +- [Contributing and Git workflow](./CONTRIBUTING.md) +- [Documentation index](./docs/README.md) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..7af092b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,11 @@ +# Project Documentation + +- [需求理解](./需求理解.md): current product scope, priorities, constraints, and acceptance criteria. +- [Project memory](./project-memory.md): durable decisions and unresolved questions to read at the start of future work. +- [Current status](./status.md): bounded snapshot of the current phase, completed baseline, next step, and blockers. +- [Maintenance guide](./maintenance.md): Git workflow, context hygiene, document lifecycle, research policy, and automation. +- [P2P WebRTC research](./research/webrtc-p2p-screen-sharing.md): evidence, bandwidth model, browser constraints, reference implementations, and feasibility assessment. +- [Agent context governance research](./research/agent-context-governance.md): official Codex, Claude Code, Hermes Agent, and GitHub practices adopted by this repository. +- [ADR-0001](./adr/0001-p2p-first-media-topology.md): accepted P2P-first media topology and its consequences. + +Documentation and project memory are part of the product source of truth. Update them in the same change that alters the corresponding requirement or architecture. diff --git a/docs/adr/0001-p2p-first-media-topology.md b/docs/adr/0001-p2p-first-media-topology.md new file mode 100644 index 0000000..f4af1d1 --- /dev/null +++ b/docs/adr/0001-p2p-first-media-topology.md @@ -0,0 +1,52 @@ +# ADR-0001: P2P-First Media Topology + +- Status: Accepted for the MVP +- Date: 2026-08-18 + +## Context + +The product targets one game broadcaster and a small group of trusted friends. Low glass-to-glass latency, private access, no-install browser viewing, and low server bandwidth cost matter more than large-room scalability. Public broadcasting is deliberately delegated to existing OBS/Twitch-class services. NATs, CGNAT, restrictive firewalls, variable publisher upload, and browser capture limitations prevent a direct-only design from being reliable for every user; partial room reachability is not acceptable behavior. + +## Decision + +Use separate control and media planes: + +- A small HTTPS/WSS service owns identity, rooms, invitations, presence, and WebRTC signaling. +- A viewer can join through an authenticated, expiring Web link on desktop or mobile without installing the sharing client. +- ICE attempts a direct UDP path for every broadcaster-viewer pair, using STUN to discover candidates. +- Only pairs that cannot connect directly use authenticated TURN, preferring UDP and falling back to TCP and TLS on port 443. TURN is required production infrastructure, not an optional enhancement. +- Candidate selection is independent per pair. A room may simultaneously contain direct and relayed viewers without moving working peers onto the server. +- The broadcaster initially creates one peer connection per viewer. The provisional default envelope is one to three viewers, with a measured fourth-viewer admission check. +- No SFU is planned for the normal product envelope. It is only a future reconsideration point if the small-room scope changes or measured relay/upload/encoder pressure makes the chosen envelope unworkable. + +## Consequences + +Positive: + +- Direct sessions consume almost no server media bandwidth and normally take the shortest network path. +- TURN cost is paid only for failed direct pairs. +- The initial product can be built with standard browser WebRTC and a small backend. +- Friends can watch from a phone or unmanaged desktop through a link instead of installing a TeamSpeak-like full client. + +Negative: + +- Broadcaster upload scales approximately as `viewer_count * stream_bitrate`. +- Browser APIs do not guarantee that several peer connections share a single hardware encode. +- Direct peers learn one another's network addresses; this is acceptable only for the initial trusted-friends threat model. +- A room-level P2P-to-SFU migration adds state, keyframe, and reconnection complexity and is not part of the first prototype. +- TURN/TLS on port 443 materially improves compatibility but cannot guarantee success through every authenticated proxy or policy-controlled network; failures still need actionable diagnostics. + +## Rejected For The MVP + +- Always-SFU: operationally stable but makes the server carry all viewer egress, contrary to the main cost constraint. +- Public-broadcast scaling: Twitch/OBS-class services already solve this separate workload and it would distort the private-room design. +- MCU/transcoding: unnecessary CPU/GPU work and added latency for a single screen stream. +- Peer-assisted relay tree: adds churn handling, extra latency, trust problems, and browser re-encoding or a custom packet-forwarding protocol. +- Custom Parsec-like transport: duplicates capture, codec, congestion-control, NAT, and security work already provided by WebRTC. + +## Revisit Triggers + +- More than 20% of successful viewer connections use TURN in representative telemetry. +- The publisher is CPU-limited or cannot sustain the requested aggregate upload for three viewers. +- Product requirements raise the normal room size above four viewers. +- Protecting peer IP addresses becomes a product requirement. diff --git a/docs/maintenance.md b/docs/maintenance.md new file mode 100644 index 0000000..71ec046 --- /dev/null +++ b/docs/maintenance.md @@ -0,0 +1,54 @@ +# 项目维护与上下文治理 + +## 目标 + +仓库本身是跨 session、跨设备、跨 agent 的唯一持久事实源。聊天记录可以很长,但任何会影响后续工作的决定都不能只存在于聊天中;同时,常驻上下文不能退化成聊天流水账。 + +所有维护遵守奥卡姆剃刀:选择能完整满足已验证需求的最简单方案。没有当前消费者、实测瓶颈或重复问题时,不增加抽象、服务、拓扑、hook 或“以后可能用到”的框架。 + +## 事实分层 + +| 内容 | 文件 | 维护方式 | +| --- | --- | --- | +| 每次都必须遵守的规则 | `AGENTS.md` | 保持短、明确、无冲突,最多 200 行 | +| 当前产品事实与长期决定 | `docs/project-memory.md` | 就地更新,最多 200 行且不超过 12 KB | +| 当前阶段、下一步和阻塞项 | `docs/status.md` | 就地更新,最多 120 行且不超过 8 KB | +| 当前可验收需求 | `docs/需求理解.md` | 修改现行规范;变更历史交给 Git | +| 架构取舍与原因 | `docs/adr/` | 状态化管理;新决定 supersede 旧 ADR | +| 可复核的外部证据 | `docs/research/` | 标注日期、来源和适用边界,过时结论要更新或标记 | +| 已完成工作的时间线 | commit、PR、issue | 不复制进常驻 memory | + +## 长 Session 检查点 + +在以下时机更新仓库快照: + +- 用户确认或改变产品范围、优先级、约束或工作习惯后; +- 一个重要设计被接受、推翻或被实测结果修正后; +- 长任务即将发生上下文压缩、交接或暂停前; +- 一个功能分支准备提交或 PR 前。 + +检查点只保存以后仍需要的结论、当前状态、下一步和阻塞项。不要保存原始对话、完整日志、工具输出、临时文件路径、重复的代码结构、可随时重新搜索的常识或已经在其他规范中存在的内容。 + +恢复上下文时按以下顺序读取:`AGENTS.md`、`docs/project-memory.md`、`docs/status.md`、相关需求/ADR/调研,然后检查当前分支、`git status`、最近 commit 和 PR 状态。任何摘要与仓库冲突时,以当前代码、测试和正式文档为准。 + +## 清理规则 + +- 新事实与旧事实冲突时,在同一变更中替换或删除旧内容。 +- `project-memory.md` 达到约 80% 上限时,先合并同类条目并删除可从代码或 Git 重新发现的内容。 +- 需求文档保持单一现行版本,不在末尾无限追加确认记录。 +- 调研结论失效时更新正文并保留“最后验证日期”;只有仍有历史解释价值的决策才进入 ADR。 +- 已完成的临时计划从 `status.md` 删除,由合并后的 PR 和 commit 保存历史。 +- 每个重要 PR 都检查文档是否互相矛盾、链接是否仍有效、memory/status 是否仍代表当前事实。 + +## 在线调研规则 + +非平凡的设计、实现和 bug 修复先读仓库,再查当前的一手资料。优先级为规范/官方文档、官方源码与维护者说明、原始论文,然后才是高质量社区复现与运行经验。安全、网络协议、浏览器能力、依赖版本和云服务行为属于易变化信息,必须按当前日期重新验证。 + +调研输出应说明问题、来源日期、已证实事实、推断、候选方案、许可证、适用边界和最终采用理由。不要把搜索结果原样堆进常驻上下文。 + +## 自动化 + +- `.githooks/pre-commit` 和 CI 调用 `scripts/check-project-state.sh`;原生 Windows 可运行等价的 `scripts/check-project-state.ps1`。两份入口共享 `scripts/required-project-paths.txt`,检查 whitespace、必需文件、Git 跟踪状态和上下文文件上限。 +- `.github/workflows/repository-hygiene.yml` 在 push/PR 上运行 tracked POSIX 入口。 +- hook 只做快速、确定、可复现的检查;需要工程判断的内容留给评审和测试。 +- 新脚本必须有明确当前用途、跨平台入口、失败信息和 CI 调用方。没有现实使用者的 hook 或框架不进入仓库。 diff --git a/docs/project-memory.md b/docs/project-memory.md new file mode 100644 index 0000000..d8d15f9 --- /dev/null +++ b/docs/project-memory.md @@ -0,0 +1,57 @@ +# Project Memory + +Last updated: 2026-08-18 + +## Confirmed Intent + +- The product is a Discord/KOOK/Oopz/TeamSpeak-like screen-sharing tool for gaming with friends. +- One user broadcasts a game, application, or display; several friends watch with very low latency. +- The normal use case is a small, trusted friend group. Public or large-scale streaming is explicitly out of scope and can be handled by OBS/Twitch-class services. +- Viewers should be able to open an invite link in a desktop or mobile browser without installing a dedicated client. +- The system should resemble a Photon-style developer experience: a central room/rendezvous service establishes sessions while realtime traffic is carried by clients whenever possible. +- Server bandwidth cost is a primary constraint. The media path must be P2P-first, with servers used for signaling, STUN, TURN fallback, and observability. SFU is outside the normal small-room scope. +- Avoiding TeamSpeak-like partial reachability is a primary requirement: every broadcaster-viewer pair must independently have TURN/UDP, TURN/TCP, and TURN/TLS candidates available when direct ICE cannot connect. +- A web experience is preferred for convenience, but using a desktop sender is acceptable when it materially improves game capture, audio capture, or hardware encoding. +- All project documentation, memory, code, `AGENTS.md`, and `.codex/` configuration must live in this Git repository and remain tracked for cross-device development. +- Normal maintenance uses short-lived branches, focused commits, remote backup, pull requests, checks, merge, and branch deletion. +- Long conversations require explicit repository checkpoints. Current memory is curated and rewritten when facts change; it must not grow as a transcript or duplicate Git history. +- Material designs, fixes, and implementations begin with current online research of official docs and established projects, supplemented by community reports or papers where useful. +- Engineering follows Occam's razor: use the simplest complete solution for verified needs and reject speculative scale, abstractions, services, and compatibility work until evidence justifies them. + +## Current Recommendation + +- Start with a Windows Chrome/Edge sharing MVP and a responsive Web viewer using one `RTCPeerConnection` per viewer. Validate current Android Chrome and iOS Safari as viewing endpoints. +- Initial operating envelope: one broadcaster and one to three viewers; allow a fourth only after publisher upload and encoder load pass a preflight check. +- Use HTTPS/WSS signaling, trickle ICE, STUN, and authenticated coturn candidates. Prefer direct UDP, then relay UDP, while keeping TURN/TCP and TURN/TLS on port 443 available for restrictive networks. +- Allow mixed connectivity in one room: direct viewers stay direct while only incompatible network pairs consume TURN bandwidth. +- Treat 1080p60 as a best-effort quality profile, not a universal guarantee. Provide 720p60 and 720p30 fallbacks. +- Prefer a runtime-confirmed hardware codec, normally H.264 first with VP8 fallback. Set game capture to `contentHint = "motion"` and preserve framerate before resolution under congestion. +- Add an Electron or native Windows sender only after browser measurements identify capture, application-audio, or encode bottlenecks. +- Do not plan an SFU for the normal small-room product path. Reconsider it only if the product scope or real telemetry later invalidates the P2P envelope; never implement peer forwarding trees in the MVP. + +## Provisional Quality Targets + +- Under a controlled direct path with RTT at or below 40 ms and packet loss at or below 1%: glass-to-glass latency p50 at or below 150 ms and p95 at or below 250 ms. +- Under a regional TURN/UDP path with the same endpoint conditions: glass-to-glass latency p95 at or below 350 ms. +- First picture within 3 seconds after a viewer requests to watch, excluding explicit user permission time. +- A stable 60 fps profile should not silently fall below 50 decoded fps for more than 5 consecutive seconds; it must visibly downgrade or report the limiting reason. +- These are engineering targets to validate with timestamp/high-speed-camera tests, not claims about untested networks. + +## Open Decisions + +- Exact MVP viewer cap and minimum supported publisher upload speed. +- Exact mobile browser support matrix and the required behavior around autoplay, backgrounding, orientation changes, and network handoff. +- Whether the first release is open source, source-available, or proprietary; this affects whether GPL/AGPL projects can be reused rather than only studied. +- Account model versus expiring room links, and whether friends require an allowlist. +- Initial deployment regions and expected mainland China/Hong Kong/overseas network mix. +- Whether Windows per-application audio is P0 or whether whole-system loopback is acceptable initially. +- Whether voice chat is ever in scope or the product remains complementary to an existing voice application. + +## Source Of Truth + +- Requirements: `docs/需求理解.md` +- Research and feasibility: `docs/research/webrtc-p2p-screen-sharing.md` +- Topology decision: `docs/adr/0001-p2p-first-media-topology.md` +- Current phase and next step: `docs/status.md` +- Maintenance and context lifecycle: `docs/maintenance.md` +- Agent workflow: `AGENTS.md` and `.codex/` diff --git a/docs/research/agent-context-governance.md b/docs/research/agent-context-governance.md new file mode 100644 index 0000000..4d9ef4a --- /dev/null +++ b/docs/research/agent-context-governance.md @@ -0,0 +1,55 @@ +# Agent 上下文与仓库治理调研 + +- 调研日期:2026-08-18 +- 范围:Codex、Claude Code、Hermes Agent 与 GitHub Flow 的官方资料 +- 目的:让长 session 和多设备开发可恢复,同时限制常驻上下文膨胀与陈旧事实污染 + +## 官方资料中的共同原则 + +### Codex + +Codex 从 Git 根目录向当前工作目录发现 `AGENTS.md`,越接近当前目录的文件优先级越高;默认合并上限为 32 KiB。适合把仓库级不变量放在根文件,把未来模块特有规则放在相邻目录,而不是把所有细节集中到一个无限增长的说明中。 + +来源: + +- [Codex AGENTS.md 官方说明](https://learn.chatgpt.com/docs/agent-configuration/agents-md) +- [Codex 项目配置官方说明](https://learn.chatgpt.com/docs/config-file/config-basic) + +### Claude Code + +Claude Code 官方建议项目指令具体、简洁、结构化,单个 `CLAUDE.md` 目标控制在 200 行以内,并定期清理冲突或过时指令。官方还区分“上下文中的建议”和“确定执行的 hook”:必须在固定时机运行的检查应写成 hook。根 `CLAUDE.md` 会在压缩后重新注入;大型读取可以交给独立 subagent,主上下文只接收结论。 + +Claude Code 原生读取 `CLAUDE.md`,官方建议已有 `AGENTS.md` 的仓库通过 `@AGENTS.md` 导入,避免复制两份规则。本仓库因此保留一个极小的根 `CLAUDE.md` 作为兼容入口。 + +来源: + +- [Claude Code 项目记忆与 CLAUDE.md](https://code.claude.com/docs/en/memory) +- [Claude Code 上下文窗口与压缩](https://code.claude.com/docs/en/context-window) +- [Claude Code hooks](https://code.claude.com/docs/en/hooks-guide) + +### Hermes Agent + +Hermes Agent 的内置 memory 采用硬字符上限,并明确要求容量接近上限时先合并或删除旧条目。官方把“始终需要的关键事实”与“需要时再搜索的历史 session”分开;还明确不应保存原始数据、临时上下文、容易重新发现的信息或已经存在于项目指令中的重复内容。 + +Hermes Agent 的开发指南强调保持核心窄小、优先扩展已有接口、避免没有当前使用者的 speculative hook,并要求真实路径验证而不只依赖 mock。这些原则适合约束本项目未来的 agent、hook 和脚本增长。 + +来源: + +- [Hermes Agent 持久记忆](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/memory.md) +- [Hermes Agent 开发指南](https://github.com/NousResearch/hermes-agent/blob/main/AGENTS.md) + +### GitHub Flow + +GitHub 官方流程是短分支、隔离的完整 commit、push 远端、PR 评审与检查、merge 后删除分支。远端 push 同时提供跨设备备份;PR 和 commit 历史比在 memory 中复制时间线更适合保存完成记录。 + +来源:[GitHub Flow](https://docs.github.com/en/get-started/using-github/github-flow) + +## 本仓库采用的设计 + +- 根 `AGENTS.md` 最多 200 行,只放跨任务不变量;`.codex/config.toml` 将指令发现预算固定为 32 KiB。 +- `CLAUDE.md` 只导入 `AGENTS.md`,不维护第二份规则。 +- `project-memory.md` 是最多 12 KB 的当前事实快照;`status.md` 是当前阶段快照;两者都更新和清理,不追加对话历史。 +- 需求、ADR、调研、代码、测试和 Git/PR 各自承担不同类型的事实,避免同一信息在多个常驻文件里漂移。 +- 非平凡变更先做一手资料调研,耐久结论才进入 `docs/research/`;大量原始资料不进入常驻 memory。 +- 快速确定的约束由 tracked Git hook 和 CI 执行,判断型检查留给测试和评审。 +- 每个重大 PR 都包含一次上下文卫生检查:更新新事实、删除旧事实、确认没有未跟踪项目文件。 diff --git a/docs/research/webrtc-p2p-screen-sharing.md b/docs/research/webrtc-p2p-screen-sharing.md new file mode 100644 index 0000000..18f3c14 --- /dev/null +++ b/docs/research/webrtc-p2p-screen-sharing.md @@ -0,0 +1,272 @@ +# P2P 优先游戏屏幕共享调研与可行性评估 + +- 调研日期:2026-08-18 +- 目标场景:一名玩家向少量熟人私密分享,观看者可用手机/桌面浏览器加入,低延迟,尽量不消耗媒体服务器带宽 +- 结论状态:可用于原型立项;人数、音频范围和部署地区仍需实测确认 + +## 结论 + +这个想法可行,而且非常适合先做 Web MVP。它的定位不是缩小版 Twitch,而是好友之间临时、私密、低延迟的实时窗口。正确的首版不是“完全没有服务器”,而是把系统拆为轻量控制面和 P2P 优先的数据面;分享者可以先用 Web/Electron,观看者应能直接打开手机或桌面浏览器链接: + +```text + HTTPS / WSS +分享者 <------------ 房间、鉴权、信令 ------------> 观看者 + | | + +---- ICE + STUN 尝试直接 UDP WebRTC ----------+ + | + +---- 仅直连失败的观看者 ---- TURN relay -------+ + +若未来产品范围超出小房间边界,再单独评估区域 SFU;大规模公开分享直接使用现有直播服务。 +``` + +“让画面跑起来”难度不高;“像 Discord/TeamSpeak 一样在不同 GPU、浏览器、NAT、运营商和弱网中都保持清晰、60 fps、低延迟”难度高。建议把产品分层: + +- 原型:Web、P2P、STUN、TURN,验证一至三名观看者。 +- 可用 MVP:房间鉴权、短期 TURN 凭据、质量降档、ICE restart、统计与 30 分钟稳定性测试。 +- 产品化:Electron 分享端、Web/移动观看端、Windows 应用音频、硬件编码诊断和区域化 TURN。 +- Discord 级:原生捕获/编码引擎、多个捕获后端、GPU 零复制、进程树音频、广泛兼容与持续遥测,属于长期工程。 + +## 竞品事实 + +| 产品 | 已公开的可靠事实 | 对本项目的含义 | +| --- | --- | --- | +| Discord Go Live | 使用 WebRTC,但媒体发往 Discord RTC Worker 后再转发给观看者;官方明确这是为了路由控制和隐藏用户 IP。桌面端有原生 C++ media engine、自研捕获/编码、多后端回退和硬件编码。 | Discord 的稳定性并不是纯 P2P 或纯浏览器免费获得的。它是未来质量上限参考,不是首版拓扑参考。 | +| TeamSpeak 6 | 官方技术回复说明用 WebRTC/ICE 做 P2P 屏幕分享;Windows 在 DX、Windows Game Capture 与传统捕获路径间选择。2026-04 官方称其 `turn.*` 主机实际仅启用 STUN,server-side SFU 仍在开发。 | 与当前目标最接近,也解释了 direct-only/STUN-only 在严格 NAT 下可能出现“同房间部分人能看、部分人不能看”;本项目必须从首版部署 TURN relay。 | +| KOOK / Oopz | 官方 SDK 清单只足以证明屏幕分享接入声网相关能力,没有公开具体媒体拓扑。 | 可以参考交互,不能把它们写成已经证实的 P2P 或 SFU 案例。 | + +来源: + +- [Discord Go Live 技术概览](https://discord.com/blog/how-it-all-goes-live-an-overview-of-discords-streaming-technology) +- [Discord WebRTC 架构](https://discord.com/blog/how-discord-handles-two-and-half-million-concurrent-voice-users-using-webrtc) +- [Discord GPU 编码优化](https://discord.com/blog/from-blocky-to-brilliant-improving-video-quality-on-discord-go-live-on-amd-gpus) +- [TeamSpeak 6 WebRTC 捕获与传输说明](https://community.teamspeak.com/t/what-dependencies-are-needed-for-screen-share/56852) +- [TeamSpeak 6 P2P 与未来 SFU 状态](https://community.teamspeak.com/t/where-is-screen-share-available-ts3-server-does-not-show-start-stream-button/58890) +- [TeamSpeak 6 P2P 技术讨论](https://community.teamspeak.com/t/how-does-share-screen-technically-work/56850) +- [TeamSpeak 6 当前 STUN/TURN 状态讨论](https://community.teamspeak.com/t/only-p2p-available-on-server-not-implemented-yet/55034?page=3) +- [KOOK SDK 清单](https://m.kookapp.cn/sdk_list.html) +- [Oopz 隐私与 SDK 说明](https://help.oopz.cn/agreement/privacy) + +## ICE、STUN、TURN 与信令 + +- 信令服务交换身份、房间状态、SDP 和 ICE candidates。WebRTC 不规定信令协议,WSS 是直接选择。 +- STUN 让客户端发现公网映射并产生 server-reflexive candidate。它不承载媒体,也不能保证穿过所有 NAT。 +- ICE 测试 host、server-reflexive、peer-reflexive 和 relay candidates,并选择可工作的候选对。Trickle ICE 可以减少建连等待。 +- TURN 在无法直连时转发完整媒体流,是 NAT 组件中真正产生高带宽成本的部分。 +- 候选优先级应表达:direct UDP -> TURN/UDP -> TURN/TCP -> TURN/TLS 443。ICE 可能交错或并发进行候选检查,应用不应手写严格串行计时器;TCP/TLS 能穿过更严格的网络,但丢包时可能产生队头阻塞。 + +不存在适用于所有用户的权威“P2P 直连率”。CGNAT、endpoint-dependent mapping、校园/企业防火墙、移动网络、IPv6 和地区运营商都会改变结果。首版必须通过 `getStats()` 统计自己的 `host/srflx/prflx/relay` 比例,而不是引用未经验证的行业百分比。 + +### 为什么会“有人能看,有人看不了” + +ICE 是按分享者与每一名观看者的网络组合独立选路,而不是整个房间只做一次连接。一个家庭宽带观看者可能成功 UDP 打洞,另一个处于 CGNAT、对称 NAT、校园网、企业代理或蜂窝网络的观看者却没有可用直连候选。因此同一房间天然可能出现不同结果。 + +解决方式不是强迫所有人走服务器,也不是继续增加 STUN 地址,而是同时提供有效的 TURN 凭据和多种传输: + +1. 将 direct UDP 设为最高优先级,成功者保持零媒体服务器路径。 +2. 同时提供有效的 TURN/UDP、TURN/TCP 与 `turns` TLS 443 candidates,由 ICE 连通性检查和优先级选择路径。 +3. 通过统计确认最终选中的 candidate pair,而不是从应用计时顺序推断路径。 +4. 网络切换或候选对失效时执行 ICE restart,超时后重建该 peer connection。 +5. 在 UI 和诊断中区分“直连”“服务器中继”“正在恢复”和明确失败原因。 + +这种混合房间里,一名观看者走 TURN 不会迫使其他观看者也中继。TURN/TLS 443 能显著覆盖严格网络,但仍无法穿过所有认证代理、深度检测或管理员策略,因此产品还需要连接自检和可操作的失败信息,不能承诺 100% 网络可达。 + +来源: + +- [WebRTC peer connection 与信令](https://webrtc.org/getting-started/peer-connections) +- [ICE RFC 8445](https://www.rfc-editor.org/rfc/rfc8445.html) +- [WebRTC transport 要求 RFC 8835](https://www.rfc-editor.org/rfc/rfc8835.html) +- [TURN RFC 8656](https://www.rfc-editor.org/rfc/rfc8656.html) +- [Trickle ICE RFC 8838](https://www.rfc-editor.org/rfc/rfc8838.html) + +## 一对多带宽账 + +设单路编码码率为 `B`,观看者数量为 `N`。以下未计音频、RTP/IP 头、重传和 FEC;生产预算再预留约 10% 至 20%。 + +| 拓扑 | 分享者上行 | 服务器媒体 I/O | 适用性 | +| --- | ---: | ---: | --- | +| P2P 星型 | `N * B` | 直连时接近 0 | 最符合成本目标;适合少量朋友 | +| P2P + TURN | 仍为 `N * B` | 每条中继约 `B` 入 + `B` 出 | 只解决不可达,不解决分享者上行 | +| SFU | 约 `B` | `B` 入 + `N * B` 出 | 分享者轻松,但服务器承担全部观看流量 | +| MCU | 约 `B` | 另有解码、合成、重编码 | 本场景没有合成需求,不采用 | + +以 `8 Mbps` 的 1080p60 目标流为例: + +| 观看者 | 分享者上行 | 每小时分享者发送量 | +| ---: | ---: | ---: | +| 1 | 8 Mbps | 3.6 GB | +| 2 | 16 Mbps | 7.2 GB | +| 3 | 24 Mbps | 10.8 GB | +| 4 | 32 Mbps | 14.4 GB | +| 5 | 40 Mbps | 18.0 GB | + +实际高动态 1080p60 可以把首版目标区间设为约 6 至 10 Mbps,因此四名观看者需要约 24 至 40 Mbps 持续上行,再加协议和重传余量。对光纤用户这并非不可行,但不能假设所有用户都具备该条件。 + +把同一 `MediaStreamTrack` 加到多个 `RTCPeerConnection` 会产生多个独立 `RTCRtpSender`、拥塞控制状态和码率目标。标准不保证跨连接只编码一次;浏览器可能优化,也可能创建多个编码上下文。因此分享端 CPU/GPU 容量必须实测。即使未来原生实现单次编码、多 peer packet fan-out,上行仍然是 `N * B`。 + +IETF 对 mesh/SFU 的拓扑说明见 [RFC 7667](https://www.rfc-editor.org/rfc/rfc7667.html)。 + +## 推荐的拓扑策略 + +MVP(也是预期的正常产品形态): + +- 一至三名观看者默认 P2P。 +- 第四名观看者加入前测量可用上行、当前发送码率、`qualityLimitationReason`、编码耗时和发送队列;不足时先降档。 +- 每条链路独立使用 ICE;只有失败的链路走 TURN。 +- 若一开始就有多条 `relay`,应提示服务器带宽正在增加。 +- 桌面和手机观看者使用同一个 Web 播放端;分享者不要求朋友安装完整客户端。 + +本项目不以 SFU 作为正常扩容路径;超过小房间上限时直接建议使用外部直播服务。只有以后改变产品范围时,才根据遥测重新评估房间级 SFU,观察项包括: + +- 观看者多于三或四名。 +- 第一名或多名观看者已使用 TURN,继续 P2P 会重复占用服务器上行。 +- 分享者上行安全余量不足。 +- 分享者因 CPU/encoder 限制降质。 +- 产品开始要求隐藏好友之间的 IP。 + +不要在 MVP 做观看者转发树。浏览器对收到的 WebRTC track 再发布可能发生解码/重编码,增加负载、质量损失和一跳延迟;真正的数据包级转发还需要自定义密钥、拥塞、故障切换和原生网络层。 + +## Web、Electron 与原生能力 + +### Web MVP + +Web 版本能直接使用 `getDisplayMedia()` + WebRTC,最适合验证产品。但需要接受: + +- 只能在 HTTPS 或 localhost 安全上下文使用。 +- 每次捕获必须由用户手势触发并重新选择来源;授权不能持久保存。 +- 网页不能通过 `deviceId` 强制指定某个游戏窗口。 +- 即使请求 `audio: true`,浏览器规范也允许只返回视频;必须检查音轨是否存在。 +- 1080p60 可以请求但不能保证。选择来源后用 `track.getSettings()` 验证实际分辨率和帧率。 +- 最小化窗口、受保护内容、UAC secure desktop 和某些独占全屏路径可能黑屏或停止。游戏使用无边框窗口,或分享整个显示器,通常更稳。 + +游戏轨建议设置: + +```js +track.contentHint = "motion"; + +const parameters = sender.getParameters(); +parameters.encodings ??= [{}]; +parameters.encodings[0].maxFramerate = 60; +parameters.encodings[0].maxBitrate = selectedProfileBitrate; +parameters.degradationPreference = "maintain-framerate"; +await sender.setParameters(parameters); +``` + +这些参数是偏好或上限,不能绕过浏览器拥塞控制,也不能保证目标码率。 + +来源: + +- [W3C Screen Capture](https://www.w3.org/TR/screen-capture/) +- [MDN getDisplayMedia](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) +- [MediaStreamTrack content hints](https://www.w3.org/TR/mst-content-hint/) +- [RTCRtpSender 参数](https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/setParameters) +- [Chromium capture 架构](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/media/capture/) + +### Electron 分享端 + +Electron 可以固定 Chromium 版本,枚举屏幕/窗口,改善选源、热键和分享状态 UX,并使用 loopback 系统音频。它底层仍是 Chromium,单靠封装不会自动获得 Discord 级捕获质量。 + +推荐升级顺序: + +1. Web 分享端和观看端。 +2. Electron 分享端 + Web 观看端。 +3. 若音频范围仍有问题,添加很小的 Windows WASAPI process-loopback native helper。 +4. 只有实测证明捕获/编码是瓶颈时,才加入 WGC/DDA 纹理和原生 libwebrtc/硬件编码路径。 + +来源: + +- [Electron desktopCapturer](https://www.electronjs.org/docs/latest/api/desktop-capturer/) +- [Windows.Graphics.Capture](https://learn.microsoft.com/en-us/windows/apps/develop/media-authoring-processing/screen-capture) +- [WASAPI loopback](https://learn.microsoft.com/en-us/windows/win32/coreaudio/loopback-recording) +- [Application loopback sample](https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/ApplicationLoopback) +- [Desktop Duplication API](https://learn.microsoft.com/en-us/windows-hardware/drivers/display/desktop-duplication-api) + +### Web 与移动观看端 + +观看端不需要屏幕捕获权限,做成响应式 Web 页面比绑定桌面客户端更符合本项目的便利性目标。首版至少要实测当前 Android Chrome 和 iOS Safari,并处理: + +- 浏览器自动播放策略:先显示明确的“进入并播放”操作,再启动含声音的媒体。 +- `playsinline`、横竖屏切换、全屏和安全区域,避免视频被强制弹出或裁切。 +- 页面进入后台、锁屏后恢复,以及 Wi-Fi/蜂窝网络切换导致的 ICE restart。 +- 手机解码温度与耗电;观看端只接收单路,不启用不必要的视频处理。 +- H.264/VP8 能力协商和降档,不能假设桌面分享者选中的高级 codec 在每部手机上都有高效解码。 + +“私密”首先来自房间鉴权、高熵且过期的邀请、发布/观看权限分离和不录制。WebRTC 媒体本身使用 DTLS-SRTP 加密;但 direct P2P 仍会让这组可信好友看到彼此网络地址,若需要隐藏 IP,必须允许强制 TURN,这会增加服务器带宽。 + +## 编解码策略 + +- 所有合规 WebRTC 浏览器必须支持 VP8 和 H.264 Constrained Baseline;VP9/AV1 是能力协商项。 +- 游戏场景首版通常优先 H.264,因为硬件编码覆盖广;保留 VP8 回退。 +- 不要仅凭 codec 名称判断性能。通过 `RTCRtpSender.getCapabilities()` 协商,通过统计确认 `encoderImplementation`、`powerEfficientEncoder`、`totalEncodeTime/framesEncoded` 和 `qualityLimitationReason`。 +- 软件 VP9/AV1 可能抢占游戏 CPU;只有端点支持且实测硬件高效时才启用。 +- Simulcast/SVC 主要帮助 SFU 按观看者网络选择层。它们不会消除 P2P fan-out 的 `N * B` 上行,还可能增加编码实例。 + +来源: + +- [WebRTC codec requirements RFC 7742](https://www.rfc-editor.org/rfc/rfc7742.html) +- [WebRTC SVC](https://www.w3.org/TR/webrtc-svc/) +- [WebRTC Stats](https://www.w3.org/TR/webrtc-stats/) + +## 可观测性与延迟验收 + +每个 peer connection 至少采集: + +- selected candidate pair 和 `candidateType` +- current/available outgoing bitrate +- RTT、jitter、packets lost、NACK/PLI +- frameWidth、frameHeight、framesPerSecond、framesDropped +- `totalEncodeTime / framesEncoded` +- `encoderImplementation`、`powerEfficientEncoder` +- `qualityLimitationReason` 和各原因累计时长 +- jitter buffer delay、decode time 和 total packet send delay + +WebRTC 标准没有承诺固定毫秒延迟。工程目标必须带网络条件,并使用画面时间码或高速摄像机测量玻璃到玻璃延迟。60 fps 的单帧周期是 16.7 ms,端到端延迟还包含采集等待、编码、单程网络、jitter buffer、解码和显示。 + +建议原型目标:受控 direct/RTT <= 40 ms/丢包 <= 1% 时 p50 <= 150 ms、p95 <= 250 ms;区域 TURN/UDP p95 <= 350 ms。先测量,再决定是否需要原生或 SFU。 + +## 安全与成本防护 + +- WebRTC 使用 DTLS-SRTP。TURN 只能看到加密后的媒体包,但仍能看到地址、房间时序和流量元数据。 +- P2P 会让房间内双方得知网络地址。熟人首版可以接受,陌生人房间不能默认接受。 +- TURN 必须使用短期凭据、速率限制、每用户/房间配额和出口告警,不能提供匿名公共 relay。 +- 房间链接应高熵、可过期;发布和观看权限分离。 +- 如果未来使用 SFU 且要求服务器看不到内容,再评估 SFrame/WebRTC Encoded Transform 和群组密钥管理。 + +## 参考代码优先级 + +| 项目 | 用途 | 许可证与判断 | 重点代码 | +| --- | --- | --- | --- | +| [MiroTalk BRO](https://github.com/miroslavpejic85/mirotalkbro) | 最贴近一人广播、多名观看;可用部署级开关选择 P2P 或 mediasoup SFU | AGPL-3.0。最快验证概念,闭源前只能研究或另购许可 | [broadcast.js](https://github.com/miroslavpejic85/mirotalkbro/blob/main/public/js/broadcast.js)、[server.js](https://github.com/miroslavpejic85/mirotalkbro/blob/main/app/server.js)、[mediasoup handler](https://github.com/miroslavpejic85/mirotalkbro/blob/main/app/mediasoup-handler.js) | +| [Screego](https://github.com/screego/server) | 极简屏幕分享、P2P fan-out、WebSocket 信令、内置 Pion TURN | GPL-3.0。最适合学习轻控制面和按需 TURN | [useRoom.ts](https://github.com/screego/server/blob/master/ui/src/useRoom.ts)、[ws](https://github.com/screego/server/tree/master/ws)、[turn/server.go](https://github.com/screego/server/blob/master/turn/server.go)、[NAT 文档](https://github.com/screego/server/blob/master/docs/nat-traversal.md) | +| [WebRTC samples](https://github.com/webrtc/samples) | 官方浏览器 API 最小示例 | BSD 风格。用于理解 API,不是产品框架 | [getDisplayMedia](https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/getdisplaymedia)、[peer connection examples](https://github.com/webrtc/samples/tree/gh-pages/src/content/peerconnection) | +| [PeerJS](https://github.com/peers/peerjs) | 快速 P2P 原型与简单信令抽象 | MIT。原型快,但产品最终可能需要直接控制 RTCPeerConnection 和统计 | [PeerJS server](https://github.com/peers/peerjs-server) | +| [coturn](https://github.com/coturn/coturn) | 生产 STUN/TURN fallback | BSD-3-Clause。P2P-first 必需基础设施 | [turnserver 文档](https://github.com/coturn/coturn/blob/master/README.turnserver)、[Docker](https://github.com/coturn/coturn/blob/master/docker/coturn/README.md) | +| [Peer Calls](https://github.com/peer-calls/peer-calls) | 同一应用中的 mesh/SFU 双模式 | Apache-2.0;维护速度较慢,适合参考而非首选底座 | [mesh.go](https://github.com/peer-calls/peer-calls/blob/master/server/mesh.go)、[sfu.go](https://github.com/peer-calls/peer-calls/blob/master/server/sfu.go)、[iceauth.go](https://github.com/peer-calls/peer-calls/blob/master/server/iceauth.go) | +| [Broadcast Box](https://github.com/Glimesh/broadcast-box) | 未来专用一对多 SFU,WHIP 推流/WHEP 播放 | MIT。比会议型 SFU 更贴近单路广播 | [Broadcast.tsx](https://github.com/Glimesh/broadcast-box/blob/main/web/src/components/broadcast/Broadcast.tsx)、[simple watcher](https://github.com/Glimesh/broadcast-box/blob/main/examples/simple-watcher.html) | +| [LiveKit](https://github.com/livekit/livekit) | 生产级区域 SFU、SDK、内置 TURN、鉴权 | Apache-2.0。未来需要稳定 SFU 时的首选完整底座 | [屏幕共享](https://docs.livekit.io/transport/media/screenshare/)、[turn.go](https://github.com/livekit/livekit/blob/master/pkg/service/turn.go) | +| [mediasoup](https://github.com/versatica/mediasoup) | 强定制低层 SFU | ISC。自由度高,但房间、信令、鉴权、TURN 和 UI 都需自建 | [mediasoup demo](https://github.com/versatica/mediasoup-demo) | +| [Valve GameNetworkingSockets](https://github.com/ValveSoftware/GameNetworkingSockets) | Photon/Steam 式控制面与 P2P NAT 模型参考,不是媒体引擎 | BSD-3-Clause。其 P2P 文档同样要求信令、ICE/STUN 和 relay fallback | [README_P2P](https://github.com/ValveSoftware/GameNetworkingSockets/blob/master/README_P2P.md)、[test_p2p.cpp](https://github.com/ValveSoftware/GameNetworkingSockets/blob/master/tests/test_p2p.cpp) | + +不建议新项目以休眠的 `ion-sfu` 为底座。Janus 和 Galene 均可用,但分别偏底层网关和完整会议系统,不如上述项目贴合当前边界。 + +## 实施难度与预估 + +以下以一名熟悉 Web/TypeScript、具备基本网络经验的全职工程师为基准,只用于量级判断: + +| 阶段 | 难度 | 预估 | 交付物 | +| --- | --- | --- | --- | +| 技术原型 | 低到中 | 3 至 7 个工作日 | Web 选源、1:3 P2P、基础信令、STUN/TURN、统计面板 | +| 可给朋友使用的 MVP | 中 | 4 至 8 周 | 私密房间、短期 TURN 凭据、质量档位、断线恢复、音频提示、测试矩阵和部署 | +| 稳定产品化 | 高 | 2 至 4 个月以上 | Electron 分享端、应用音频、硬编诊断、多地区 TURN、遥测、升级/安全/兼容处理 | +| Discord 级跨平台体验 | 很高 | 多人持续工程 | 原生 media engine、多捕获后端、GPU 零复制、广泛硬件与网络优化、SFU/E2EE | + +最大的未知不是信令服务,而是浏览器是否在目标 GPU 上复用硬件编码、用户真实 P2P 直连率、香港/内地/海外路由和应用音频需求。第一里程碑应是一个带完整统计的原型和真实朋友网络测试,而不是先设计大规模后端。 + +## 最终建议 + +1. 首版坚持 P2P-first,但明确只服务小房间,暂定一至三名观看者、条件允许时四名。 +2. Web 先行,目标 Windows Chrome/Edge;把 1080p60 写成 best effort,同时提供降档。 +3. 从第一天部署 coturn,并验证 direct、TURN/UDP、TURN/TCP、TURN/TLS 443 以及移动网络切换;这是避免“部分好友永远看不了”的必要条件。 +4. 观看端优先做成免安装响应式 Web;分享端先 Web 验证,再按捕获/音频实测升级 Electron。 +5. 产品代码优先直接使用浏览器 WebRTC API;借鉴 MiroTalk BRO 和 Screego,不在许可证未定前直接 fork GPL/AGPL 代码。 +6. 正常人数超过产品上限时引导使用现有直播服务,不为了假设规模提前搭 SFU;若范围以后改变,首选 Broadcast Box 或 LiveKit,不从零写 SFU。 +7. 不做客户端转发树和自定义视频协议;它们会把项目从中等难度推到接近自研 Parsec 的高难度。 diff --git a/docs/status.md b/docs/status.md new file mode 100644 index 0000000..ab0bfd2 --- /dev/null +++ b/docs/status.md @@ -0,0 +1,32 @@ +# Current Status + +Last updated: 2026-08-18 + +## Phase + +Discovery is complete enough to begin technical design and a measured WebRTC proof of concept. No application code or production infrastructure exists yet. + +## Established Baseline + +- Product scope: private game sharing for one broadcaster and one to three normal viewers, with a measured fourth-viewer decision. +- Client shape: Windows Chrome/Edge sharing MVP; responsive desktop and mobile Web viewer; Electron/native sender only after measurements justify it. +- Media topology: independent WebRTC P2P connections with STUN discovery and mandatory authenticated TURN/UDP, TURN/TCP, and TURN/TLS fallback. +- Large public broadcasts are out of scope and should use OBS/Twitch-class services. +- Research, requirements, current memory, agent instructions, and repository governance are stored in Git. + +## Next Milestone + +Write the technical design and build a narrow prototype that validates: + +- screen and game-audio capture behavior; +- one broadcaster with three heterogeneous viewers; +- direct and mixed direct/TURN rooms; +- mobile browser playback and network handoff; +- actual codec implementation, encode load, bitrate, frame rate, and glass-to-glass latency. + +## Blocking Decisions + +- Three versus four as the hard MVP viewer limit. +- Whole-system versus selected-game audio for the first release. +- Initial deployment region and network test cohort. +- Project license and intended distribution model. diff --git a/docs/需求理解.md b/docs/需求理解.md new file mode 100644 index 0000000..c65c781 --- /dev/null +++ b/docs/需求理解.md @@ -0,0 +1,99 @@ +# 屏幕共享项目需求理解 + +## 2026-08-18 13:31 + +## 需求概述 + +构建一个面向熟人游戏场景的低延迟屏幕共享工具。分享者可以在私密房间内分享游戏、应用窗口或显示器画面及声音,多名朋友可以即时观看。系统采用类似 Photon Host Mode 的产品抽象:中心服务负责房间、身份和建立连接,媒体默认通过 WebRTC 在客户端间 P2P 传输;STUN 协助打洞,直连失败的单条链路才使用 TURN。分享端 Web 优先并允许后续 Electron/原生增强;观看端必须可通过桌面或手机浏览器打开邀请链接,不绑定专用客户端。 + +当前规模假设为一名分享者和一至三名常规观看者,第四名观看者需要通过上行带宽和编码负载预检。产品不承担公开直播或大规模观看,相关需求交给 OBS/Twitch 等成熟直播服务。该人数是待原型验证的工程边界,不是未经测量的承诺。 + +## 功能需求 + +### P0 - 必须实现 + +- [ ] 私密房间:用户可创建房间,并通过不可猜测且可过期的链接或邀请码让朋友加入。 +- [ ] 免安装观看:桌面和手机观看者可直接通过浏览器链接进入、授权并播放,不依赖与分享端相同的桌面客户端。 +- [ ] 屏幕采集:分享者可主动选择显示器或应用窗口,开始、停止和切换分享状态。 +- [ ] 游戏声音:Windows Chrome/Edge 目标环境可请求系统音频;没有返回音轨时必须明确提示,而不是静默无声。 +- [ ] 一对多观看:一名分享者可同时向至少三名观看者发送画面,每名观看者拥有独立的 WebRTC 连接与状态。 +- [ ] P2P 优先:ICE 必须先尝试直接 UDP 路径,服务器不得默认中转可直连的媒体。 +- [ ] NAT 兜底:每名观看者独立进行 ICE;必须提供 TURN/UDP、TURN/TCP 与 TURN/TLS 443 candidates,通过优先级偏好直连和 UDP,同时允许严格网络选择 TCP/TLS,不能因为其网络与其他观看者不同而静默失败。 +- [ ] 质量档位:至少提供 1080p60、720p60、720p30 三档目标,并根据拥塞主动降档。 +- [ ] 断线恢复:临时网络变化时尝试 ICE restart 和重连,并向用户显示当前连接状态。 +- [ ] 可观测性:采集候选类型、RTT、丢包、抖动、实际码率、帧率、编码耗时和质量受限原因。 +- [ ] 访问控制:未获授权的用户不能观看、发布或获取长期 TURN 凭据。 + +### P1 - 重要功能 + +- [ ] 分享端桌面封装:用 Electron 固定 Chromium 版本、改善选源/热键/系统音频体验,同时保留 Web 观看端。 +- [ ] Windows 应用音频:仅捕获所选游戏及其子进程的音频,避免把语音聊天或通知声一并分享。 +- [ ] 连接自检:在开始分享前验证信令、STUN、TURN/UDP 和 TURN/TLS 可达性,并给出不暴露凭据的诊断结果。 +- [ ] 运行时编解码选择:根据端点能力和统计数据选择实际硬件编码的 H.264、VP8、VP9 或 AV1,并始终保留兼容回退。 +- [ ] 诊断页面:用户可导出不含敏感地址和凭据的连接诊断信息。 + +### P2 - 可选功能 + +- [ ] 原生 Windows 捕获辅助程序,使用 WGC/DDA、WASAPI 和硬件编码器降低复制与编码开销。 +- [ ] 多地区 TURN 调度与按地区、运营商统计直连率。 + +## 非功能需求 + +- 性能:在 RTT 不高于 40 ms、丢包不高于 1% 的受控 P2P 路径中,玻璃到玻璃延迟目标为 p50 不高于 150 ms、p95 不高于 250 ms;区域 TURN/UDP 路径的 p95 目标不高于 350 ms。 +- 启播:观看者请求观看后,除用户授权耗时外,3 秒内出现首帧。 +- 流畅性:选择 60 fps 档位时,不能在没有降档或提示的情况下连续 5 秒低于 50 个解码帧每秒。 +- 成本:直连媒体不经过服务器;STUN 与信令仅承担小流量;TURN 的实际流量必须可计量、限额和告警。 +- 安全:全程 HTTPS/WSS,媒体使用 WebRTC DTLS-SRTP;TURN 使用短期凭据、配额和速率限制;仓库不得包含真实密钥。 +- 隐私:P2P 会向房间内好友暴露网络地址,首版威胁模型仅覆盖可信熟人;若要求隐藏 IP,必须切换为服务器转发。 +- 可维护性:代码、需求、调研、项目记忆、`AGENTS.md` 和 `.codex/` 均在 Git 中跟踪;开发配置不得依赖单一操作系统的绝对路径。 +- 兼容性:分享端首个目标矩阵为 Windows 10/11 的当前 Chrome/Edge;观看端还必须覆盖当前 Android Chrome 和 iOS Safari,并实测自动播放限制、横竖屏、锁屏恢复与移动网络切换。 + +## 边界与约束 + +- 首版不包含远程键鼠控制、公开/大规模直播、录制、社区系统或完整语音聊天;大规模分享直接使用现有直播服务。 +- 首版不以超过四名观看者为正常工作负载,也不承诺跨洲网络下的固定低延迟。 +- 浏览器规范要求每次由用户主动选择分享源,权限不能永久保存;纯网页不能保证指定游戏窗口或始终获得系统音频。 +- 1080p60 是能力目标,实际分辨率、帧率和硬件编码由浏览器、GPU、驱动、画面复杂度及网络共同决定。 +- 首版不实现 SFU,不从零实现编解码器或自定义 UDP 视频协议;产品范围以后改变时重新立项,而不是预留复杂框架。 +- GPL/AGPL 项目在许可证策略确认前仅用于研究,不能直接复制到计划闭源的产品中。 + +## 验收标准 + +### P2P 建立与 TURN 兜底 + +- Given 两端网络允许 UDP 打洞,When 观看者加入,Then `getStats()` 显示选中的候选对不是 `relay`,且服务器没有承载媒体流量。 +- Given 模拟对称 NAT 或封锁直连,When 观看者加入,Then 系统自动选择 TURN 并在界面标识“中继连接”,无需用户配置端口。 +- Given UDP 被封锁但 TLS 443 可达,When ICE 检查发现 direct 与 TURN/UDP candidates 不可用,Then 可用的 TURN/TLS candidate 能建立连接或返回可诊断的明确失败原因。 +- Given 同一房间中一名观看者可以直连、另一名处于严格 NAT 或移动网络,When 两者同时观看,Then 前者保持 P2P,后者单独使用 TURN,双方都能看到同一分享且互不影响。 + +### 浏览器与移动端观看 + +- Given 观看者只有受支持的手机浏览器,When 打开有效邀请链接并执行浏览器要求的播放手势,Then 无需安装应用即可进入房间、看到首帧并听到可用音频。 +- Given 手机从 Wi-Fi 切换到蜂窝网络或页面从后台恢复,When 原候选对失效,Then 客户端执行 ICE restart 或重建连接,并显示恢复进度而不是永久黑屏。 + +### 一对多与带宽保护 + +- Given 一名分享者和三名观看者,When 选择 1080p60,Then 三名观看者可独立加入和离开,任一观看者离开不会中断其他连接。 +- Given 新观看者会使预计总上行超过安全预算,When 其请求加入,Then 系统自动降低档位、拒绝该档位或建议改用外部直播服务,而不是持续堆积发送队列。 + +### 质量和恢复 + +- Given 受控 RTT/丢包测试条件,When 连续分享高动态游戏画面 30 分钟,Then 延迟、帧率与丢帧达到上述目标,且没有不可恢复的冻结。 +- Given 分享过程中切换 Wi-Fi/有线网络,When ICE 连接失败,Then 客户端触发 ICE restart,并在成功或超时后给出明确状态。 +- Given 浏览器未返回系统音轨,When 分享开始,Then 分享者在启播前看到“当前来源没有可共享音频”的提示。 + +### 仓库可移植性 + +- Given 在新的 Windows、macOS 或 Linux 工作副本中克隆仓库,When 打开项目,Then 需求、调研、项目记忆、`AGENTS.md` 与 `.codex/` 均存在且处于 Git 跟踪状态,不依赖原开发机器的绝对路径或密钥。 + +## 待确认事项 + +- MVP 的硬上限应为三名还是四名观看者。 +- 首版是否必须只捕获所选游戏的声音,还是允许整机系统音频。 +- 项目许可证及是否计划闭源商用。 +- 首批用户主要位于香港、内地还是跨地区;该信息决定 TURN 部署位置和测试矩阵。 +- iOS Safari 与 Android Chrome 的最低版本及是否要求平板适配。 + +详细技术证据、参考代码和难度评估见 `docs/research/webrtc-p2p-screen-sharing.md`。 + +--- diff --git a/scripts/check-project-state.ps1 b/scripts/check-project-state.ps1 new file mode 100644 index 0000000..0532053 --- /dev/null +++ b/scripts/check-project-state.ps1 @@ -0,0 +1,51 @@ +$ErrorActionPreference = "Stop" + +$repoRoot = (git rev-parse --show-toplevel).Trim() +if ($LASTEXITCODE -ne 0) { + throw "This command must run inside the Screener Git repository." +} +Set-Location $repoRoot + +$requiredPaths = Get-Content -Encoding UTF8 scripts/required-project-paths.txt + +foreach ($path in $requiredPaths) { + if ([string]::IsNullOrWhiteSpace($path)) { continue } + git ls-files --error-unmatch -- $path 2>$null | Out-Null + if ($LASTEXITCODE -ne 0) { + throw "Required project artifact is not Git-tracked: $path" + } +} + +$untracked = @(git ls-files --others --exclude-standard) +if ($untracked.Count -gt 0 -and $env:SCREENER_ALLOW_UNTRACKED -ne "1") { + Write-Error "Untracked project files found. Track or ignore them intentionally:`n$($untracked -join "`n")" + throw "Set SCREENER_ALLOW_UNTRACKED=1 only for an intentional partial commit." +} + +$agentLines = @(Get-Content -Encoding UTF8 AGENTS.md).Count +$memoryLines = @(Get-Content -Encoding UTF8 docs/project-memory.md).Count +$memoryBytes = (Get-Item docs/project-memory.md).Length +$statusLines = @(Get-Content -Encoding UTF8 docs/status.md).Count +$statusBytes = (Get-Item docs/status.md).Length + +if ($agentLines -gt 200) { + throw "AGENTS.md exceeds the 200-line context budget: $agentLines lines" +} +if ($memoryLines -gt 200 -or $memoryBytes -gt 12000) { + throw "docs/project-memory.md exceeds its budget: $memoryLines lines, $memoryBytes bytes" +} +if ($statusLines -gt 120 -or $statusBytes -gt 8000) { + throw "docs/status.md exceeds its budget: $statusLines lines, $statusBytes bytes" +} + +git diff --check +if ($LASTEXITCODE -ne 0) { throw "Working-tree whitespace check failed." } +git diff --cached --check +if ($LASTEXITCODE -ne 0) { throw "Staged whitespace check failed." } + +if ($env:SCREENER_BASE_SHA) { + git diff --check "$env:SCREENER_BASE_SHA...HEAD" + if ($LASTEXITCODE -ne 0) { throw "Commit-range whitespace check failed." } +} + +Write-Host "Repository hygiene checks passed." diff --git a/scripts/check-project-state.sh b/scripts/check-project-state.sh new file mode 100755 index 0000000..44155f2 --- /dev/null +++ b/scripts/check-project-state.sh @@ -0,0 +1,51 @@ +#!/bin/sh +set -eu + +repo_root=$(git rev-parse --show-toplevel) +cd "$repo_root" + +while IFS= read -r path || [ -n "$path" ]; do + [ -z "$path" ] && continue + if ! git ls-files --error-unmatch -- "$path" >/dev/null 2>&1; then + echo "Required project artifact is not Git-tracked: $path" >&2 + exit 1 + fi +done < scripts/required-project-paths.txt + +untracked=$(git ls-files --others --exclude-standard) +if [ -n "$untracked" ] && [ "${SCREENER_ALLOW_UNTRACKED:-0}" != "1" ]; then + echo "Untracked project files found. Track or ignore them intentionally:" >&2 + printf '%s\n' "$untracked" >&2 + echo "Set SCREENER_ALLOW_UNTRACKED=1 only for an intentional partial commit." >&2 + exit 1 +fi + +agent_lines=$(wc -l < AGENTS.md | tr -d '[:space:]') +memory_lines=$(wc -l < docs/project-memory.md | tr -d '[:space:]') +memory_bytes=$(wc -c < docs/project-memory.md | tr -d '[:space:]') +status_lines=$(wc -l < docs/status.md | tr -d '[:space:]') +status_bytes=$(wc -c < docs/status.md | tr -d '[:space:]') + +if [ "$agent_lines" -gt 200 ]; then + echo "AGENTS.md exceeds the 200-line context budget: $agent_lines lines" >&2 + exit 1 +fi + +if [ "$memory_lines" -gt 200 ] || [ "$memory_bytes" -gt 12000 ]; then + echo "docs/project-memory.md exceeds its budget: $memory_lines lines, $memory_bytes bytes" >&2 + exit 1 +fi + +if [ "$status_lines" -gt 120 ] || [ "$status_bytes" -gt 8000 ]; then + echo "docs/status.md exceeds its budget: $status_lines lines, $status_bytes bytes" >&2 + exit 1 +fi + +git diff --check +git diff --cached --check + +if [ -n "${SCREENER_BASE_SHA:-}" ]; then + git diff --check "$SCREENER_BASE_SHA"...HEAD +fi + +echo "Repository hygiene checks passed." diff --git a/scripts/install-hooks.ps1 b/scripts/install-hooks.ps1 new file mode 100644 index 0000000..b08f1cb --- /dev/null +++ b/scripts/install-hooks.ps1 @@ -0,0 +1,15 @@ +$ErrorActionPreference = "Stop" + +$repoRoot = git rev-parse --show-toplevel +if ($LASTEXITCODE -ne 0) { + throw "This command must run inside the Screener Git repository." +} + +git -C $repoRoot config core.hooksPath .githooks +if ($LASTEXITCODE -ne 0) { + throw "Failed to configure core.hooksPath." +} + +& "$repoRoot/scripts/check-project-state.ps1" + +Write-Host "Git hooks installed from .githooks." diff --git a/scripts/install-hooks.sh b/scripts/install-hooks.sh new file mode 100755 index 0000000..a5effb0 --- /dev/null +++ b/scripts/install-hooks.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -eu + +repo_root=$(git rev-parse --show-toplevel) +git -C "$repo_root" config core.hooksPath .githooks +sh "$repo_root/scripts/check-project-state.sh" +echo "Git hooks installed from .githooks." diff --git a/scripts/required-project-paths.txt b/scripts/required-project-paths.txt new file mode 100644 index 0000000..31b953f --- /dev/null +++ b/scripts/required-project-paths.txt @@ -0,0 +1,24 @@ +AGENTS.md +CLAUDE.md +CONTRIBUTING.md +README.md +.gitattributes +.gitignore +.codex/config.toml +.codex/README.md +.githooks/pre-commit +.github/pull_request_template.md +.github/workflows/repository-hygiene.yml +docs/README.md +docs/project-memory.md +docs/status.md +docs/maintenance.md +docs/需求理解.md +docs/adr/0001-p2p-first-media-topology.md +docs/research/agent-context-governance.md +docs/research/webrtc-p2p-screen-sharing.md +scripts/required-project-paths.txt +scripts/check-project-state.sh +scripts/check-project-state.ps1 +scripts/install-hooks.sh +scripts/install-hooks.ps1