docs(native): record product gate no-go

This commit is contained in:
AAA committed 2026-08-19 18:57:46 +08:00
1 parent 5b66cb22a9
commit 7efbb194b4
5 files changed
+159 -36

No files matched your search

+2 -1
View File
@@ -10,7 +10,7 @@
- [Peer-assisted media research](./research/peer-assisted-media.md): browser shared-encode limits, standard relay re-encoding, deterministic sticky topology, runtime relay-capacity extension, and abandon gates.
- [Low-server-cost media routes](./research/low-server-media-routes.md): per-edge TURN versus SFU-root topology, hop-level traffic accounting, privacy-safe ICE evidence, dual-TURN shadow gates, and failure-only final fallback.
- [Advanced peer distribution](./research/advanced-peer-distribution.md): multi-tree/SVC, encoded-object relay, native RTP forwarding, FEC/network-coding, MoQ, and measurable go/no-go gates.
- [Native shared-encode sender](./research/native-shared-encode-sender.md): consolidated Draft #16/#18/#22/#23/#25/#28 evidence ladder, current Pion/WebCodecs verdicts, and product stop line.
- [Native shared-encode sender](./research/native-shared-encode-sender.md): consolidated Draft #16/#18/#22/#23/#25/#28 ladder, unclassified product-gate failure, staged revalidation, and stop line.
- [Realtime quality adaptation](./research/realtime-quality-adaptation.md): correlated capture/send/receive diagnosis, production generation-rebuild gate, demand-driven dual representations, and bounded simulcast/Dynacast/SVC checks.
- [Browser screen-audio quality](./research/browser-screen-audio-quality.md): capture/source compatibility, standard readback limits, Opus negotiation boundaries, and the retained no-control decision.
- [Agent context governance research](./research/agent-context-governance.md): official Codex, Claude Code, Hermes Agent, and GitHub practices adopted by this repository.
@@ -18,6 +18,7 @@
- [ADR-0002](./adr/0002-persistent-protected-rooms.md): optional SQLite persistence, protected sequential room IDs, and room lifecycle.
- [ADR-0004](./adr/0004-peer-assisted-media-experiment.md): proposed, experiment-only standard-WebRTC peer-assisted spike with hard fanout and failure gates.
- [ADR-0005](./adr/0005-automatic-hybrid-media-routing.md): proposed, default-off automatic P2P/peer/SFU preparation, token-free standby prewarm, commit, failback, and rollback with a hard host-edge budget.
- [ADR-0006](./adr/0006-fixed-high-native-sender-canary.md): proposed fixed-`HIGH` native canary, current no-go result, and staged revalidation boundary.
- [ADR-0007](./adr/0007-demand-driven-dual-representation-quality.md): accepted demand-driven `HIGH + at most one on-demand LOW` quality policy, path-level hysteresis, evidence contract, and SVC boundary.
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.
@@ -0,0 +1,87 @@
# ADR-0006: Fixed-HIGH Native Sender Canary
- Status: Proposed - Product Gate No-Go
- Date: 2026-08-19
## Context
The Draft #16/#18/#22/#23/#25/#28 ladder proved isolated WebCodecs/Pion
properties, not integration with Screener's current rooms, signaling, ordinary
browser viewers, or per-edge ICE/TURN. A native sender remains a later
optimization and must not be presented as available product behavior.
## Retained Boundary
Any renewed product-wiring canary stays deliberately narrow:
- one fixed VP8 `HIGH` representation at 1280x720, 30 fps, and a 3 Mbps ceiling;
- one WebCodecs encoder object feeding at most two independent Pion WebRTC legs;
- the current strict ordinary-P2P signaling wire and unmodified browser viewers;
- independent ICE/TURN, RTP/RTCP, congestion, and lifecycle state per leg;
- read-only feedback, with PLI/FIR limited to requesting a shared keyframe; and
- bounded local queues, fail-closed critical errors, and sanitized local-only
diagnostics.
This canary excludes room-wide minimum feedback, automatic quality control,
`LOW`, viewer C reporting, peer-assisted/SFU routing, audio, packaging, and any
claim of one physical or hardware encoder. A third viewer must never create a
third host edge. The missing audio path remains a product-acceptance gap.
## 2026-08-19 Gate Result
The sole authorized product-wiring run used a local current Node ordinary-P2P
server with peer assistance and TURN disabled, the loopback helper UI, and
isolated headless Chrome.
| Checkpoint | Retained evidence |
| --- | --- |
| Node and native host | The listener started and `/api/start` returned an invite. By the attempted branch's control flow, room creation and host signaling authentication completed first. |
| Local encoder | The configuration acknowledgement preceded construction; the probe observed one `VideoEncoder` object and encoded output. Helper ingress and source RTP were not proven. |
| Viewer page | The page and probe loaded, then the 30-second combined decoded-and-rendered condition timed out. |
| Downstream path | Viewer auth, `peer-joined`, SDP/candidates, PC states, source RTP/edge packets, inbound RTP, video readiness, and console state were not retained. |
The earliest missing evidence checkpoint is viewer authentication, not a proven
authentication failure. The probe discarded false samples, swallowed some
stats errors, and used render-callback evidence without readiness/current-time
fallback. It therefore cannot identify the first runtime break or exclude a
probe-only false negative.
The result is `no-go-unclassified`. No second viewer, two-edge proof,
third-viewer wait/FIFO promotion, or direct/TURN pairing ran. The attempted
native branch must not be opened or merged as product code from this state.
## Staged Revalidation
A new run requires separate authorization and must stop at the first failed
stage while retaining a bounded final-negative snapshot:
1. Prove host room/auth, local config acknowledgement, one encoder object,
bridge ingress, frame decode, source RTP, and zero critical errors.
2. Prove viewer auth, `peer-joined`, exact offer/answer/candidate exchange, and
both PeerConnection state timelines.
3. Prove one viewer's inbound packets, `framesDecoded`, video readiness/current
time/dimensions, and rendered frames.
4. Add viewer 2 and prove two independent legs while encoder instances stay one
and host media edges stay at most two.
5. Add viewer 3, prove no third edge and a host-side waiting observation while
the viewer keeps its existing waiting state, then close viewer 1 and prove
FIFO promotion plus decoding/rendering.
6. If the isolated environment has valid TURN configuration, prove one direct
and one forced-TURN leg; otherwise record the gap once without retrying.
Only after these stages pass may separate performance, quality, loss,
reconnect, browser, audio/A-V-sync, packaging, and license gates begin. Do not
expand the feature to make the diagnostic gate pass.
## Consequences
ADR-0006 records a bounded candidate and its failed first product gate, not an
accepted architecture or shipped sender. The deployed Web sender and ordinary
viewer remain unchanged. ADR-0007 continues to own demand-driven
`HIGH`/on-demand `LOW`; this canary cannot advance or replace it.
## References
- `docs/research/native-shared-encode-sender.md`
- `docs/adr/0007-demand-driven-dual-representation-quality.md`
- `docs/research/browser-screen-audio-quality.md`
+2 -2
View File
@@ -19,7 +19,7 @@ Last updated: 2026-08-19
- Use direct host P2P for one or two viewers. Keep ADR-0004 off for broad production traffic: two mobile leaves can consume both host roots. Use the exact-room allowlist only on an isolated canary until its resource, quality, recovery, TURN, and browser/mobile gates pass.
- Browser relays resend remote `MediaStreamTrack` values and re-encode at each hop; WebRTC does not guarantee a shared encoder across peer connections, so measure the cost.
- Keep browser experiments bounded; spike native RTP relay, encoded-object striping, SVC, and FEC separately.
- Plan a separate native-sender ADR regardless of browser relay results. One encode may feed at most two WebRTC packetizers, reducing encode work but not per-edge upload; it cannot rescue a failed browser gate.
- ADR-0006 retains a fixed-`HIGH` native canary boundary, but its sole product-wiring run reached only host setup and one encoder output before the first-viewer decoded/rendered gate timed out. Downstream checkpoints were not retained, so the result is no-go-unclassified, not a diagnosed product bug. No native product code is accepted; revisit only through the staged evidence gate.
- Optional single-node SFU capacity is failure-only final fallback; it never defaults to whole-room fanout. Stable dual-TURN host roots are only a future exact-room shadow comparison against the same SFU roots, with real hop/egress and E2EE/operator accounting, until evidence amends ADR-0005. PR #12's whole-room SFU mode is superseded.
- Keep ADR-0005 routing default-off: discrete failures drive peer/SFU recovery, full host fanout uses break-before-make, and mobile/iPad clients stay leaves. PR #20 prewarm passes locally; public transport/load gates remain.
- Local reparenting is a later candidate: start with unassigned relay admission rescue; do not block current work.
@@ -74,5 +74,5 @@ These are measurement gates, not performance claims.
- Documentation index and current phase: `docs/README.md` and `docs/status.md`
- Requirements and design: `docs/需求理解.md` and `docs/方案设计.md`
- Media research: `docs/research/`, indexed by `docs/README.md`
- Architecture: ADR-0001/0002, proposed ADR-0004/0005, accepted ADR-0007, and rejected/superseded ADR-0003
- Architecture: ADR-0001/0002, proposed ADR-0004/0005, no-go proposed ADR-0006, accepted ADR-0007, and rejected/superseded ADR-0003
- Deployment and maintenance: `docs/deployment.md`, `docs/maintenance.md`, `AGENTS.md`, and `.codex/`
+37 -2
View File
@@ -4,8 +4,8 @@
- Scope: one Windows game-capture sender, one encoded video stream, and at most
two independent standard WebRTC media edges
- Status: stacked Draft PRs #16/#18/#22/#23/#25/#28 pass one bounded live
two-leg WebCodecs/Pion candidate; product and physical shared encoding remain
unproven
two-leg WebCodecs/Pion candidate. The first product-wiring gate is
no-go-unclassified; product and physical shared encoding remain unproven.
## Decision Input
@@ -108,6 +108,41 @@ bounded RTP/RTX queues. The already bounded #23/#28 ladder uses the same
minimum-edge safety rule and must not invent a custom SRTP, ICE, or congestion
protocol. A product route must follow ADR-0007 instead.
## Fixed-HIGH Product-Wiring Attempt
ADR-0006 retains one narrow candidate: fixed VP8 1280x720@30 with a 3 Mbps
ceiling, one WebCodecs encoder object, at most two independent Pion legs, the
strict ordinary signaling wire, unmodified viewers, read-only feedback, and no
audio or automatic `LOW`. This is a proposed test boundary, not current product
behavior or hardware-encoder evidence.
The sole authorized 2026-08-19 local product gate is
`no-go-unclassified`:
| Checkpoint | Retained evidence |
| --- | --- |
| Ordinary Node server | The local listener started with peer assistance and TURN disabled. No raw signaling frames were retained. |
| Native room/host | `/api/start` returned an invite, which in the attempted branch followed room creation and host signaling authentication. |
| Local encoder | Config acknowledgement preceded construction; the probe observed one `VideoEncoder` object and encoded output. Helper ingress, frame decode, and source RTP were not proven. |
| Viewer page | The page and probe loaded, but the 30-second combined decoded-and-rendered condition timed out. |
| Downstream lifecycle | Viewer auth, `peer-joined`, SDP/candidates, PC states, source RTP/edge packets, inbound RTP, video readiness, and console state were not retained. |
The first missing evidence checkpoint is viewer authentication, not a proven
authentication failure. The harness discarded each false sample; its reused
`progress()` path can omit a connection after a swallowed `getStats()` error;
and render evidence had no readiness/current-time/video-dimension fallback.
The timeout therefore cannot locate the runtime break or rule out a probe-only
false negative. The static code review found no obvious protocol disconnect,
which is not runtime evidence.
No second viewer, two-edge proof, third-viewer waiting/FIFO promotion, or
direct/TURN pair ran. Do not treat the attempted branch as usable or mergeable
product code. A separately authorized rerun must retain bounded checkpoints for
host auth/config/bridge/source RTP, viewer auth/presence/signaling/PC state,
inbound decode/render, then add viewers 2 and 3 in order. TURN is tested once
only when the isolated environment has valid credentials. ADR-0006 owns the
exact staged gate and stop line.
## Stacked Validation Ladder
These Draft PRs are deliberately stacked research, not six product features:
+31 -31
View File
@@ -5,19 +5,19 @@ Last updated: 2026-08-19
## Phase
`https://share.bonfire.icu` runs `769de201f7cc` with simplified entry, bounded
clarity-first controls, persistent rooms, and reusable links. Peer/LiveKit is
unconfigured, so production remains one host connection per viewer and P2P/TURN.
quality controls, and persistent links. Peer/LiveKit is off; production remains
one host connection per viewer with per-edge P2P/TURN.
## Current Snapshot
- Capture precedes room creation; live source/quality changes preserve peers and picture pause keeps audio/connections. Quality defaults to `maintain-resolution` with balanced/fluid options.
- Optional `ACCESS_PASSWORD` uses a stateless 12-hour HMAC HttpOnly Strict cookie; host auth stays internal and signaling role-bound.
- Without `ROOM_DATABASE_PATH`, rooms are temporary. With it and the password, sequential links persist after stop; SQLite stores only room ID and host-token digest.
- Direct ICE is preferred independently per media edge. Authenticated TURN/UDP and TURN/TCP are required production fallbacks; TURN/TLS is optional.
- Hidden routing is `direct P2P -> peer-assisted -> optional SFU roots`; TURN is selected per edge, and SFU roots retain bounded peer descendants. Exact-room canaries use `PEER_ASSISTED_ROOM_IDS`; production is off.
- Configured fallback adds a non-secret standby URL and token-free DNS/TLS warmup; absent configuration adds no field, import, request, participant, or edge.
- Each viewer advertises relay capacity 0/1; detected mobile/iPad clients are leaves. Withdrawal does not move a healthy edge. Browser relays stay one-child and host two-child.
- Production has memory-only video settings and manual readback. Automatic `HIGH`/`FALLBACK` plus one shared on-demand `LOW` is accepted but not deployed; host A+B exists, while C/actions do not. Browser audio is request/presence-only.
- Capture precedes room creation; source/quality changes preserve peers, and picture pause keeps audio/connections. Quality defaults to `maintain-resolution`.
- Optional access uses a stateless 12-hour HMAC HttpOnly Strict cookie; host auth is internal and signaling role-bound.
- Rooms are temporary without `ROOM_DATABASE_PATH`; password plus SQLite persists sequential links and only room ID/host-token digest.
- Direct ICE is preferred per edge; authenticated TURN/UDP and TURN/TCP are required fallbacks. TURN/TLS is optional.
- Hidden routing is `direct P2P -> peer-assisted -> optional SFU roots`; exact-room canaries use `PEER_ASSISTED_ROOM_IDS`, and production is off.
- Configured fallback exposes only a non-secret standby URL and token-free DNS/TLS warmup; absent configuration adds no runtime path.
- Relay capacity is host 2/browser 1; detected mobile/iPad clients are leaves, and withdrawal does not move a healthy edge.
- Video settings are memory-only/manual. Accepted per-path `HIGH`/`FALLBACK` plus one shared on-demand `LOW` is not deployed; host A+B exists, C/actions do not. Audio is request/presence-only.
## Verified Evidence
@@ -25,8 +25,8 @@ unconfigured, so production remains one host connection per viewer and P2P/TURN.
- Chrome 151 synthetic `1/3/5/8` kept host/relay edges at 2/1 and all viewers decoding; slowest first frame was 1.05 seconds and one three-viewer relay close recovered in 5.32 seconds.
- Synthetic Chrome 151 720p30 propagated balanced/clarity to one host and three viewers without peer changes; decoding and 2/1 fanout continued. This proves control continuity only.
- Chrome 151/LiveKit localhost A/B measured cold active/render at 1.481/2.257 seconds versus standby 0.200/0.320 with host edges at two. It includes SDK/prewarm and is not public-network evidence.
- Atomic activation passed health/access, exact bundle, SQLite preservation, services, and logs; the old release is rollback-ready. Responsive 320/375/390 px checks passed without live media.
- Host A+B aligns capture with one unique outbound RTP, interval/identity/deltas, `remoteId`, and path. Its same-transport codec exposes nullable MIME/self-describing profile token/allowlisted fmtp/`scalabilityMode`; tokens imply no quality or capability conclusion, and no raw fmtp/SDP/stats are added. Stale, ambiguous, reset, or cross-transport evidence stays unknown. Independent review found no P1/P2 issues; the final local check passed 22 files/299 tests and both builds.
- Activation passed health/access, exact bundle, SQLite preservation, services, and logs; rollback is ready. Static 320/375/390 px checks passed.
- Host A+B binds capture to one unique outbound RTP, interval/deltas, `remoteId`, path, and same-transport nullable codec/profile/fmtp/`scalabilityMode`. It stores no raw fmtp/SDP/stats and infers no quality/capability; stale, ambiguous, reset, and cross-transport evidence stays unknown. Review found P1/P2=0; 22 files/299 tests and both builds passed.
- HTTPS/WSS, access and room/WebSocket auth, renewal, public STUN, and authenticated TURN/UDP/TCP relay-only paths pass; TURN/TLS is off.
- Draft native ladder #16/#18/#22/#23/#25/#28 passed one bounded 720p30 two-leg loop with an experiment-only minimum GCC target and one recovered loss; it is not product behavior.
@@ -35,30 +35,28 @@ unconfigured, so production remains one host connection per viewer and P2P/TURN.
- Full-resolution 30-minute runs, relay CPU/GPU, generational quality, controlled loss/RTT, depth latency, and game capture are unverified.
- Android Chrome/iOS Safari leaves are unverified; runtime conservatively marks detected mobile/iPad clients as leaves.
- Silent partitions can wait 30 to 60 seconds for heartbeat detection before the default 5-second grace; this remains unverified.
- Real audio, heterogeneous clients, mobile lifecycle, quality/pause, room `1` republish, and sustained profiles are unverified. Poor movie/video audio is user-reported but unclassified; after video A+B, diagnose capture settings, codec/fmtp, actual bitrate, loss, jitter, concealment, jitter buffer, and A/V sync.
- Production `769de201f7cc` reportedly sustains native `qualityLimitationReason=bandwidth` and severe blur with one capable LAN/direct viewer; only Host refresh restores quality. Within the five-second grace, stable-clientId Viewer refresh retains its `peerId`/`HostPeer`, while Host refresh rebuilds peers/capture; churn persistence is also reported. This prioritizes but does not prove sender/PC/GCC/capture generation over network or lifecycle causes.
- ADR-0007 remains incomplete. Host A+B codec fields exist, but browser support and the reported case are unverified; authenticated C, the controller, and `LOW` are absent. Weak paths must share one `LOW`; fail closed protects `HIGH` only exceptionally, and an unreliable supported cohort fails acceptance.
- Real audio, heterogeneous clients, mobile lifecycle, quality/pause, room `1` republish, and sustained profiles are unverified. Reported poor movie/video audio needs capture, codec/fmtp, bitrate, loss/jitter/concealment/buffer, and A/V-sync evidence.
- Production reportedly stays bandwidth-limited and blurred for one capable LAN/direct viewer until Host refresh. Viewer refresh retains its peer during grace; Host refresh rebuilds peers/capture. This prioritizes, but does not prove, a sender/PC/GCC/capture-generation cause.
- ADR-0007 lacks verified browser codec evidence for the reported case, authenticated C, controller, and `LOW`. Weak paths share one `LOW`; an unreliable supported cohort fails acceptance.
- Browser relays re-encode. The native ladder proves one WebCodecs object, not hardware encode. GCC+RTX is no-go; no-RTX weakens stats. Broader audio/loss/routes/reconnect/browser/product/striping gates remain.
- The endpoint budget remains host/relay at most two downstream edges; the browser path is host two/viewer one.
- ADR-0005 permits SFU only after peer failure. Stable dual-TURN roots are shadow-only; exact-room TURN-root/SFU-root cost, recovery, and trust evidence is absent.
- Pinned LiveKit 1.13.5 Dynacast enables all qualities at or below the room's maximum request, so a `HIGH` root is expected to keep `LOW` enabled. It remains a bounded rejection/verification spike, not evidence that on-demand `LOW` can stop.
- Pinned LiveKit 1.13.5 Dynacast likely keeps `LOW` enabled under a `HIGH` maximum; this is a rejection/verification spike, not stop-on-recovery evidence.
- The corrected standby smoke is localhost/headless/video-only; public DNS/TLS reuse, transport, audio, shaping, load, mobile, endurance, and sub-25 ms overlap remain open.
- ADR-0006's sole fixed-`HIGH` product gate reached host setup and one encoder output, then timed out before viewer 1 decoded/rendered. Downstream checkpoints were lost, so it is no-go-unclassified; no native product path is accepted.
## Next Milestone
Use the completed local host A+B diagnostics to reproduce the same-LAN/direct
case. First rebuild one affected `HostPeer`/`connectionId` while retaining
capture and healthy peers; separately compare capture-only and full Host rebuilds with
A+B/C evidence and opaque generations. The affected viewer must recover without
moving healthy peers or breaking the host-edge cap. Only proven stuck
generations justify guarded/cooldown recovery; never reconnect periodically or
raise ceilings blindly.
Use host A+B to reproduce the same-LAN/direct case. Rebuild one affected
`HostPeer`/`connectionId` while retaining capture/healthy peers, then compare
capture-only and full Host rebuilds with A+B/C evidence. Recovery must preserve
healthy peers and the edge cap; only proven stuck generations justify guarded,
cooldown recovery.
After video A+B, separately run the movie/video audio A/B/C and sync diagnosis
in `docs/research/browser-screen-audio-quality.md`; `maxBitrate` is not a quality
fix. Then add minimal authenticated video C, the two-state controller, and
on-demand shared `LOW`. Later route work starts with manual ICE ground truth and
shadow-only exact-room TURN-root/SFU-root comparisons, not automatic migration.
Then run the separate audio A/B/C and sync diagnosis; `maxBitrate` is not a
quality fix. Next add authenticated video C, the two-state controller, and
on-demand shared `LOW`. Route work starts with manual ICE truth and shadow-only
TURN-root/SFU-root comparisons.
ADR-0004 still requires a full-resolution 30-minute `1/3/5/8` network,
resource, quality, latency, recovery, and browser/mobile-leaf matrix.
@@ -69,15 +67,17 @@ picture; silent partitions add detection delay.
ADR-0004 fails closed. Native shared encode and packet/layer striping remain
separate experiments, not ways to relabel a failed browser route.
The native ladder stops at #28; its minimum-of-two policy is not a product
candidate. Do not bypass stock GCC/RTX; no-RTX remains research-only.
The native ladder stops at #28; minimum-of-two is not a product candidate and
stock GCC/RTX must not be bypassed. ADR-0006 stays paused at no-go. Any new run
needs separate authorization and retained host, bridge/RTP, signaling/PC,
decode, and render checkpoints before adding viewers 2/3 or TURN.
## Blockers And Decisions
Peer assistance and automatic routing remain No-Go until ADR-0004/0005 pass.
Without LiveKit, two mobile leaves can fill both host roots; use an isolated
exact-room canary only.
Native shared encode and striping remain separate experiments.
Native shared encode is no-go-unclassified; striping remains separate research.
Deferred architecture audit: `docs/maintenance.md`.
- Whole-system versus selected-game audio for the first release.