Direction/progress pressure-test (D1-D6, P1-P6, R1-R3) and the boost.ai/Parloa/Vapi cohort scan (F1-F7, adjacencies A1-A11). Standing backlog for planning sessions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vNe64BBDkgo5oQVkBa3XF
349 lines
20 KiB
Markdown
349 lines
20 KiB
Markdown
# Adversarial Review — Direction & Progress
|
||
|
||
- **Date:** 2026-07-03
|
||
- **Scope:** Project direction (ADRs 0001–0008, vision revision, README/ARCHITECTURE/PORT_PLAN)
|
||
and progress to date (slices 1–3, all six crates, tests, CI) — reviewed adversarially, at the
|
||
request of the maintainer.
|
||
- **Method:** Full read of the ratified docs; source-level review of the shipped code; market
|
||
claims pressure-tested against the July-2026 landscape (web-verified, sources at the end).
|
||
- **Repo state reviewed:** `main` @ `c30a452` (Slice 3 — OpenAI Realtime brain, merged 2026-07-01).
|
||
Initial commit 2026-06-26 — the project is **one week old** at review time.
|
||
- **Status of findings:** Advisory. Nothing here is ratified; each finding names the doc or code
|
||
it attacks so it can be accepted, rebutted, or amended per the ADR process.
|
||
|
||
---
|
||
|
||
## Verdict
|
||
|
||
The direction has survived its own internal pressure-tests well. ADR-0007 (rent the trunk) was
|
||
the right kill, ADR-0008 (FOB/green-zone) is a genuinely useful mechanical rule, and the doc
|
||
discipline is why a review this specific is even possible one week in.
|
||
|
||
But three things have shifted under the docs:
|
||
|
||
1. **The knife that killed the SIP moat now hangs over the reflex loop** — the plank everything
|
||
currently rests on — and the docs still state it as durable white space.
|
||
2. **Two external events** (LiveKit's up-stack march; OpenAI Realtime SIP going GA) have eroded
|
||
competitive claims the README and ADR-0002 still assert.
|
||
3. **Two ratified ADR claims are now factually wrong** (ADR-0002's "structurally impossible"
|
||
spend argument post-0007; ADR-0004's AGPL escape hatch) and need amendment before anyone
|
||
else makes these arguments in public.
|
||
|
||
Progress is genuinely fast — three spearhead steps in one week, green CI, honest ADRs — but all
|
||
of it so far is the commodity layer every voice-agent framework ships as a quickstart, and the
|
||
project's single most important proof artifact (a latency/barge-in benchmark) does not exist.
|
||
|
||
---
|
||
|
||
## Part I — Direction
|
||
|
||
### D1. The reflex loop fails the test ADR-0007 invented — and it's now the whole moat
|
||
|
||
**Attacks:** ARCHITECTURE.md ("Biggest technical risk"), README pillar 1 / wedge bullet 1,
|
||
ADR-0002 §Consequences.
|
||
|
||
ADR-0007 killed first-party SIP with three arguments: highest cost, lowest differentiation,
|
||
everyone credible already stands on something mature. Apply the same test to the reflex loop,
|
||
which ARCHITECTURE.md now names "the risk that remains… also the most-differentiating work":
|
||
|
||
- **LiveKit Agents ships semantic turn detection today**, plus interruption handling, DTMF,
|
||
warm/cold SIP transfer, and sub-500 ms end-to-end voice agents as the documented 2026 default
|
||
stack.
|
||
- **Pipecat ships interruption handling** as a configuration flag.
|
||
- **OpenAI Realtime does server-side VAD and barge-in natively** — and slice 3 already consumes
|
||
OpenAI's `speech_started` rather than running local VAD.
|
||
|
||
Barge-in and turn-taking are **table stakes implemented by every voice-agent framework**, not
|
||
white space. Turn-taking *quality* is increasingly fought inside the speech-to-speech models
|
||
themselves — the layer rutster explicitly rents.
|
||
|
||
What residually differentiates is the **quality floor under load**: p99 barge-in kill-latency
|
||
when a GC'd Python framework is running 50 concurrent calls. That is a real claim but a narrow
|
||
one — and rutster currently has zero measurement infrastructure to demonstrate it (see P3).
|
||
|
||
Meanwhile the genuinely undisputed white space in the project's own competitor table — **ACD,
|
||
queues, human escalation, supervisor tooling, CDR-you-own** — sits at capability-ladder rungs
|
||
2–3, *behind* spearhead steps 4–6.
|
||
|
||
**The uncomfortable question:** if every framework has interruption handling and the s2s models
|
||
are internalizing turn-taking, what does an ADR-0007-style strategic review conclude about
|
||
step 4's billing as "the most-differentiating work"?
|
||
|
||
**Assessment:** barge-in is still worth building — core-authoritative playout demands it, and it
|
||
is needed plumbing. But it is *plumbing you need*, not *the moat*. The sequencing conclusion is
|
||
in R1 below.
|
||
|
||
### D2. OpenAI Realtime SIP is GA and the docs don't account for it
|
||
|
||
**Attacks:** README competitor table, vision-revision §10 (the momentum-fuel thesis),
|
||
spearhead step 5.
|
||
|
||
A SIP trunk can now be pointed **directly at `sip:$PROJECT_ID@sip.api.openai.com`** — with call
|
||
transfer via REFER and a hangup endpoint. Twilio ships a first-party tutorial for exactly this.
|
||
|
||
Consequences, stated bluntly:
|
||
|
||
- The spearhead's emotional payoff — *"I called my Rust box and an AI answered the phone"*, which
|
||
vision-revision §10 explicitly names as the momentum fuel for a solo multi-year build — is now
|
||
a **no-code product feature of the brain vendor**.
|
||
- Step 5's topology (CPaaS media fork → rutster → OpenAI) is a strictly more complex path to the
|
||
same demo.
|
||
- Even primitive escalation (REFER to a human's SIP URI) is achievable OpenAI-direct.
|
||
|
||
The README competitor table has rows for LiveKit, cloud CCaaS, cloud AI-voice, and dated OSS —
|
||
but **no row for the brain vendor itself**, which is now the most dangerous competitor on the
|
||
board: it is eating the transport and the reflexes from the other side.
|
||
|
||
What survives is real: multi-vendor brains, data ownership, recording, queueing, containment
|
||
analytics, the auditable boundary. But the pitch must be re-aimed at what trunk→OpenAI
|
||
*structurally cannot be*, and step 5 needs an honest answer to "why is this better than pointing
|
||
the trunk at OpenAI directly?"
|
||
|
||
### D3. The compliance wedge's target customer can't run anything currently built or scheduled
|
||
|
||
**Attacks:** README wedge bullet 2, ADR-0007 §"What this costs", vision-revision §3.
|
||
|
||
The wedge is declared "strongest for PCI / HIPAA / TCPA." Walk a regulated buyer through the
|
||
actual topology: layer-1 PSTN audio traverses the CPaaS's infrastructure, and *all* audio
|
||
traverses OpenAI's. The buyer who cares about the auditable boundary is precisely the buyer who
|
||
refuses both.
|
||
|
||
For that buyer, rutster needs **two things that don't exist**:
|
||
|
||
1. The out-of-tree SBC graduation path — green zone, unbuilt, not scheduled as a deliverable.
|
||
2. A self-hosted brain — and the open speech-to-speech ecosystem remains the weak link. The
|
||
viable self-hosted stack in mid-2026 is *cascade* (Whisper-class STT + open LLM +
|
||
Kokoro/Chatterbox-class TTS); true open s2s is immature. Yet the tap protocol and slice 3 are
|
||
shaped around an s2s API.
|
||
|
||
So the moat, today, is a **promissory note whose redemption depends on two third-party
|
||
ecosystems maturing**. ADR-0007's "What this costs" section underprices this: the cost is not
|
||
"data-ownership dilutes for PSTN in layer 1," it is "**the flagship customer segment cannot
|
||
deploy at all until graduation, and graduation depends on components nobody is building**."
|
||
|
||
Meanwhile the segment the built path *does* serve (CPaaS + OpenAI, indifferent to the boundary)
|
||
is Vapi's and OpenAI-direct's home turf.
|
||
|
||
**Two concrete moves:** (a) prove the tap is brain-agnostic *now* — a cascade-stack example brain
|
||
(Whisper + Llama + local TTS) in `examples/` would make the sovereignty story demonstrable and is
|
||
roughly a day of work given the existing Python brain examples; (b) promote "SBC-graduation
|
||
reference configuration" from a footnote to a scheduled deliverable.
|
||
|
||
### D4. The "structurally impossible for a 3-vendor stack" spend claim died with ADR-0007
|
||
|
||
**Attacks:** ADR-0002 pillar table, README pillar 3, PORT_PLAN §10 (toll-fraud row).
|
||
|
||
Under ADR-0003 (rutster terminates the trunk) the claim was structural: the brain can't spend
|
||
because rutster holds the wire. Under ADR-0007, **rutster doesn't hold the wire either — the
|
||
CPaaS does.** The gate is now "rutster holds the provider API credential and the brain doesn't,"
|
||
which is an IAM/configuration property, not a structural one:
|
||
|
||
- Any orchestrator that holds the credentials can make the same claim (Pipecat with a spend
|
||
counter and scoped credentials).
|
||
- The CPaaS itself ships spend limits.
|
||
- A mis-scoped provider credential bypasses rutster entirely.
|
||
|
||
ADR-0002's pillar table still reads "structurally impossible for a 3-vendor stack" while the
|
||
ADR-0007 architecture **is** a 3-vendor stack (CPaaS + rutster + brain). The pillar is now
|
||
self-describing.
|
||
|
||
**What remains genuinely structural** and should replace the current language: pacing,
|
||
half-duplex, and playout enforcement over media rutster terminates — the brain proposes audio
|
||
but cannot place it on a wire rutster doesn't authorize, and the gate cannot be *skipped* on any
|
||
egress rutster mediates. That is a true and defensible claim. The current one is not.
|
||
|
||
### D5. The AGPL escape hatch in ADR-0004 is legally broken
|
||
|
||
**Attacks:** ADR-0004 §"Trade-off accepted deliberately".
|
||
|
||
ADR-0004 states: *"this ADR's 'or-later' clause permits that transition [to AGPL-3.0-or-later]
|
||
cleanly, since GPL-3.0-or-later is a strict subset of AGPL-3.0-or-later for recipients."*
|
||
|
||
This is incorrect. The "or-later" proviso (GPLv3 §14) covers **later versions of the GNU GPL**.
|
||
AGPL is a *different license*, not a later GPL version. GPLv3 §13 permits *combining* GPL and
|
||
AGPL works into a larger work — it does not permit relicensing GPL-3.0 code as AGPL-3.0.
|
||
|
||
Today this is moot: a sole copyright holder can relicense at will. But **the first external
|
||
contribution accepted without a CLA or copyright assignment permanently closes the AGPL
|
||
option** — every contributor would need to consent. Asterisk's own lineage is the precedent:
|
||
Digium required copyright assignment precisely to retain relicensing power.
|
||
|
||
Three options; pick one deliberately, before the first external PR:
|
||
|
||
1. **Decide AGPL now**, while it costs nothing.
|
||
2. **Institute a CLA / DCO-plus-assignment policy** from day one, preserving the option.
|
||
3. **Delete the claimed escape hatch** from ADR-0004 and accept GPL-3.0 permanently, mitigated
|
||
by the wedge as the ADR already argues.
|
||
|
||
The current ADR records a hedge the project does not actually hold.
|
||
|
||
### D6. The Asterisk-position analogy has a scarcity gap (noted, not fatal)
|
||
|
||
**Attacks:** ADR-0002 §Context, vision-revision TL;DR.
|
||
|
||
Asterisk won because it made the *expensive thing* — PBX/PSTN interop — free. The expensive
|
||
thing in 2026 is the model, and rutster explicitly rents it. The scarcity rutster arbitrates is
|
||
**trust and data sovereignty**: real, but thinner, and maturing on a slower clock than the
|
||
demo-driven voice-AI wave.
|
||
|
||
This creates a tension the docs don't name: the project's *emotional* engine (demos, momentum
|
||
fuel) and its *strategic* engine (sovereignty, compliance) pull in different directions —
|
||
compliance buyers don't show up for demos, and demo-chasers use OpenAI-direct. Given the stated
|
||
goal is strategic relevance rather than revenue, this is acceptable — but sequencing should
|
||
consciously serve the strategic engine (see R1).
|
||
|
||
---
|
||
|
||
## Part II — Progress
|
||
|
||
### Credit first
|
||
|
||
Initial commit 2026-06-26; slices 1–3 merged by 2026-07-01. In one week:
|
||
|
||
- A working WebRTC media core (`str0m` sans-IO, spec-driven).
|
||
- A versioned tap protocol with forward-compat by construction (`#[serde(other)]` catch-all).
|
||
- An OpenAI Realtime adapter with a pure-function translator layer and a mock brain enabling a
|
||
credential-free dev loop.
|
||
- ~7,200 lines landed with green CI, cargo-deny, rustfmt/clippy clean, and integration tests
|
||
that exercise the actual seams (reconnect, teardown ordering, playout flush).
|
||
- ADRs that record reversals honestly (0003 → 0007) instead of retconning.
|
||
- A teaching-grade codebase (LEARNING.md concept index) serving the stated learning goal.
|
||
|
||
For a solo-plus-agents week this is an outlier pace, and the docs-as-agent-context methodology
|
||
is visibly working. Now the adversarial part.
|
||
|
||
### P1. Three slices in, the claimed differentiator is untouched
|
||
|
||
Everything shipped — browser echo, WS tap, OpenAI adapter — is the commodity layer that exists
|
||
as a quickstart in every voice-agent framework. The advisory interruption events are decoded and
|
||
deliberately dropped (`crates/rutster-tap/src/tap_client.rs:409` — *"advisory interruption event
|
||
observed; not acted on in slice-3"*).
|
||
|
||
The pattern to watch: ADR-0003 declared SIP the hard, differentiating core — then ADR-0007
|
||
deleted it. The docs now declare the reflex loop the hard, differentiating core — and three
|
||
slices have shipped *around* it. Step 4 is where the thesis first touches reality; it should be
|
||
treated as the project's actual first proof, not step 4 of 6.
|
||
|
||
### P2. Doctrine-vs-code drift on the hot path, one week in
|
||
|
||
ARCHITECTURE.md (Media plane): *"Dedicated timing threads for the 20ms loop, **never the shared
|
||
tokio pool**."*
|
||
|
||
The implementation: a single `tokio::spawn` task with `tokio::time::interval(10ms)` driving
|
||
**all sessions serially**, with per-session `.await`s and async-mutex acquisitions inside each
|
||
tick (`crates/rutster/src/session_map.rs:215-224`, `251+`). That is the shared pool — plus
|
||
head-of-line blocking: one session stalled on a lock delays every other session's media.
|
||
|
||
Fine at slice scale. But pillar 1 *is* deterministic timing, and the code contradicts the
|
||
doctrine with no tracking issue, no debt comment, and no measurement that would reveal when it
|
||
stops being fine. Either amend the doctrine ("dedicated threads when N concurrent calls demand
|
||
it, measured by X") or file the debt explicitly. Silent drift between ratified docs and code is
|
||
the exact failure mode the ADR apparatus exists to prevent.
|
||
|
||
### P3. The wedge is unmeasured — README's latency numbers are arithmetic, not data
|
||
|
||
The ~250 ms (mock) and ~700 ms (OpenAI) figures in the README are estimates summed from
|
||
component guesses ("slice-1's 200 ms + tap round-trip + OpenAI latency + 100 ms playout
|
||
buffer"). `TapMetrics` (`crates/rutster-tap/src/metrics.rs`) is drop/gap counters only — no
|
||
latency histograms, no jitter measurement, no barge-in kill-time.
|
||
|
||
For a project whose entire technical claim versus a weekend of Pipecat is "tighter real-time
|
||
behavior, deterministically, under load," the missing artifact is a **repeatable benchmark**:
|
||
p50/p99 mouth-to-ear latency and barge-in kill-time at 1 / 10 / 50 concurrent calls, run in CI,
|
||
regressed per commit. Until it exists, the no-GC pillar is a brochure claim.
|
||
|
||
It is also the only credible marketing asset the project could produce this year: *"rutster vs
|
||
LiveKit Agents vs Pipecat, p99 barge-in kill latency under load — reproduce it yourself."*
|
||
Recommendation: make it part of step 4's definition of done (R2).
|
||
|
||
### P4. Doc rot at one week old
|
||
|
||
- `README.md` Status (both the §Status block and the mid-file callout) still says *"Slice 1
|
||
(WebRTC media loopback) is the active build target"* directly beneath a quickstart
|
||
demonstrating slice 3.
|
||
- `fuzz/README.md` still plans SIP/SDP parser fuzz targets landing at step 5 — ADR-0007
|
||
abolished the SIP parser. The fuzz story should re-aim at what actually parses hostile-ish
|
||
bytes now: tap protocol frames, the provider media-fork framing (step 5), RTP.
|
||
|
||
Small in isolation — but this repo's methodology is docs-as-agent-context, and stale docs are
|
||
corrupted context for every future agent session.
|
||
|
||
### P5. Process weight is worth an honest measurement
|
||
|
||
The slice-3 implementation plan is 2,967 lines — roughly 40% the size of the entire codebase it
|
||
produced. The spec/ADR discipline is clearly paying for itself (this review is downstream of
|
||
it). The per-task SDD scaffolding (briefs, reports, review diffs per task) may or may not be.
|
||
The data to check exists in `.superpowers/sdd/`: correlate review-cleanliness per task with
|
||
brief length. If short briefs review just as clean, the ritual is costing slices.
|
||
|
||
### P6. The tap protocol has no auth story, and protocols ossify at first adoption
|
||
|
||
The core listens plaintext on `0.0.0.0:8080` (`crates/rutster/src/main.rs:38` — documented,
|
||
acceptable for slices). The tap dials `ws://` with no authentication in either direction.
|
||
|
||
Core-as-client is genuinely good design. But "security-as-product" plus the ambition of
|
||
tap-as-open-protocol means **v1 of the protocol should carry an auth field** (even a static
|
||
bearer token; mTLS later) *before* third-party brains exist. The moment someone else implements
|
||
the protocol, mandatory auth becomes a breaking ecosystem change instead of a line in the spec.
|
||
|
||
---
|
||
|
||
## Part III — Recommendations
|
||
|
||
### R1. Re-run the ADR-0007-style strategic review on the spearhead's back half
|
||
|
||
Likely outcome: barge-in stays (needed plumbing; the benchmark vehicle) but **rung-2 escalation
|
||
is pulled forward, ahead of step 5**. Human-takeover with queueing and recording is the actual
|
||
white space — the thing none of LiveKit / Pipecat / Vapi / OpenAI-direct ships — and it is the
|
||
capability that answers D2's "why not point the trunk at OpenAI directly?" If accepted, this is
|
||
an ADR (it amends the spearhead sequence ratified in the vision revision).
|
||
|
||
### R2. Make the latency/barge-in benchmark harness part of step 4's definition of done
|
||
|
||
It is simultaneously: the wedge's proof (P3), the doctrine-drift detector (P2), and the only
|
||
demo that differentiates rather than reproduces (D1, D2).
|
||
|
||
### R3. Amend the two false ADR claims now
|
||
|
||
| Doc | Current claim | Amendment |
|
||
|---|---|---|
|
||
| ADR-0002 / README pillar 3 / PORT_PLAN §10 | Spend control "structurally impossible for a 3-vendor stack" | Narrow to what is true post-0007: co-located, unskippable on mediated egress; brain never holds provider credentials; pacing/playout structurally enforced over terminated media (D4) |
|
||
| ADR-0004 | "or-later permits [AGPL] transition cleanly" | Choose: AGPL now / CLA before first external PR / delete the hatch and accept GPL-3 permanently (D5) |
|
||
|
||
### Smaller items (cheap, do opportunistically)
|
||
|
||
- Add a **cascade-stack example brain** (Whisper + open LLM + local TTS) to `examples/` — proves
|
||
brain-agnosticism and the sovereignty story (D3).
|
||
- Add "the brain vendor itself" as a row in the README competitor table (D2).
|
||
- Schedule the SBC-graduation reference config as a named deliverable (D3).
|
||
- Fix README status staleness; re-aim `fuzz/README.md` at the post-0007 parser surface (P4).
|
||
- Add an auth field to tap protocol v1 before third-party adoption (P6).
|
||
- File the dedicated-timing-thread debt as a tracked issue or amend the doctrine (P2).
|
||
|
||
---
|
||
|
||
## Appendix — What was reviewed
|
||
|
||
**Docs:** README.md; docs/ARCHITECTURE.md; docs/PORT_PLAN.md; ADRs 0001–0008;
|
||
vision-revision (2026-06-26); slice 1–3 design specs; LEARNING.md; fuzz/README.md;
|
||
`.superpowers/sdd/progress.md`.
|
||
|
||
**Code:** all six crates (`rutster`, `rutster-media`, `rutster-tap`, `rutster-tap-echo`,
|
||
`rutster-brain-realtime`, plus the `rutster-call-model` / `rutster-spend` / `rutster-trunk`
|
||
stubs); integration tests; CI workflow; git history (d3bd621..c30a452).
|
||
|
||
**Market claims verified (July 2026):**
|
||
|
||
- LiveKit telephony + Agents: SIP inbound/outbound, DTMF, warm/cold transfer, semantic turn
|
||
detection, sub-500 ms default stack —
|
||
[LiveKit voice agents](https://livekit.com/voice-agents),
|
||
[LiveKit telephony docs](https://docs.livekit.io/telephony/),
|
||
[2026 LiveKit Agents guide](https://www.forasoft.com/blog/article/building-multimodal-ai-agents-with-livekit-guide)
|
||
- OpenAI Realtime SIP, GA, with REFER transfer + hangup endpoints —
|
||
[OpenAI Realtime SIP guide](https://platform.openai.com/docs/guides/realtime-sip),
|
||
[Twilio × OpenAI Realtime SIP trunking](https://www.twilio.com/en-us/blog/developers/tutorials/product/openai-realtime-api-elastic-sip-trunking),
|
||
[Introducing gpt-realtime (GA)](https://openai.com/index/introducing-gpt-realtime/)
|
||
- Self-hosted brain landscape: cascade (Whisper + open LLM + Kokoro/Chatterbox-class TTS)
|
||
viable; open s2s immature —
|
||
[Self-hosted voice AI stack 2026](https://blog.dograh.com/complete-self-hosted-voice-ai-stack-in-2026/)
|