Files
rutster/docs/reviews/2026-07-03-adversarial-review.md
Aaron D. Lee 4c36a4ded3 docs(reviews): 2026-07-03 adversarial review + market feature scan
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
2026-07-04 00:00:57 -04:00

20 KiB
Raw Permalink Blame History

Adversarial Review — Direction & Progress

  • Date: 2026-07-03
  • Scope: Project direction (ADRs 00010008, vision revision, README/ARCHITECTURE/PORT_PLAN) and progress to date (slices 13, 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 23, behind spearhead steps 46.

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 13 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 .awaits 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 00010008; vision-revision (2026-06-26); slice 13 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):