From 99d5d4174083de674fef8d871b51ca21d39a1d65 Mon Sep 17 00:00:00 2001 From: Stackie Jia Date: Sat, 12 Sep 2026 18:49:28 +0800 Subject: [PATCH] docs(agents): streamline project instructions and skills --- .agents/skills/build-run/SKILL.md | 120 +---- .agents/skills/e2e/SKILL.md | 287 +---------- .agents/skills/e2e/references/authoring.md | 42 ++ .../skills/e2e/references/troubleshooting.md | 15 + .agents/skills/release/SKILL.md | 472 +----------------- .agents/skills/release/references/notes.md | 67 +++ .../skills/release/references/publishing.md | 95 ++++ .agents/skills/sync-fcc-ip-docs/SKILL.md | 124 +---- .agents/skills/translate-docs-zh-en/SKILL.md | 153 +----- .../references/translation-memory.md | 16 +- AGENTS.md | 71 +-- 11 files changed, 350 insertions(+), 1112 deletions(-) create mode 100644 .agents/skills/e2e/references/authoring.md create mode 100644 .agents/skills/e2e/references/troubleshooting.md create mode 100644 .agents/skills/release/references/notes.md create mode 100644 .agents/skills/release/references/publishing.md diff --git a/.agents/skills/build-run/SKILL.md b/.agents/skills/build-run/SKILL.md index b186bbbb..3ca84e19 100644 --- a/.agents/skills/build-run/SKILL.md +++ b/.agents/skills/build-run/SKILL.md @@ -1,123 +1,45 @@ --- name: build-run -description: > - Build, run, and configure rtp2httpd locally. Use this skill whenever the user wants to compile - the project, start the daemon, pass command-line arguments, edit configuration, or troubleshoot - build/runtime issues. Also activate when the user mentions cmake, build directory, rtp2httpd.conf, - web-ui build, pnpm run, vite build, embedded_web_data.h, or asks how to test the service locally. +description: Build, configure, and run rtp2httpd locally, including embedded Web UI builds and build/runtime troubleshooting. Excludes cross-compilation and deployment. --- -# Building and Running rtp2httpd +# Local Build and Run -rtp2httpd is a C daemon (CMake build system) that converts RTP multicast / RTSP / HTTP streams -to HTTP unicast. This skill covers local development builds — not OpenWrt cross-compilation. +Run commands from the repository root. Use the toolchain conventions in [AGENTS.md](../../../AGENTS.md). -## Build +Cursor Cloud's startup setup installs locked pnpm dependencies and runs `uv sync --group dev`. Reuse that environment; repeat setup only when dependencies are missing or changed. -Always prefer production builds unless the user explicitly asks for debug. +## Build the needed components + +For embedded UI changes, rebuild the frontend before the C binary. C-only changes can use the committed header without Node.js. Follow `package.json` for build scripts and `CMakeLists.txt` for current options. ```bash -# 1. If web-ui/src/ has changed, rebuild the frontend first (generates src/embedded_web_data.h) -pnpm run web-ui:build # production (preferred) -pnpm run web-ui:build:debug # debug: unminified, with source maps +# When Web UI sources or build inputs changed: +pnpm run web-ui:build -# 2. Configure & compile the C binary cmake -B build -DCMAKE_BUILD_TYPE=Release -DENABLE_AGGRESSIVE_OPT=ON cmake --build build -j$(getconf _NPROCESSORS_ONLN) ``` -The binary lands at `build/rtp2httpd`. Skip step 1 when only C code changed — the generated -`embedded_web_data.h` is committed so Node.js is not required for C-only builds. +Release is the default for normal builds. Use Debug/RelWithDebInfo or `pnpm run web-ui:build:debug` when diagnosis needs symbols or source maps. `ENABLE_AGGRESSIVE_OPT` enables LTO and fast-math and defaults to OFF in CMake; disable it when investigating optimization-sensitive behavior. -### CMake options +The binary is `build/rtp2httpd`. The generated header follows the commit boundary in AGENTS.md. -| Option | Default | Purpose | -|---------------------------|---------|--------------------------------------| -| `CMAKE_BUILD_TYPE` | Release | Debug / Release / RelWithDebInfo | -| `ENABLE_AGGRESSIVE_OPT` | OFF | LTO, fast-math, loop unrolling | +## Run and configure -## Run +Use an unused port, bind a local preview to loopback, and avoid loading a deployed configuration accidentally: ```bash -# Minimal: no config file, verbose, listen on port 8080 -./build/rtp2httpd -C -v -v -v -v -l 8080 - -# With a config file -./build/rtp2httpd -c rtp2httpd.conf - -# Override specific settings via CLI -./build/rtp2httpd -c rtp2httpd.conf -l 5140 -m 20 -v -v +./build/rtp2httpd -C -v -v -v -v -l 127.0.0.1:8080 ``` -### Commonly used CLI flags - -| Flag | Short | Purpose | -|--------------------|-------|--------------------------------------------| -| `--noconfig` | `-C` | Skip default config file | -| `--config ` | `-c` | Use specific config file | -| `--listen [addr:]port` | `-l` | Bind address/port (default ANY:5140) | -| `--verbose` | `-v` | Increase verbosity (stack up to 4 times) | -| `--maxclients ` | `-m` | Max simultaneous clients (default 5) | -| `--help` | `-h` | Show all available options | - -Run `./build/rtp2httpd --help` for the complete flag list. - -## Configuration - -The config file is INI-style with three sections: `[global]`, `[bind]`, `[services]`. - -- **Reference config**: `rtp2httpd.conf` in the project root — all options are documented with comments -- **Full docs**: `docs/reference/configuration.md` - -When both CLI flags and config file settings are present, CLI flags take precedence. - -### Quick config example - -```ini -[global] -verbosity = 3 - -[bind] -* 5140 +`-C` skips the default config; `-c ` selects a config; `-l [addr:]port` sets the listener. For a config-based reproduction, inspect that file's listeners and upstreams first. Use `./build/rtp2httpd --help` for current flags. -[services] -#EXTM3U -#EXTINF:-1,Channel One -rtp://239.253.64.120:5140 -#EXTINF:-1,RTSP Channel -rtsp://10.0.0.50:554/live -#EXTINF:-1,HTTP Channel -http://upstream.example.com/stream -``` - -## URL formats - -Constructing correct URLs is critical when testing rtp2httpd. The path prefix determines the -protocol handler — getting it wrong gives a 404 or unexpected behavior. - -| Type | URL pattern | Example | -|---------------|------------------------------------------------------|---------------------------------------------------------------| -| RTP multicast | `/rtp/:[?fcc=...&fec=...]` | `/rtp/239.253.64.120:5140` | -| RTSP proxy | `/rtsp/:/[?playseek=...]` | `/rtsp/iptv.example.com:554/channel1` | -| HTTP proxy | `/http/[:]/[?params]` | `/http/upstream.example.com:8080/live/stream.m3u8` | -| Playlist | `/playlist.m3u` | `/playlist.m3u` | - -For the full URL reference with all query parameters (fcc, fec, playseek, tvdr, r2h-ifname, -r2h-token, r2h-seek-name, r2h-seek-offset, etc.), read `docs/guide/url-formats.md`. - -## Verify it works - -```bash -# Status page -curl http://127.0.0.1:5140/status - -# M3U playlist (if services configured) -curl http://127.0.0.1:5140/playlist.m3u - -# Stream a channel (replace with actual multicast addr) -curl http://127.0.0.1:5140/rtp/239.253.64.120:5140 --max-time 3 -o /dev/null -w "%{http_code}" -``` +- For INI settings and `[global]`, `[bind]`, `[services]` examples, read [rtp2httpd.conf](../../../rtp2httpd.conf) and the relevant section of [Configuration Reference](../../../docs/reference/configuration.md). CLI settings take precedence. +- For stream paths and query parameters, read [URL Formats](../../../docs/guide/url-formats.md). The `/rtp/`, `/rtsp/`, and `/http/` prefixes choose different protocol handlers. +- For player scenarios using mock upstreams, use [devlab](../../../tools/devlab/README.md). +- For automated daemon behavior checks, use [e2e](../e2e/SKILL.md). -## Troubleshooting +## Verify the requested behavior -- **Port in use**: change `-l` port or kill the old process +`curl --fail http://127.0.0.1:8080/status` checks basic readiness. Playback, seek, or rendering work also needs the affected stream/player scenario; a status response alone does not verify it. Reuse the chosen listener port in checks, and stop the task's daemon when finished. If a port is occupied, choose another port rather than terminating an unrelated process. diff --git a/.agents/skills/e2e/SKILL.md b/.agents/skills/e2e/SKILL.md index 5d1f11f6..d0c956fe 100644 --- a/.agents/skills/e2e/SKILL.md +++ b/.agents/skills/e2e/SKILL.md @@ -1,286 +1,33 @@ --- name: e2e -description: > - Write, run, review, and debug end-to-end tests for rtp2httpd. ALWAYS use this skill when the user: - (1) wants to write or optimize e2e/integration tests, (2) asks to run tests or mentions run-e2e.sh, - uv, pytest, collect-only, xdist, markers, fixtures, or parallelism, (3) needs to debug failing, - flaky, slow, or hanging e2e tests, (4) mentions any file under e2e/ or scripts/run-e2e.sh, - (5) mentions MockRTSP*, MockHTTP*, MockFCC*, MockSTUN*, R2HProcess, MulticastSender, helper APIs, - or test fixtures, (6) asks about multicast, RTSP, HTTP proxy, FCC, STUN, M3U, EPG, URL template, - or shared multicast test coverage in rtp2httpd, or (7) uses Chinese phrases such as "端到端测试", - "跑测试", or "e2e 测试" in this repo. +description: Write, run, review, or debug rtp2httpd E2E tests and their harness in e2e/ and scripts/run-e2e.sh. --- -# rtp2httpd E2E Testing +# E2E Testing -rtp2httpd e2e tests live in `e2e/`. They run the real `build/rtp2httpd` binary against mock -servers and assert behavior over HTTP, RTSP, UDP, multicast, Unix sockets, and generated playlists. +Tests run the real `build/rtp2httpd` against local mock upstreams. Use [build-run](../build-run/SKILL.md) if the binary is missing or stale. Collection does not require a binary. -## Running Tests +## Run affected tests -Run commands from the project root. Prefer `./scripts/run-e2e.sh` over invoking pytest directly. -The wrapper uses `uv run pytest`, resolves bare test filenames to `e2e/`, and defaults to -xdist with `--dist loadscope`. +From the repository root, prefer the wrapper, which uses uv and defaults to xdist `-n auto --dist loadscope`: ```bash -./scripts/run-e2e.sh -./scripts/run-e2e.sh -p 1 -./scripts/run-e2e.sh --parallel=4 -./scripts/run-e2e.sh test_m3u.py -./scripts/run-e2e.sh e2e/test_multicast.py -./scripts/run-e2e.sh -k "etag" -./scripts/run-e2e.sh -m "not multicast" -./scripts/run-e2e.sh -x -./scripts/run-e2e.sh --co -./scripts/run-e2e.sh --collect-only +./scripts/run-e2e.sh test_m3u.py # one file +./scripts/run-e2e.sh -p 1 -k "test_name" -x # serial reproduction +./scripts/run-e2e.sh --parallel=4 -m "not multicast" +./scripts/run-e2e.sh --co # collection only +./scripts/run-e2e.sh # full suite ``` -Build `build/rtp2httpd` before running real tests: +The wrapper accepts one test file per invocation, either bare or under `e2e/`; if several are supplied, only the last is selected. Run selected files separately. `-p 1` disables xdist. Registered markers live in `pyproject.toml`. -```bash -cmake -B build -DCMAKE_BUILD_TYPE=Release -DENABLE_AGGRESSIVE_OPT=ON -cmake --build build -j$(getconf _NPROCESSORS_ONLN) -``` - -Collect-only mode is the exception: `./scripts/run-e2e.sh --co` and `--collect-only` must work -without `build/rtp2httpd`. - -Use direct pytest only for static or collection checks: - -```bash -uv run ruff check e2e -uv run pytest e2e --collect-only -q -``` - -## Layout - -```text -e2e/ -├── conftest.py -├── test_m3u.py -├── test_epg.py / test_pages.py / test_auth.py / test_config.py / test_error.py -├── test_multicast.py / test_fcc.py / test_multicast_shared.py -├── test_http_proxy*.py -├── test_rtsp_*.py -├── test_url_template_http.py -├── test_url_template_rtsp.py -├── test_url_template_m3u.py -├── test_url_template_placeholders.py -└── helpers/ - ├── __init__.py - ├── config.py - ├── http.py - ├── ports.py - ├── r2h_process.py - ├── mock_http.py / mock_rtsp.py / mock_fcc.py / mock_stun.py - └── rtp.py -``` - -Keep URL template coverage split by resolver responsibility. Do not recreate a monolithic -`test_url_template.py`; place new cases in the matching HTTP, RTSP, M3U, or placeholder file. - -## Core Conventions - -- Import helpers only from `helpers`; update both imports and `__all__` in `helpers/__init__.py` - when adding a helper. -- Never hardcode ports. Use `find_free_port()`, `find_free_udp_port()`, or - `find_free_udp_port_pair()`. These allocate from a per-xdist-worker range - below typical ephemeral ports (14000-32399) so parallel client connections - cannot steal a listen port before rtp2httpd binds it. -- Prefer module-scoped or class-scoped `R2HProcess` fixtures when tests share config and args. -- Use per-test `R2HProcess` only for mutually exclusive configs, port-range behavior, timeout or - log-capture cases, Unix socket cases, or tests whose process state must be isolated. -- Group by functional sub-area, not chronology. Avoid large catch-all classes; smaller classes help - `--dist loadscope` avoid one-worker tail latency. -- Use `@pytest.mark.parametrize` for input/expected matrices. Keep separate tests only for genuinely - different workflows or complex end-to-end paths. -- Keep each test file with a module docstring, accurate marker(s), and clear fixture scope. -- Do not import one test module from another. Move shared constants or small utilities into - `e2e/helpers/`. -- Do not change production C/runtime behavior just to make e2e tests pass unless the test exposes a - real bug and the user asked for the fix. - -## Markers - -Use accurate markers so filtered runs mean what they say. Keep `pyproject.toml` marker registration -in sync with tests. - -```python -@pytest.mark.multicast -@pytest.mark.rtsp -@pytest.mark.fcc -@pytest.mark.http_proxy -@pytest.mark.slow -``` - -Use module-level `pytestmark = pytest.mark.` when the whole file has one requirement. -Use class-level markers when only one class needs the capability. - -## Helper API - -Port and process helpers: - -- `find_free_port()` -- `find_free_udp_port()` -- `find_free_udp_port_pair()` -- `worker_port_range()` -- `wait_for_port(port, host="127.0.0.1", timeout=5.0)` -- `wait_for_unix_socket(path, timeout=5.0)` -- `R2HProcess(binary, port, extra_args=None, config_content=None, capture_log=False, listen=None)` - (`start()` fail-fasts if the process exits and includes rtp2httpd logs) -- `make_m3u_rtsp_config(port, rtsp_port, service_name="Test RTSP")` - -Config and file helpers: - -- `build_config(port, global_lines=None, services_content=None) -> str` -- `build_single_service_config(port, service_name, service_url, global_lines=None, extinf_attrs=None) -> str` -- `write_temp_file(data, suffix="", prefix="r2h_test_") -> str` - -HTTP helpers: - -- `http_get(host, port, path, timeout=5.0, headers=None)` -- `http_request(host, port, method, path, timeout=5.0, headers=None, body=None)` -- `stream_get(host, port, path, read_bytes=8192, timeout=10.0, headers=None)` -- `unix_http_get(socket_path, path, timeout=5.0, headers=None)` -- `unix_http_request(socket_path, method, path, timeout=5.0, headers=None, body=None)` -- `get_header(headers, name, default="")` -- `assert_etag_cache_behavior(host, port, path)` -- `get_upstream_path(upstream)` -- `extract_catchup_source(playlist_text, channel_name)` - -Mock servers: - -- `MockHTTPUpstream(routes={...})` -- `MockHTTPUpstreamSilent()` -- `MockRTSPServer(...)` -- `MockRTSPServerUDP()` -- `MockRTSPServerSilent()` -- `MockRTSPServerNoMedia()` -- `MockRTSPServerNoTeardownResponse()` -- `MockFCCServer()` -- `MockSTUNServer(port=0, mapped_port=0, mapped_ip="1.2.3.4", silent=False)` -- `MulticastSender(...)` -- `make_rtp_packet(...)` - -## Patterns - -Preferred shared process fixture: - -```python -@pytest.fixture(scope="module") -def shared_r2h(r2h_binary): - port = find_free_port() - config = build_single_service_config( - port, - "Channel One", - "rtp://239.0.0.1:1234", - global_lines=["maxclients = 10"], - ) - r2h = R2HProcess(r2h_binary, port, config_content=config) - r2h.start() - yield r2h - r2h.stop() -``` +## Choose the relevant detail -Per-test process when isolation is required: +- When adding, reviewing, or restructuring tests/helpers, read [authoring.md](references/authoring.md) for fixture isolation, port allocation, and source pointers. +- For hangs, flaky tests, or parallel-only failures, read [troubleshooting.md](references/troubleshooting.md). -```python -def test_custom_config(r2h_binary): - port = find_free_port() - config = build_config(port, global_lines=["maxclients = 1"]) - r2h = R2HProcess(r2h_binary, port, config_content=config) - try: - r2h.start() - status, _, _ = http_get("127.0.0.1", port, "/status") - assert status == 200 - finally: - r2h.stop() -``` - -HTTP upstream path assertion: - -```python -upstream = MockHTTPUpstream(routes={"/archive/1.ts": {"status": 200, "body": b"ok"}}) -upstream.start() -try: - status, _, _ = http_get("127.0.0.1", shared_r2h.port, f"/http/127.0.0.1:{upstream.port}/archive/1.ts") - assert status == 200 - assert get_upstream_path(upstream) == "/archive/1.ts" -finally: - upstream.stop() -``` - -ETag behavior: - -```python -assert_etag_cache_behavior("127.0.0.1", shared_r2h.port, "/playlist.m3u") -``` - -## Runner and Parallelism Guidance - -- Default runner mode is parallel: `uv run pytest e2e/ -v -n auto --dist loadscope`. -- `-p 1` disables xdist and is best for debugging logs, hangs, and order-sensitive failures. -- Large files/classes make `loadscope` less effective. Split by functional area and keep fixtures - scoped to the smallest stable shared config. -- Keep tests that truly need isolation per-test. Do not force process sharing for config mutation, - port binding edge cases, log capture, Unix socket behavior, or timeout assertions. -- If a failure only appears in parallel, re-run the exact test with `-p 1 -x`, then inspect fixture - scope, ports, shared mock state, and filesystem temp paths. - -## Debugging Checklist - -1. Reproduce narrowly: - - ```bash - ./scripts/run-e2e.sh -p 1 -k "test_name" -x - ``` - -2. Confirm binary and collection: - - ```bash - ls -la build/rtp2httpd - ./scripts/run-e2e.sh --co - ``` - -3. Check common failure causes: - - - Missing marker or marker missing from `pyproject.toml`. - - Helper added but not re-exported in `helpers/__init__.py`. - - Hardcoded port or port pair collision, or a listen port from `bind(0)` - in the ephemeral range (use `find_free_port()`). - - Mock server started after rtp2httpd needs it. - - `stream_get()` timeout too short for streaming or multicast. - - External M3U fetch via `-M http://...` or `-M file://...` needs a short async wait. - - Shared fixture leaking mutable upstream request logs between assertions. +## Completion -## Validation Before Finishing +Run affected cases and fix regressions caused by the requested change. For changed Python files, use `uv run --group dev ruff check ` and `uv run --group dev ruff format --check `. Check collection when imports, markers, or discovery changed; one wrapper collection run is sufficient unless direct pytest compatibility is itself under test. -Run at least: - -```bash -uv run ruff check e2e -uv run pytest e2e --collect-only -q -./scripts/run-e2e.sh --co -``` - -For URL template changes, run the focused split files: - -```bash -./scripts/run-e2e.sh -p 1 test_url_template_http.py -./scripts/run-e2e.sh -p 1 test_url_template_rtsp.py -./scripts/run-e2e.sh -p 1 test_url_template_m3u.py -./scripts/run-e2e.sh -p 1 test_url_template_placeholders.py -``` - -For marker or scheduling changes, run: - -```bash -./scripts/run-e2e.sh -m "not multicast and not slow" -./scripts/run-e2e.sh -m "not multicast" -``` - -For broad e2e changes, build the binary and run the full suite: - -```bash -./scripts/run-e2e.sh -``` +Exercise parallel execution for shared fixtures, port allocation, or scheduling changes. Use the full suite for broad harness/runtime changes or when requested. Once relevant checks pass, do not add unrelated test runs. Keep production fixes within the requested scope and backed by a real behavioral failure, rather than changing runtime behavior to accommodate an incorrect test. diff --git a/.agents/skills/e2e/references/authoring.md b/.agents/skills/e2e/references/authoring.md new file mode 100644 index 00000000..96d4968d --- /dev/null +++ b/.agents/skills/e2e/references/authoring.md @@ -0,0 +1,42 @@ +# Authoring and Reviewing E2E Tests + +Paths below are relative to the repository root. Read the relevant helper implementation for its current signature rather than copying an API catalog. + +## Isolation and scheduling + +- Import helpers from `helpers`. Export new helpers through both imports and `__all__` in `e2e/helpers/__init__.py`; do not import one test module from another. +- Allocate listening ports with `find_free_port()`, `find_free_udp_port()`, or `find_free_udp_port_pair()`. `e2e/helpers/ports.py` divides a non-ephemeral window among xdist workers; hardcoded ports and `bind(0)` can race with client source-port allocation. Port-range tests are the exception when the port itself is under test. +- Share a module- or class-scoped `R2HProcess` fixture when tests use a stable config. Use per-test processes for config mutation, timeout/log assertions, Unix sockets, port binding edge cases, or other state that must be isolated. +- Release processes, sockets, and files in fixture teardown or `finally`, including when setup or assertions fail. +- Group tests by functional area so `--dist loadscope` can distribute work. Parameterize input/expected matrices; keep distinct end-to-end workflows separate. +- Preserve the split URL-template suites: `test_url_template_http.py`, `test_url_template_rtsp.py`, `test_url_template_m3u.py`, and `test_url_template_placeholders.py`. +- Use accurate module/class/test markers for the capability needed. Keep registration in `pyproject.toml` aligned with usage and document each module's purpose. + +## Where to find helpers + +| Need | Source under `e2e/` | +| --- | --- | +| Binary and common fixtures | `conftest.py` | +| Public helper exports | `helpers/__init__.py` | +| Worker port ranges and readiness | `helpers/ports.py` | +| Daemon lifecycle, startup logs, config | `helpers/r2h_process.py`, `helpers/config.py` | +| HTTP/Unix requests, stream reads, ETags, upstream path assertions | `helpers/http.py` | +| HTTP, RTSP, FCC, STUN upstreams | Matching `helpers/mock_*.py` | +| RTP packets and multicast senders | `helpers/rtp.py` | + +## Shared process example + +```python +@pytest.fixture(scope="module") +def shared_r2h(r2h_binary): + port = find_free_port() + config = build_config(port, global_lines=["maxclients = 10"]) + r2h = R2HProcess(r2h_binary, port, config_content=config) + try: + r2h.start() + yield r2h + finally: + r2h.stop() +``` + +Start mock upstreams before the daemon needs them. Assert the observable result: for URL rewriting, for example, check the recorded upstream path as well as the downstream HTTP response. Read captured logs before stopping the process, because `stop()` removes its temporary logs. diff --git a/.agents/skills/e2e/references/troubleshooting.md b/.agents/skills/e2e/references/troubleshooting.md new file mode 100644 index 00000000..678d327c --- /dev/null +++ b/.agents/skills/e2e/references/troubleshooting.md @@ -0,0 +1,15 @@ +# E2E Failure Diagnosis + +Reproduce the affected case with `./scripts/run-e2e.sh -p 1 -k "test_name" -x`. For failures seen only in parallel, compare that result with the original parallel invocation and verify the fix under the failing mode. + +| Symptom | Inspect | +| --- | --- | +| Binary missing or behavior inconsistent with the change | Rebuild `build/rtp2httpd`; collection alone does not exercise it | +| Import, discovery, or marker errors | `helpers/__init__.py`, `pyproject.toml`, and one `--co` run | +| Bind/startup failure | `R2HProcess.start()` error and captured logs; allocation through `helpers/ports.py`; occupied ports and fixture teardown | +| Parallel-only failures | Fixture scope, mutable mock request logs, worker port ranges, shared files | +| Hanging stream or multicast read | Whether media is produced, mock startup order, interface/multicast availability, and the read length/timeout | +| Missing external M3U immediately after startup | Fetching `-M http://...` or `-M file://...` is asynchronous; wait for the expected condition with a bounded timeout | +| Slow suite tail | Large classes/modules holding one loadscope worker; split by functional area without breaking fixture isolation | + +Do not treat a skipped platform capability as tested coverage. Record environment blockers, and avoid increasing timeouts or disabling parallelism as a substitute for diagnosing shared-state failures. diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md index 884f7385..93673cd3 100644 --- a/.agents/skills/release/SKILL.md +++ b/.agents/skills/release/SKILL.md @@ -1,469 +1,27 @@ --- name: release -description: Execute the rtp2httpd release workflow — cumulative prerelease and formal release notes, tagging, GitHub releases, collapsing superseded prerelease notes after GA, CI handling, and stable branch updates. +description: Prepare rtp2httpd release notes or publish a requested version, including prerelease and stable-branch handling. --- -# rtp2httpd Release Workflow +# Releases -This skill orchestrates the entire release process for `rtp2httpd`. +Match the requested outcome: drafting notes ends with a reviewable notes file; publishing continues through release creation and the applicable cleanup/CI/stable steps. An explicit request to publish is authorization; do not ask for the same approval again. If publication has not been authorized, finish preparation before asking to publish. -## Workflow Steps +## Select the work -Follow these steps in order. +- For notes, version selection, and cumulative prerelease history, read [notes.md](references/notes.md). +- For build preparation, publication, recovery, and CI/stable handling, read [publishing.md](references/publishing.md). Load it only when preparing or executing publication. ---- - -### Step 0: Pre-flight Checks - -- Ensure you are at the **rtp2httpd project root** (`cd` there first if needed). -- Check current branch with `git branch --show-current`. -- Check for uncommitted changes with `git status --porcelain`. If the workspace is not clean, abort with instructions. -- Fetch the latest release branch and tags before inspecting history with `git fetch origin main --tags`. Use - `origin/main`, not a potentially stale local branch, as the endpoint for release-note diffs. -- Determine the **latest published non-draft release tag** using - `gh release list --exclude-drafts --limit 1 --json tagName --jq '.[0].tagName'` (e.g., `v3.14.2-rc.2`). Save it as - the immediately previous release tag so its donation block can be removed after publishing. "Immediately previous" - means the most recently published non-draft GitHub Release, whether formal or prerelease. -- If the user supplied an explicit target tag, use that tag. Otherwise, inspect the latest formal and prerelease versions - and compute the next tag after asking for the release type. -- Determine whether the target tag is a **prerelease** from its SemVer suffix, such as `-rc.1`, `-beta.2`, or - `-alpha.1`. Also record its base version by stripping the suffix (for example, `v3.15.0-rc.2` has base version - `v3.15.0`). Prereleases skip the versioned Makefile and `stable` branch steps. -- Enumerate releases with `gh release list --exclude-drafts --limit 100 --json tagName,isPrerelease,publishedAt`; if 100 - results are returned, increase the limit so the relevant release series cannot be truncated. Identify: - - the previous formal release: the highest lower non-prerelease SemVer tag that is an ancestor of `origin/main`; - - every published prerelease whose SemVer suffix can be stripped to exactly the target base version; and - - the latest such prerelease by publication time, if any. - These are release-note sources, not just version-number inputs. Never use prerelease notes from another base version. - After a formal release is published, the same-series prerelease list is reused to collapse those GitHub Release - notes into a default-hidden accordion. - ---- - -### Step 1: Ask Release Type - -If the user did not provide an explicit target tag, use `AskQuestion` with header "Release type": - -> What type of release is this? - -Options (single-select): -- **patch** — Bug fixes only (Z+1). Release notes are a simple bullet list. -- **minor** — New features + bug fixes (Y+1). Release notes split into "新功能 / 问题修复" (Chinese) and "New Features / Bug Fixes" (English). -- **major** — Breaking changes (X+1). Same format as minor. - -Based on the answer and latest version, compute the new tag (e.g., `v3.15.0`). - ---- - -### Step 2: Draft Release Notes - -Release-note history and git history serve different purposes: - -- Existing prerelease notes describe the complete user-facing change set expected for the upcoming formal release. -- Git history finds newly added work and verifies coverage; it must not cause previously documented prerelease changes to - disappear merely because they fall outside the latest tag-to-`origin/main` diff. - -#### Choose the release-note sources - -**For a prerelease:** use rolling cumulative notes. - -1. Fetch the body of every existing prerelease for the same base version with `gh release view --json body`. -2. Use the latest prerelease body as the primary draft because it should already contain the accumulated notes. -3. Compare it with earlier prerelease bodies in the same series and carry forward any still-relevant user-facing item that - is missing. A new prerelease must include all applicable content from all earlier prereleases in that series. -4. Inspect commits after the immediately previous prerelease tag to find new items. For the first prerelease in a series, - inspect commits after the latest formal release tag instead. -5. Merge the new items into the accumulated draft. Do not replace the draft with only the latest git diff. - -**For a formal release:** prefer the prerelease notes for that version. - -1. If the same base version has prereleases, fetch their bodies and use the latest prerelease body as the primary draft. -2. Compare all earlier prerelease bodies in the series and restore any still-relevant item missing from the latest one. -3. Inspect commits after the latest prerelease tag and add any newly introduced user-facing changes. -4. Cross-check commits from the previous formal release through `origin/main` for omissions, but preserve the prerelease - wording where it remains accurate. The formal notes should represent the finalized cumulative prerelease notes plus - later work. -5. If no prerelease exists for the target version, draft from commits after the previous formal release as usual. - -Before using a source tag, verify that it belongs to the release branch with -`git merge-base --is-ancestor "" origin/main`. Abort for clarification if it is not an ancestor. Use git -history for the applicable ranges above: - -```bash -git log "..origin/main" --oneline --no-merges --format="%s (%h)" -``` - -Categorize commits by type (`feat:`, `fix:`, `perf:`, `refactor:`, `chore:` etc.) to understand what changed. Treat commit -subjects as evidence, not release-note copy: retain user-focused wording from existing release notes when possible. - -#### Merge and normalize the notes - -- Preserve accumulated items even if they were introduced by an older prerelease in the same series. -- Add genuinely new user-facing changes from git history or user-provided notes. -- Deduplicate semantically equivalent bullets across prereleases and new changes; do not repeat an item merely because its - wording changed. -- Remove an accumulated item only when it was reverted, superseded, proven inaccurate, or the user explicitly asks to - remove it. If the evidence is ambiguous, keep it and flag it during review rather than silently dropping it. -- User-provided corrections take precedence over inherited wording. Otherwise, avoid gratuitously rewriting established - prerelease notes when preparing the formal release. -- Normalize each source body before merging: unwrap a GitHub `
` accordion if present (drop the outer - `
` / `` / `
` tags and keep only the inner markdown; do not treat the summary line as a - release-note item), remove the complete donation table using the donation asset URL as its marker, then split at the - standalone `---` separator into Chinese and English sections. Do not confuse that separator with the donation - table's `| --- |` row. -- Treat each retained or new change as a bilingual item pair. Merge and deduplicate the pairs, keep the two language - sections semantically aligned, and update the counterpart translation whenever a correction changes one language. -- Rebuild the canonical structure below only after merging so the final file contains exactly one donation block. - -Draft bilingual release notes **in a file** (e.g., `/tmp/release-notes-v3.x.y.md`) following these conventions. - -Release notes must always contain both Chinese and English: - -- If the user supplies only Chinese release notes, preserve the Chinese text and append a faithful English translation. -- If the user supplies only English release notes, preserve the English text and add a faithful Chinese translation before it. -- If the user supplies both languages, preserve both and only make changes needed for consistent formatting. -- Place the canonical donation block after the Chinese content and before the `---` separator. -- Separate the Chinese section (including its donation block) from the English section with `---`. -- Never publish single-language release notes, even when the user supplied the wording. - -**Patch release format:** -```markdown -- {Chinese description} - - Detail if needed -- {Chinese description} - -| 如果这个项目对你有帮助,不妨请作者喝一杯咖啡 ☕️ | -| --- | -| | - ---- - -- {English description} - - Detail if needed -- {English description} -``` - -**Minor/major release format:** -```markdown -## 新功能 - -- {feature description in Chinese} - - Detail if needed -- ... - -## 问题修复 - -- {fix description in Chinese} - - Detail if needed -- ... - -| 如果这个项目对你有帮助,不妨请作者喝一杯咖啡 ☕️ | -| --- | -| | - ---- - -## New Features - -- {feature description in English} - - Detail if needed -- ... - -## Bug Fixes - -- {fix description in English} - - Detail if needed -- ... -``` - -Always include this canonical donation block in the new release, regardless of whether the release is a patch, -minor, major, or prerelease: - -```markdown -| 如果这个项目对你有帮助,不妨请作者喝一杯咖啡 ☕️ | -| --- | -| | -``` - -Place the block exactly once, immediately after the Chinese content and before the `---` separator that introduces -the English content. Do not append another donation block after the English content. If the user-provided notes -already contain the block, move it to the required position if necessary rather than adding a duplicate. - -**Release notes guidelines:** -- ✅ End-user focused — target audience is rtp2httpd users, not developers -- ✅ Avoid internal implementation details (e.g., "refactored X module", "upgraded Y dependency") -- ✅ Mention the web player / OpenWrt / Docker / specific feature areas where relevant -- ✅ Be concise; one short bullet per change with optional sub-bullet for context -- ✅ Bilingual: Chinese first, English second (separated by `---`) -- ✅ Include exactly one canonical donation block between the Chinese content and the `---` separator -- ❌ Do not include `Closes` / `Fixes #123` — those belong in git history only - -Show the drafted notes to the user by reading and outputting the **full release notes file content** so the user can -directly review the complete bilingual notes and donation block. - -### Step 3: Confirm with User - -Use `AskQuestion` with header "Ready to release?": - -> Ready to create release v3.x.y? - -Show a summary: release notes file path, the release-note sources used (including inherited prereleases), lint, tag -creation, release creation, previous-release donation cleanup, collapsing same-series prerelease notes after a formal -release, and the CI/stable behavior appropriate for a formal release or prerelease. - -Options: -- **Yes, release it!** — proceed with the release -- **No, let me make changes first** — abort -- **No, let me edit release notes** — user edits the notes file, then mark task as complete - -After user approval, continue. - ---- - -### Step 4: Ensure on `main` with a Clean Workspace - -```bash -# Switch to main if not already there, only if workspace is clean -git checkout main -git pull --ff-only origin main -``` - -If there are uncommitted changes preventing a branch switch, advise the user to stash or commit first and abort. - ---- - -### Step 5: Build Web UI - -First, install frontend dependencies to avoid stale local caches: - -```bash -pnpm install -``` - -Then regenerate `src/embedded_web_data.h` so the released binary includes the latest frontend: - -```bash -pnpm run web-ui:build -``` - -This updates `src/embedded_web_data.h`. Commit it if it changed: - -```bash -git add src/embedded_web_data.h -git commit -m "chore: update embedded_web_data.h for v3.x.y" -``` - -If the file did not change (no diff), skip the commit. - ---- - -### Step 6: Run Lint - -```bash -pnpm run lint -``` - -If lint fails, show the output and ask the user whether to fix and retry or abort. - ---- - -### Step 7: Push `main`, Then Create and Push Tag - -```bash -git push origin main -git tag -a "v3.x.y" -m "v3.x.y" -git push origin "v3.x.y" -``` - -### Step 8: Create GitHub Release - -For a formal release: - -```bash -gh release create "v3.x.y" \ - --title "v3.x.y" \ - --notes-file /tmp/release-notes-v3.x.y.md -``` - -For a prerelease, include `--prerelease`: - -```bash -gh release create "v3.x.y-rc.n" \ - --title "v3.x.y-rc.n" \ - --notes-file /tmp/release-notes-v3.x.y-rc.n.md \ - --prerelease -``` - -After this, the CI release workflow is triggered automatically (it listens for `release.published` events). - -### Step 9: Remove the Donation Block from the Previous Release - -Only the latest published release, including a prerelease, may display the donation QR code. Immediately after the -new release is published, inspect only the previous release tag recorded during Step 0 and remove the canonical -donation table from that release if it contains one. - -- Use the donation image asset URL as the stable marker when detecting the block. -- Preserve all other release-note content exactly. -- Do not remove the donation block from the newly published release. -- Do not scan or edit releases older than the immediately previous release. -- Use temporary files and `gh release edit --notes-file` rather than passing multiline notes as command arguments. -- Run Python helpers through `uv run`, never directly through `python`. -- Verify afterward that the new release contains the donation asset URL and the previous release does not. -- If cleanup fails after publishing, report that the previous release still contains the block and provide a safe retry command; - do not delete or recreate the new release. - -The intended end state is: - -```text -latest published release (formal or prerelease): exactly one donation block -immediately previous release: no donation block -``` - -### Step 10: Collapse Same-Series Prerelease Notes After a Formal Release - -If the GitHub Release is a prerelease, **skip this step entirely**. Earlier prereleases in an in-progress series must -remain fully visible. - -After a formal release is published, the same-base-version prereleases are superseded by the GA notes. Collapse each of -those prerelease GitHub Release bodies into a default-hidden accordion so they no longer occupy the releases page. - -Use the same-series prerelease tags identified in Step 0 — every published prerelease whose SemVer suffix strips to -exactly the new formal tag's base version (for example `v3.16.0-beta.0`, `v3.16.0-beta.1`, `v3.16.0-beta.2` after -publishing `v3.16.0`). Do not include prereleases from a different base version. - -For each such tag: - -1. Fetch the current body with `gh release view --json body`. -2. If the body is already wrapped in a top-level `
` accordion, skip that tag. Do not nest another wrapper. -3. Preserve the inner notes exactly. Do not add or remove the donation block in this step; donation cleanup is Step 9 - only. -4. Wrap the body with this canonical GitHub Flavored Markdown accordion. Do **not** add the `open` attribute — the - section must be collapsed by default. Leave a blank line after `` so GitHub renders the inner markdown: - -```markdown -
-预发布说明(已并入 v3.x.y) / Prerelease notes (included in v3.x.y) - -{original body} - -
-``` - -Replace `v3.x.y` with the formal tag just published. Keep the summary wording exactly in this bilingual form. - -5. Write the wrapped notes to a temporary file and apply them with `gh release edit --notes-file ...`. Do not - pass multiline notes as command arguments. -6. Run any wrapping helper through `uv run`, never directly through `python`. -7. Retry transient GitHub API 503s; if an edit still fails, report the remaining tags and a safe retry command. Do not - delete or recreate the new formal release. - -Do not wrap the newly published formal release, prereleases from another base version, or older formal releases. - -Verify afterward that each updated prerelease body contains `
` and `
`, does not contain -`
` - accordion. Leave in-progress prerelease series fully visible. Do not wrap the new formal release. -- The latest published release always contains the donation QR block after its Chinese content; the immediately - previous release has that block removed after publishing. -- Never force-push to `main` or `stable`. -- Use `gh run list` and `gh run view` to monitor CI progress. -- If something goes wrong mid-release, communicate clearly what happened and what manual recovery steps are needed. +Report the notes file or published release URL, exact tag, cleanup/stable results, and actual CI status. If assets are still building, say so; publication does not prove all artifacts are ready. Continue any requested CI/runtime verification to its result, or report the concrete blocker and remaining work. diff --git a/.agents/skills/release/references/notes.md b/.agents/skills/release/references/notes.md new file mode 100644 index 00000000..a932fbc9 --- /dev/null +++ b/.agents/skills/release/references/notes.md @@ -0,0 +1,67 @@ +# Release Notes + +Commands run from the repository root. Draft notes in a task-owned file outside the tracked tree, ready for `gh release create --notes-file`. + +## Find the sources + +Fetch `origin/main` and tags before selecting history: + +```bash +git fetch origin main --tags +gh release list --exclude-drafts --limit 100 --json tagName,isPrerelease,publishedAt +``` + +Increase the limit if the relevant history is truncated. Record: + +- The immediately previous release: the most recently **published** non-draft release, including prereleases. This is the donation-cleanup target, not necessarily the previous formal version. +- The previous formal release: the highest lower non-prerelease SemVer tag that is an ancestor of `origin/main`. +- All published prereleases with exactly the target's base version after stripping its suffix, and the latest by publication time. + +An explicit tag determines the target and prerelease status. Otherwise use the requested patch/minor/major or prerelease increment. Ask only when that choice remains unresolved. A patch uses simple bullets; a minor/major uses the feature/fix sections below, including for its prereleases. + +## Build a cumulative draft + +Fetch same-series prerelease bodies with `gh release view --json body`. For both a later prerelease and its formal release, start with the latest same-series body and reconcile every earlier body for still-relevant missing items. Never inherit from a different base version. + +Inspect commits after the latest same-series prerelease for additions. If there is no such prerelease, start from the previous formal release. Also cross-check the full previous-formal-to-`origin/main` range for coverage when finalizing a formal release: + +```bash +git merge-base --is-ancestor origin/main +git log ..origin/main --no-merges --format='%s (%h)' +``` + +If a source tag is not an ancestor, resolve the intended release branch/range before using that diff. Commit subjects identify work; write notes for end users, preserving accurate existing wording. Deduplicate equivalent items. Remove an inherited item only when reverted, superseded, demonstrably inaccurate, or explicitly removed by the user. Flag uncertainty without silently dropping content. + +Normalize source bodies before merging: unwrap an outer `
` accordion, remove its summary, remove the whole donation table using the image URL below as the marker, and split languages at the standalone `---` (not the table row `| --- |`). Merge Chinese/English item pairs together so corrections remain aligned. + +## Canonical format + +Keep user-provided wording and add the missing language if only one is supplied. For minor/major releases: + +```markdown +## 新功能 + +- 中文功能说明 + +## 问题修复 + +- 中文修复说明 + +| 如果这个项目对你有帮助,不妨请作者喝一杯咖啡 ☕️ | +| --- | +| | + +--- + +## New Features + +- English feature description + +## Bug Fixes + +- English fix description +``` + +For patch releases, omit the feature/fix headings and use one bullet list per language. Every release, including prereleases, has exactly one donation table after the Chinese content and before the separator. Reuse/move an existing block rather than duplicating it. + +Keep notes concise and user-facing, naming player/OpenWrt/Docker areas when useful. Omit internal refactors, dependency churn without user impact, and `Closes`/`Fixes #...` bookkeeping. Check bilingual coverage and final formatting, then show the complete notes for review when drafting or seeking publication approval. A draft-only request needs no build, tag, or GitHub mutation. diff --git a/.agents/skills/release/references/publishing.md b/.agents/skills/release/references/publishing.md new file mode 100644 index 00000000..77a77408 --- /dev/null +++ b/.agents/skills/release/references/publishing.md @@ -0,0 +1,95 @@ +# Publication and Recovery + +Commands below use `` and task-owned notes paths as placeholders. Inspect `.github/workflows/release.yaml` when diagnosing CI; it is the source of truth for release jobs. + +## Prepare a concrete release + +Prepare notes using [notes.md](notes.md), then build and validate before any outstanding publication approval. Existing authorization to publish covers the release operations below. + +Use a clean checkout of current `origin/main`. Check branch, worktree state, and remote refs. Preserve unrelated changes: a dirty development checkout is a reason to use an isolated release worktree, not to discard changes or stop drafting. If `main` is already checked out elsewhere, a temporary branch from `origin/main` in an isolated worktree can push `HEAD:main` normally after verifying the base. Do not force checkout or push. + +```bash +git fetch origin main --tags +# In the clean main release checkout: +git pull --ff-only origin main +pnpm install --frozen-lockfile +pnpm run web-ui:build +pnpm run lint +``` + +Use the project's Node/Corepack setup. Correct build/lint failures introduced by release preparation and rerun the affected check. For unrelated failures requiring a broader change, report the blocker before publishing. Keep lockfiles unchanged during release preparation. + +Review the generated diff and commit only `src/embedded_web_data.h` if changed, for example `chore(release): refresh embedded UI for `. An authorized release includes this generated-file commit. If publication authorization is still outstanding, leave this diff ready for review with the notes and validation results. + +Before pushing, confirm the release base is still current, the intended release commit is clean, and the tag is unused locally and remotely. If `origin/main` advanced during preparation, incorporate it safely and reconcile affected notes/build results before tagging. Check for an existing GitHub Release too; resume a matching partial attempt instead of creating duplicates. + +## Publish + +Push the prepared commit to `main` before the annotated tag. In the clean main checkout: + +```bash +git push origin main +git tag -a -m +git push origin +gh release create --verify-tag --title --notes-file /path/to/notes.md +``` + +For a prerelease, add `--prerelease` to the last command. From an isolated release branch, replace the main push with `git push origin HEAD:main`; verify that remote main contains the intended commit before tagging. + +Publishing triggers `.github/workflows/release.yaml` on `release.published`. If a call fails or its result is uncertain, inspect the remote tag and release state before retrying. A matching existing tag may be reused for an interrupted publication; a tag pointing at a different commit requires resolution, never retagging. + +## Remove the previous donation block + +After publication, fetch the current body of the immediately previous release recorded during preparation. Remove only its canonical donation table, detecting it by the image URL in [notes.md](notes.md). Preserve all other text; use a temporary file and `gh release edit --notes-file ...`. + +Do not scan older releases for donation cleanup. Verify that the new release contains exactly one donation asset URL and the immediately previous release contains none. If this step fails, keep the new release and report the specific cleanup still needed. + +## Collapse superseded prereleases after GA + +Skip this step for a prerelease. For a formal release, use the same-base-version prerelease list collected for its notes. Fetch each current body and skip one already wrapped in a top-level `
` accordion. Otherwise preserve its body exactly inside: + +```markdown +
+预发布说明(已并入 ) / Prerelease notes (included in ) + +{original body} + +
+``` + +Substitute the formal tag in the summary. Leave the blank line after the summary and omit the `open` attribute. Edit through `--notes-file`; do not change donation blocks during this step. Verify the default-hidden wrapper and preservation of the inner bilingual notes. Never wrap the new formal release or a different version series. + +For transient API failures, re-read current state and make a bounded retry. Report any remaining tags/cleanup without deleting the new release. Run any Python helper with `uv run`. + +## CI and stable + +Locate the release's run, verifying its tag/commit rather than relying on an unrelated latest run: + +```bash +gh run list --workflow=release.yaml --branch --limit 5 --json databaseId,headSha,status,conclusion,url +gh run view +``` + +For prereleases, `versioned` is skipped by design. Do not wait for a Makefile commit or update `stable`. + +For formal releases, the `versioned` job generates and commits: + +- `openwrt-support/rtp2httpd/Makefile.versioned` +- `openwrt-support/luci-app-rtp2httpd/Makefile.versioned` + +Its commit subject is `chore: update versioned Makefiles for `. Poll the run and fetch main at bounded intervals (about 30 seconds) for up to 10 minutes. Confirm job success and inspect the matching commit/files on `origin/main`; a matching subject alone is insufficient. Stop waiting on job failure or timeout and report the remaining stable step. Do not claim the rest of CI succeeded merely because `versioned` did. + +After the matching versioned commit is present, fast-forward `stable` from its current remote tip to the verified main commit. In a clean checkout where these branches are available: + +```bash +git fetch origin main stable +git switch stable +git pull --ff-only origin stable +git merge --ff-only origin/main +git push origin stable +git switch main +``` + +For an isolated release worktree, equivalent verified ref updates can avoid disturbing another checkout. If stable diverged, report the blocker; never force-push. Inspect any unrelated main advances before including them in stable. + +Finish with the release URL, pushed tag, donation/prerelease cleanup results, stable result or deliberate prerelease skip, and actual CI status/run URL. If the user requested ready artifacts, continue through the relevant build/upload jobs. Clean up task-owned worktrees and temporary files when no longer needed, retaining notes/recovery files that are the requested deliverable or needed to resolve a failure. diff --git a/.agents/skills/sync-fcc-ip-docs/SKILL.md b/.agents/skills/sync-fcc-ip-docs/SKILL.md index bf09468a..5329958c 100644 --- a/.agents/skills/sync-fcc-ip-docs/SKILL.md +++ b/.agents/skills/sync-fcc-ip-docs/SKILL.md @@ -1,129 +1,47 @@ --- name: sync-fcc-ip-docs -description: > - Synchronize rtp2httpd FCC IP documentation from GitHub issue feedback. ALWAYS use this skill - when the user asks to update FCC address docs, check new FCC IP reports, validate existing FCC - entries, or process comments from https://github.com/stackia/rtp2httpd/issues/5. -argument-hint: "[check|sync|update] [optional issue/comment context]" +description: Check or update the rtp2httpd FCC address collection using community reports in GitHub issue 5. --- -# Sync FCC IP Documentation +# FCC Address Collection -Use this skill to curate community-sourced FCC (Fast Channel Change) IP address reports for -rtp2httpd and keep the Chinese and English FCC documentation synchronized. +Curate reports from . Use [update-memory.md](references/update-memory.md) for the checkpoint, document paths, locality caveats, and grouping conventions. These are community reports, not proof of reachability from the current machine. -Before processing comments, read `references/update-memory.md` for the last processed checkpoint, -known document paths, formatting rules, and prior update history. Update that reference after each -run with the newest processed comment timestamp and a concise update record. +A check/review request returns findings without editing docs, advancing the checkpoint, or reacting on GitHub. A sync/update request covers the Chinese collection, its English counterpart, and the processing record. Carry those edits through without asking again; contributor reactions require authorization for that external action. -## Core Mission +## Retrieve and assess evidence -1. Read comments from . -2. Identify actionable information: new working FCC IPs and credible non-working reports. -3. Update the Chinese FCC address summary document. -4. Use the `translate-docs-zh-en` skill to synchronize the English translation after Chinese doc changes. - -## Step 1: Fetch and Read Issue Comments - -Check `references/update-memory.md` for `last_processed_comment_date`. +Fetch all pages of comments. Use the recorded timestamp for incremental review, or fetch the full history when no checkpoint exists or a historical recheck is requested: ```bash -# Incremental: only comments after the last processed date -gh api "repos/stackia/rtp2httpd/issues/5/comments?since=YYYY-MM-DDTHH:MM:SSZ" --paginate - -# Full: when no checkpoint exists +gh api 'repos/stackia/rtp2httpd/issues/5/comments?since=YYYY-MM-DDTHH:MM:SSZ' --paginate gh api repos/stackia/rtp2httpd/issues/5/comments --paginate ``` -- If a checkpoint exists, process only comments created after that timestamp. -- If no checkpoint exists, process all comments. -- Read comments chronologically because later comments may correct earlier ones. - -## Step 2: Classify Each Comment - -### Actionable: New FCC IP Report - -- The user explicitly shares a new FCC IP address they discovered or successfully use. -- The user confirms an IP works for a specific region, ISP, or location. -- Look for IP addresses such as `10.x.x.x`, `172.x.x.x`, `192.168.x.x`, or public IPs with location context. -- Example: "我在北京联通发现了一个新的 FCC 地址:10.205.x.x". - -### Potentially Actionable: FCC Failure Report - -- The user definitively reports that a known FCC IP no longer works, has been decommissioned, or is unreachable. -- A single report is not enough to remove an entry. Flag it for review and ask before deleting. -- Multiple independent reports for the same IP provide stronger evidence, but still confirm before removal. - -### Not Actionable: Questions or General Discussion - -- Questions about whether an IP works, how to find IPs, configuration help, troubleshooting, thanks, or project discussion. -- Phrases such as "请问", "有没有", "能不能", "是否", and "怎么" usually indicate questions. -- A question mark (`?` or `?`) often means inquiry rather than report. +Review reports and later corrections in context. Include returned edits to older comments rather than filtering solely by `created_at`. Classify by evidence rather than punctuation or keywords: -### Classification Rules +- A working-address report identifies an address/port with explicit successful use or relevant packet-capture fields and regional/ISP context. Confirm attribution before adding it. +- A failure report needs locality and channel context: one location's failure does not establish decommissioning. Preserve existing entries and flag uncertainty unless removal or inactive marking is authorized by the user. +- Questions, requests for addresses, configuration help, and discussion supply no new working address on their own. A mixed question/report can still contain usable evidence. -- Treat phrases such as "分享一下", "发现了", "可以用", "亲测可用", and "已确认" as report signals. -- If ambiguous, do not update the document. -- If a later comment contradicts an earlier one, note both and prioritize the most recent reliable information. +Validate address/port syntax and check the current collection for duplicates. Do not invent missing ports, cities, or ISP assignments, and do not probe private/operator networks to classify a report. -## Step 3: Read Existing Documentation +## Apply the requested update -- Use the Chinese FCC summary document recorded in `references/update-memory.md`. -- Read the current document to understand existing regions, ISPs, IPs, and formatting. -- Check whether reported IPs are already documented before adding anything. +Update `docs/reference/cn-fcc-collection.md` using the province → ISP grouping in the reference. Within each group, list addresses without city annotations before city-specific entries. Extend an existing entry's supported locality when appropriate rather than duplicating it. Keep source comment IDs, authors, and dates in the processing record for traceability. -## Step 4: Update the Chinese Document +After Chinese changes, use [translate-docs-zh-en](../translate-docs-zh-en/SKILL.md) for `docs/en/reference/cn-fcc-collection.md`. Check that address/port pairs and locality annotations agree across languages. -- Add newly reported working FCC IPs with region, ISP, and city/location information when available. -- Do not duplicate existing IPs. -- Maintain existing Markdown style and grouping. -- Organize entries by province, then ISP. -- Within the same province and ISP, list addresses without city annotations first, then addresses with city annotations. -- For non-working reports, ask before removing or marking inactive unless the user already gave explicit removal instructions. -- Add notes only when the document convention supports them and the uncertainty is important. - -## Step 5: Acknowledge Contributors - -For each comment whose IP was added to the document, add a rocket reaction: +When contributor acknowledgment is authorized, add a rocket only to a comment actually used for an update and only if the current account has not already reacted: ```bash -gh api repos/stackia/rtp2httpd/issues/comments/{comment_id}/reactions -f content=rocket +gh api repos/stackia/rtp2httpd/issues/comments//reactions -f content=rocket ``` -- React only to comments actually used for document updates. -- Do not react to questions, discussions, or duplicate reports. -- If the reaction already exists from a previous run, skip it. - -## Step 6: Summarize Changes - -Prepare a concise summary listing: - -1. Number of comments reviewed. -2. Number classified as actionable vs. non-actionable. -3. IPs added, removed, or flagged for review. -4. Source comment authors and dates for traceability. - -## Step 7: Sync English Translation - -After Chinese document changes are complete, use the `translate-docs-zh-en` skill to update the -corresponding English document. Provide the updated Chinese document path and the change summary so -the translation update can focus on modified sections. - -## Quality Assurance - -- Validate every IP address and port before adding it. -- Confirm regional and ISP attribution from source comments. -- Verify no still-working IPs were removed accidentally. -- Preserve document formatting and ordering. -- If no actionable updates exist, report that clearly and make no doc changes. - -## Updating Sync Memory +Do not react to duplicate-only reports or general questions. If a mutation returns an uncertain result, inspect existing reactions before retrying. -After each run, update `references/update-memory.md` with: +## Completion and checkpoint -- `last_processed_comment_date`: set to the `created_at` timestamp of the last processed comment. -- An update record noting what changed. +Report comments reviewed, addresses added/changed, and unresolved reports with source links. If nothing actionable was found, leave the collection unchanged. -You may also record reusable classification lessons, known platform/port patterns, or stable -document conventions. Do not record temporary task state, speculation, or duplicate repo-level -instructions. +For a completed sync, update `references/update-memory.md` with a concise result and the latest fully processed comment's `created_at` as `last_processed_comment_date`, without moving the checkpoint backward. Record any reviewed edits by comment ID and keep unresolved items explicitly pending; do not advance past work lost to a fetch or translation failure. A check-only run leaves this record unchanged. diff --git a/.agents/skills/translate-docs-zh-en/SKILL.md b/.agents/skills/translate-docs-zh-en/SKILL.md index a1b5aeab..db2b9bcc 100644 --- a/.agents/skills/translate-docs-zh-en/SKILL.md +++ b/.agents/skills/translate-docs-zh-en/SKILL.md @@ -1,148 +1,33 @@ --- name: translate-docs-zh-en -description: > - Translate and synchronize rtp2httpd Chinese documentation into English. ALWAYS use this skill - whenever Chinese docs under docs/ need English translations under docs/en/, when Chinese doc diffs - need to be reflected in existing English files, when VitePress English sidebar entries need to - mirror Chinese docs, or when translation terminology needs to stay consistent across docs. -argument-hint: "[new|update|batch] [docs path or diff context]" +description: Translate Chinese rtp2httpd docs into docs/en/ and synchronize English navigation, links, and terminology after source changes. --- -# Translate rtp2httpd Docs from Chinese to English +# Chinese to English Documentation -Use this skill to keep English documentation faithful to the Chinese source of truth. -Chinese docs under `docs/` are authoritative; English docs under `docs/en/` are translations. +Chinese files in `docs/` are authoritative; map each to the same relative path under `docs/en/`. Use [translation-memory.md](references/translation-memory.md) for established terminology and alert conventions when translating affected content. -Before translating, read `references/translation-memory.md` for established terminology and -project-specific conventions. Update that reference when you confirm stable terminology, -documentation structure, sidebar patterns, or other reusable translation decisions. +For a new page, translate the full source. For an update, use the Chinese diff and surrounding context to update the corresponding English sections. For a batch, apply the same rules to the requested files and their navigation. Do not rewrite unrelated translations or change the Chinese source to resolve a translation ambiguity; flag a source inconsistency while completing unaffected work. -## Core Responsibilities +## Preserve meaning and structure -1. Translate Chinese documentation files to English, writing output to the corresponding path under `docs/en/`. -2. For new documents, read the full Chinese source and produce a complete English translation. -3. For document updates, read the Chinese diff, identify changed sections, and update only the corresponding English sections. +- Preserve heading hierarchy, lists, tables, qualifiers, and numeric values. Translate user-facing frontmatter values, keeping keys intact. +- Preserve source markup: GitHub alert markers such as `> [!NOTE]` and any existing VitePress container delimiters. Translate their prose, including bold alert lead-ins. +- Keep executable syntax, shell commands, config keys, identifiers, URLs, addresses, ports, paths, product names, and acronyms unchanged. In examples, translate human-readable comments and illustrative display values such as `group-title="央视"` to `group-title="CCTV"` only when doing so preserves behavior. Keep semantically required literals unchanged. +- Retain Chinese terms only where the established convention requires them, with an English explanation. Keep English concise and faithful to the source. -## Translation Rules +## Resolve links by their role -### Structure Preservation +- Site-page links should target the English counterpart, e.g. `/guide/quick-start` → `/en/guide/quick-start`. Relative page links can stay relative when they resolve within the mirrored English tree. Avoid adding a second `/en/` prefix. +- Shared assets stay shared: adjust a relative path to resolve from the English file's location (usually one extra `../`); root-relative asset URLs remain unchanged. For example, `docs/guide/a.md` linking `../images/foo.png` needs `../../images/foo.png` in `docs/en/guide/a.md`. +- External URLs remain unchanged, including Chinese-only tutorials. +- For translated headings, update generated anchor targets and incoming links. Preserve explicit anchor IDs when the source defines them. +- When link text names a page, use that page's English H1. Descriptive inline link text should retain its intended meaning. -- Mirror the Chinese Markdown structure, heading hierarchy, link anchors, code blocks, and document layout. -- Translate frontmatter fields containing user-facing text. Keep frontmatter keys unchanged. -- Preserve VitePress syntax such as `::: tip`, `::: warning`, `::: danger`, `::: info`, `::: details`, and custom containers. Translate the content inside containers. +## Navigation and completion -### Link Handling +For new, moved, or retitled pages, mirror the relevant Chinese navigation in the English locale of `docs/.vitepress/config.ts`. Keep the Chinese locale unchanged unless the task includes changing it. After title/anchor changes, find and update affected English references. -- Internal/site links: add the `/en/` prefix. Example: `/guide/quick-start` becomes `/en/guide/quick-start`. -- External links: keep unchanged. -- Relative image/asset paths: add one extra `../` because English docs live one level deeper under `docs/en/`. - - `docs/index.md` using `./images/foo.png` becomes `docs/en/index.md` using `../images/foo.png`. - - `docs/guide/bar.md` using `../images/foo.png` becomes `docs/en/guide/bar.md` using `../../images/foo.png`. -- Absolute paths starting with `/`: keep unchanged. -- Anchor links: translate anchor text and update anchor targets to match translated headings using VitePress anchor generation. +Check the diff for complete translation, valid links/assets, and preserved example behavior. Run `pnpm run docs:build` when page paths, navigation, anchors, or Markdown/VitePress structure changed; a wording-only edit can use focused review. Correct failures caused by the translation before finishing. -### Do Not Translate - -- Code inside inline or fenced code blocks, shell commands, configuration parameter names, and variable names. -- URLs, IP addresses, and port numbers. -- Product and project names such as rtp2httpd, udpxy, VLC, and FFmpeg. -- File paths and filenames. - -### Special Handling - -- For China-specific concepts, retain the original term only when needed and add an English explanation. -- Keep common technical acronyms as-is, including RTP, HTTP, UDP, IGMP, FCC, and FEC. -- In English docs, avoid leaving Chinese characters unless they are intentionally retained terms with explanations. -- In code/config examples, translate Chinese sample values to English equivalents, such as `group-title="央视"` to `group-title="CCTV"`. - -## English Documentation Style - -- Use clear, concise technical English that reads naturally to a native English-speaking developer. -- Match the original tone without becoming overly formal or casual. -- Prefer active voice where natural. -- Preserve precise meaning. Do not generalize, soften, omit qualifiers, or change numeric values. -- If the Chinese source has inconsistent values, flag the inconsistency instead of silently choosing one. - -### Page Titles - -- "详解" / "参数详解" -> "Reference", for example "配置参数详解" -> "Configuration Reference". -- "说明" -> a simple noun form or "Guide", for example "URL 格式说明" -> "URL Formats". -- "报告" -> drop it when redundant, for example "性能测试报告" -> "Performance Benchmark". -- "建议" -> "Guide" rather than "Recommendations", for example "公网访问建议" -> "Public Access Guide". -- Use plural forms when a page covers multiple items, such as "URL Formats". -- Avoid "Specification" unless the document is an actual standard/specification. - -### Terminology - -- Prefer "build" over "compile" for software construction, except when referring to literal commands such as `make ... compile`. -- Translate "后台" as "admin panel" or "admin interface", not "backend". -- Translate "花屏" as "artifacts" and "卡顿" as "stuttering". -- Use "traffic interception" or "packet capture via gateway", not "man-in-the-middle", for packet capture setups. - -## Workflow - -### New File Translation - -1. Read the Chinese source file completely. -2. Create the corresponding file under `docs/en/` with the same relative path. -3. Translate the entire document using the rules above. -4. Check whether `docs/.vitepress/config.ts` needs a matching English sidebar entry. -5. Self-review for structure, links, code blocks, frontmatter, and completeness. - -### Existing File Updates - -1. Read the diff of the Chinese document to identify changed sections. -2. Open the existing English translation. -3. Locate corresponding sections in the English file. -4. Update only the changed sections. -5. Verify the updated surrounding context still reads naturally. - -### Batch Translation - -1. List all files that need translation. -2. Translate in logical order, usually overview/index files first. -3. Verify the English sidebar in `docs/.vitepress/config.ts` is complete. - -## VitePress Config Updates - -- Locate the English locale sidebar configuration, usually under `en` or `/en/`. -- Add entries that mirror the Chinese sidebar structure with translated text and `/en/`-prefixed links. -- Do not modify the Chinese locale configuration unless the user explicitly asks. - -## Quality Checklist - -Before finishing each file, verify: - -- All headings are translated and hierarchy matches the Chinese source. -- Internal links have `/en/` prefixes. -- External links are unchanged. -- Relative image/asset paths have one additional `../`. -- Code blocks are untouched. -- Product and project names are not translated. -- VitePress container syntax is preserved. -- Frontmatter text fields are translated. -- No unintended Chinese characters remain in English docs. -- Cross-reference link text matches the target page H1. - -## Cross-Reference Consistency - -When a document links to another page, the link text must match the H1 title of the target page. -After translating or updating a page title, search English docs for references to that page and -update stale link text in related documentation, next steps sections, and inline references. - -## Updating Translation Memory - -Use `references/translation-memory.md` as the persistent project memory for this skill. - -Record: - -- Stable terminology choices. -- Document structure and sidebar conventions. -- China-specific concepts and their established English rendering. -- File organization patterns between Chinese and English docs. - -Do not record: - -- Session-specific task state. -- Speculative or unverified conclusions. -- Anything that duplicates or contradicts repo instructions in `AGENTS.md` or `CLAUDE.md`. +Keep the terminology reference limited to confirmed, reusable translation choices; update it only when this work establishes one. Do not add session logs or duplicate repository rules. diff --git a/.agents/skills/translate-docs-zh-en/references/translation-memory.md b/.agents/skills/translate-docs-zh-en/references/translation-memory.md index c7e92518..5832ef19 100644 --- a/.agents/skills/translate-docs-zh-en/references/translation-memory.md +++ b/.agents/skills/translate-docs-zh-en/references/translation-memory.md @@ -43,6 +43,16 @@ - 行为变更 -> Behavior change - 响应头 -> response header - 上游流 Metadata -> upstream stream metadata (the `R2H-*` headers) +- Prefer "build" over "compile" except in literal commands. +- For packet-capture setups, use "traffic interception" or "packet capture via gateway", not "man-in-the-middle". + +### Page Titles + +- 详解 / 参数详解 -> Reference, for example Configuration Reference. +- 说明 -> a noun title or Guide, for example URL Formats. +- 报告 -> omit when redundant, for example Performance Benchmark. +- 建议 -> Guide, for example Public Access Guide. +- Use plural titles for multiple items; reserve Specification for an actual standard. ### China-specific Terms @@ -62,9 +72,9 @@ ## Admonition Style -Both doc trees use GitHub alert syntax (`> [!NOTE]`, `> [!TIP]`, `> [!IMPORTANT]`, -`> [!WARNING]`, `> [!CAUTION]`), **not** VitePress `::: tip` / `::: warning` containers. -VitePress renders both, but the whole repo uses the GitHub form — keep it that way. +The project convention is GitHub alert syntax (`> [!NOTE]`, `> [!TIP]`, `> [!IMPORTANT]`, +`> [!WARNING]`, `> [!CAUTION]`). Preserve any existing VitePress containers when translating; +changing alert formats is a separate source edit. GitHub alerts cannot carry a custom title. If a Chinese source needs one, put it as a bold lead-in inside the alert body (`> **行为变更**:…`) and mirror that in English. diff --git a/AGENTS.md b/AGENTS.md index 19895305..98202102 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,59 +1,38 @@ # rtp2httpd -RTP/IPTV multicast-to-HTTP streaming daemon written in C, with a React/TypeScript web UI embedded into the binary. +RTP/IPTV multicast-to-HTTP daemon in C11, with an embedded React/TypeScript web UI. -## Architecture +## Project constraints -- Pure C (C11) — do NOT introduce C++ code or features -- Multi-worker model via `fork()` — workers are independent, only sharing stats via shared memory -- Cross-platform: Linux, macOS, FreeBSD — use `#ifdef` for platform-specific APIs -- Web UI (React/Vite) is compiled and embedded as `src/embedded_web_data.h` — never edit this file directly -- If `src/embedded_web_data.h` changes from a Web UI rebuild, do not commit it unless explicitly requested. -- Config file format is INI (`rtp2httpd.conf`), not YAML/JSON +- Keep runtime code in C, with platform guards for Linux, macOS, and FreeBSD APIs. +- Workers use `fork()`: globals are worker-local; cross-worker state needs shared memory or IPC. +- Build with CMake. Configuration is INI (`rtp2httpd.conf`). +- Never hand-edit `src/embedded_web_data.h`. Commit it only for an authorized release or when explicitly asked to commit the generated header. +- Discuss new dependencies first; installing locked dependencies is routine setup. -## Code Style — C +## Conventions -- Indentation: 2 spaces, no tabs -- Structs: `_s` suffix for struct tag, `_t` for typedef (`struct connection_s` → `connection_t`) -- Header guards: `#ifndef __MODULE_H__` / `#define __MODULE_H__` -- Logging: always use `logger()` from `utils.h` — never `printf` / `fprintf` -- Strings: use `snprintf` / `strncpy` — never `sprintf` / `strcpy` +- C: 2-space indentation, `_s` struct tags, `_t` typedefs, `__MODULE_H__` guards. Log with `logger()` from `utils.h`; use `snprintf`/`strncpy`, not `sprintf`/`strcpy`. +- TypeScript/JavaScript: follow `biome.json`; prefer Tailwind utilities when they express the styling clearly. +- Use Corepack/pnpm and `.nvmrc` through nvm when available, otherwise system Node. Use uv for Python dependencies and `uv run` for scripts. +- Chinese documentation in `docs/` is authoritative. Use the translation skill below when synchronizing `docs/en/`. +- Commit messages and PR titles use `type(scope): subject`. Do not add `Co-Authored-By` trailers. -## Code Style — TypeScript / JavaScript +## Task-specific guidance -- Formatter/linter: Biome (`biome.json`), line width 120, indent with tabs -- Follow `.nvmrc` via `nvm` + Corepack (`pnpm`) for JS tooling when available; if `nvm` is unavailable, fall back to the system `node`. -- Prefer Tailwind CSS utilities for styling; add custom CSS classes only when Tailwind cannot express the behavior clearly. +Read only the relevant guide; current commands live in `package.json` and `pyproject.toml`. -## Code Style — Python +- Local build/configuration: [build-run](.agents/skills/build-run/SKILL.md). +- E2E tests/harness: [e2e](.agents/skills/e2e/SKILL.md). +- Release notes/publication: [release](.agents/skills/release/SKILL.md). +- English docs/navigation: [translate-docs-zh-en](.agents/skills/translate-docs-zh-en/SKILL.md). +- Community FCC reports: [sync-fcc-ip-docs](.agents/skills/sync-fcc-ip-docs/SKILL.md). +- Player mock upstreams: [devlab](tools/devlab/README.md). -- Package manager: uv — do not use pip/pipenv/poetry -- Always run Python scripts via `uv run` — do not use `python` directly +## Completion and boundaries -## Documentation +Complete the requested change and affected checks, fixing regressions it causes. Local E2E tests use mock upstreams and temporary files; builds and affected tests can run and retry without per-step approval. Docs/instruction edits do not require daemon builds or the full E2E suite. -- Chinese docs (`docs/`) are the **single source of truth** -- English docs (`docs/en/`) are translations — always use the `translate-docs-zh-en` skill, do not translate directly -- Built with VitePress: `pnpm run docs:build` +Use unused local ports and clean up task-owned processes/artifacts. Deployments and release publication need authorization; an existing request suffices. Respect draft-, review-, or upload-only scope. -## Git - -- Commit messages and PR titles use Conventional Commits: `type(scope): subject` - -## Do NOT - -- Use Linux-only APIs without `#ifdef` platform guards -- Use npm/yarn — this project uses pnpm -- Use autotools — this project uses CMake -- Add dependencies without discussing first - -## Cursor Cloud specific instructions - -Toolchain is pre-installed and refreshed by the startup update script (`pnpm install --frozen-lockfile` -then `uv sync --group dev`). Standard commands live in `package.json` scripts and the `build-run` / -`e2e` skills — use those rather than reinventing them. - -Non-obvious notes: - -- **Dev lab**: `tools/devlab/devlab.py` starts local mock upstreams for Web UI/player debugging, - including live, catchup, RTP multicast, HLS, and RTSP scenarios. See `tools/devlab/README.md`. +Report results, validation, and blockers. Keep conversation/audit history out of deliverables.