uuid 1.x → getrandom 0.4.x requires edition = "2024", stabilized in Rust 1.85.
The Task 1 pin of 1.80 was written when uuid 1.x resolved to a getrandom that
still compiled on edition 2021; the resolution has shifted. The brief said
'confirm the latest stable at impl time' — bumping the pin is faithful to that.
Plan doc's Task 6 CI matrix also updated: [stable, "1.85"].
Cross-task correction in response to Task 2 implementer's DONE_WITH_CONCERNS
escalation; not a Task 2 code change.
Verified str0m 0.21 + opus 0.3.1 API surfaces via docs.rs subagents;
patched the plan with the real signatures and several design-level fixes.
API-surface patches (str0m 0.21, verified):
- Global Constraints: full verified API surface documented inline
(Rtc::new takes Instant; SdpOffer::from_sdp_string is the entry
point, NOT from_str_unchecked; add_local_candidate returns
Option<&Candidate>; Writer::write takes rtp_time not media_time;
MediaTime has no add(Duration) -> use mt + MediaTime::from(d);
payload_params returns impl Iterator; MediaData.data is Arc<[u8]>).
- Task 4 accept_offer: rewritten with from_sdp_string + correct error
mapping; RtcSessionError::SdpOffer changed to String (collapses parse
+ accept failures uniformly).
- Task 4 loop_driver: MediaTime::add -> + MediaTime::from(Duration);
media.data deref coercion documented.
Design-level patches:
- Task 4: RtcSession::new_for_test -> pub fn new() (single idiomatic
constructor, no test/prod split).
- Task 4: added accept_offer_transitions_channel_to_connecting test
(the transition was claimed but untested in the original plan).
- Task 5: dropped reqwest from workspace deps (unused — integration
test uses tower::ServiceExt::oneshot); added tower as workspace dep
+ binary crate dev-dependency.
- Task 5: removed duplicate DELETE /v1/sessions route (was incorrectly
chained via .delete() on the collection route AND on /v1/sessions/:id
— only the latter is correct).
- Task 5: clarified pub mod requirement (must be pub because the
integration test references modules via absolute paths).
- Global Constraints: added task/PR strategy (one commit per task,
merged in numeric order, granular history is load-bearing for the
learning-codebase goal).
Self-review section updated to reflect the patches.
Plan is now implementation-ready.
API-surface corrections (verified against str0m 0.21 docs):
- str0m::Live -> str0m::Rtc (no Live type exists)
- needs_input_timeout() -> Rtc::poll_output() returning Output::Timeout(Instant)
- RtcConfig::with_dtls_cert -> RtcConfig::set_dtls_cert (also: str0m auto-gens
a cert if none passed; slice 1 still passes explicitly for ownership clarity)
- Event::RtpPacket(pkt, rid) -> Event::RtpPacket(RtpPacket) { single struct,
RID is a field }
- str0m::ice::IceAgent -> no public 'ice' module; ICE types at crate root:
Candidate, CandidateKind, IceCreds, IceConnectionState
- str0m.write_rtp() on Rtclive -> Frame API path: Rtc::writer(mid) -> Writer::write
New design decision surfaced by API verification:
- Media API path: Frame API (default) vs RTP API (set_rtp_mode(true)).
Slice 1 uses Frame API — the proof target is the codec-to-PCM boundary,
not RTP packetization. RTP API is a candidate for step 5.
Design-level fixes from close reading:
- §3.3 AudioSource/AudioSink: clarify who drives the traits (the poll loop
drives both; sink on inbound MediaData, source on Output::Timeout deadline)
- §3.6: clarify cert/CrossRef — fresh in-memory cert each restart is safe
because browser fetches fresh SDP each session, no caching layer to go stale
- §4.1: add Content-Type: application/sdp to /offer (browser-side fetch needs
it)
- §4.5: resolve 5-min-vs-60s idle-timeout contradiction (60s, not 5 min)
- §4.5: add graceful-shutdown story (drop DashMap on SIGINT, browsers see
dead peer — acceptable for dev loop)
- §6.4: fix 'mocked str0m Rtc' — str0m's Rtc is a concrete struct, not a
trait; sans-IO means drive real Rtc with synthetic Inputs, not mock it.
Stronger than mocking: production path exercised exactly.
Resolves 5 load-bearing API errors + 3 design-level ambiguities. Spec is now
implementation-ready pending user review.
Folds the 2026-06-26 vision-revision pressure-test into the canonical
docs. The spec moves from Proposed to Ratified; the decisions it produced
land in the docs it said it would amend, with ADRs recording each
load-bearing reversal.
- README.md: reframe north star (AI-era contact-center engine, not
Asterisk successor); persona; revised pillars (add data-ownership,
demote WASM, promote spend-control); update 'what it is/isn't'.
- ARCHITECTURE.md: replace three-plane framing with fused per-call
vertical + composable horizontal platform; remove control<->media
hot-path gRPC hop; make the agent tap the central interface; add
DX spine + GUI-as-API-client + k8s declarative/operational model.
Also: 'too slow to police' -> 'too slow to enforce' (terminology).
- PORT_PLAN.md: recharacterize as capability checklist (not template);
graduate contact-center capabilities to first-class domain; Rust-native
trunk SIP rows; WASM demoted; thin-slice + capability ladder phasing.
- ADR-0001: marked Superseded by ADR-0003.
- ADR-0002 (new): north star + fused per-call core.
- ADR-0003 (new): Rust-native trunk SIP, no SBC shield.
- ADR-0004 (new): GPL-3.0-or-later license.
- ADR-0005 (new): Valkey as event bus + state store.
- ADR-0006 (new): WebRTC-first ingress; SIP endpoint deferred.
- vision-revision spec: status -> Ratified 2026-06-26.
- Clarify RtcSession/Channel ownership: ChannelId IS the session id;
RtcSession owns both str0m Live + codecs and the Channel. Fixes ambiguity.
- Drop the separate /ice endpoint: non-trickle ICE bundles candidates
into the SDP offer/answer, so the endpoint was redundant. Fixes
contradiction.
- Clarify idle timeout = 'no RTP for 60s' (not 5 min blanket), and that
no per-session tokio task is spawned (pre-paves the wrong pattern
for step 4's dedicated thread).
- 'reflex to police' -> 'reflex to enforce' (terminology: avoid
authoritarian verbs).
Workspace scaffold + spearhead step 1: browser talks WebRTC to the core,
core terminates DTLS-SRTP, decodes Opus to canonical 16-bit PCM @24 kHz mono,
echoes back. Proves the media core + the codec-to-PCM boundary (pre-paves
the tap). Stubs lock the ADR-0002 fused-vertical boundaries for steps
2/5/6.
Drafted in brainstorming session 2026-06-28.
Records the decisions from pressure-testing the four founding docs:
- North-star reframe: the engine for the AI-era contact center, not an
Asterisk-protocol successor (Asterisk's map = checklist, not template)
- Fused per-call vertical + composable horizontal platform (tightens the
three-plane model; removes the control<->media hot-path gRPC hop)
- Rust-native trunk SIP, IP-allowlisted; drops the Kamailio/rtpengine C
shield (reverses ADR-0001, to be formalized as ADR-0003)
- Pillars re-weighted: +data-ownership, spend-control promoted to
constitutive, WASM demoted out of the core story
- Developer-first DX spine (config-as-code, AEL lesson, engine+batteries)
- GUI as pure API client (FreePBX model) + k8s declarative/operational split
- Sequenced thin-slice first proof + capability ladder
For review before applying to README/ARCHITECTURE/PORT_PLAN and writing
ADR-0002 (identity) and ADR-0003 (SIP).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUtgQTo3wHGXP1TixHr4gd