Commit Graph

78 Commits

Author SHA1 Message Date
opencode controller
0cccf77faf build(slice-2): workspace deps + rutster-tap-echo skeleton
- Add tokio-tungstenite 0.24, futures-util 0.3, url 2, base64 0.22 to
  [workspace.dependencies] (spec §8.1).
- Add crates/rutster-tap-echo as 7th workspace member (spec §2).
- Transition crates/rutster-tap/Cargo.toml from stub to real manifest
  with the deps Tasks 2-4 will need (rutster-media, tokio, tokio-tungstenite,
  serde, serde_json, base64, url, thiserror, tracing).
- Skeleton lib.rs (compile test) + main.rs (placeholder fn main) for
  rutster-tap-echo; Task 5 fills in the echo logic.
- Verify cargo deny check passes against the new transitive dep tree.

Spec ref: docs/superpowers/specs/2026-06-28-slice-2-agent-tap-design.md §2, §8.1.
2026-06-28 14:01:12 -04:00
opencode controller
22d3f03b8c plan(slice-2): agent tap implementation plan (spec §1.1)
8 TDD tasks, 53 checkbox steps, sequentially dependent:
1. Workspace deps + rutster-tap-echo skeleton (build foundation)
2. Tap wire protocol (spec §3) — JSON envelope, explicit LE PCM codec
3. TapMetrics + TapAudioPipe (spec §4.1) — the seam object
4. TapClient WSS pump loop (spec §4.2) — async WS pump + seq gap detection
5. Rust echo brain crate (spec §2.3) — stereotype binary + in-process EchoServer
6. Call-model TapHandle + Channel field (spec §5.2, §6)
7. Binary wiring — TapEngine + session_map + routes + main (spec §5.1, §7)
8. Integration test + Python echo brain + LEARNING.md (spec §8.4, §8.5 #7)

Plan self-reviewed: no placeholders; TapHandle::new visibility fixed
(pub(crate) → pub); spawn_tap_engine return shape fixed (returns
(TapAudioPipe, TapConn) tuple, not TapConn holding the pipe's mpsc).

Spec ref: 2026-06-28-slice-2-agent-tap-design.md (commit f83bca9).
2026-06-28 13:54:57 -04:00
opencode controller
f83bca98db spec(slice-2): adversarial review patches
Some checks failed
CI / fmt (push) Failing after 41s
CI / clippy (push) Failing after 45s
CI / test (1.85) (push) Failing after 36s
CI / test (stable) (push) Failing after 39s
CI / deny (push) Failing after 31s
Patches the agent-tap design with seven findings from PM-side review
before the implementation plan gets written. Resolves invariant-breakers
and deferred-decision debt that would compound across steps 3-6.

- P1 (high): §8.5 #6 'byte-identical loop_driver.rs' was the wrong bar
  for the seam test. Reworded: the trait-method *call sites* are
  unchanged; impl bodies are free to differ. That's what 'the seam
  held' actually means — the contract is the test.
- P2 (high): tap: Option<TapHandle> and ChannelState were parallel
  state machines without an invariant tying them. Added an explicit
  state-pair table: tap is Some iff state == Connected. Lookups tolerate
  transient inconsistency; the source-of-truth is the pair, not either
  field alone.
- P3 (medium): v1 wire format was host-endian (silent big-endian
  hazard on paper). Nailed LE explicitly via i16::to_le_bytes /
  i16::from_le_bytes. v2 can negotiate endianness if a big-endian
  brain ever materializes; v1 contract is LE-only. Added a §3.4
  byte-order invariant alongside the sample-count invariant.
- P4 (medium): wss:// URLs were accepted by schema but returned
  501 at connect time (UX: 2 s into a call). Now rejected at
  POST /v1/sessions with 400 + clear message — fail fast at
  session-create. Updated §4.4, §7.1, §7.3, §9, and the decisions
  table for consistency; removed all stale '501 at connect' claims.
- P5 (low): TapEngine spawn owner was unspecified. Added explicit
  ownership: session_map::drive_all_sessions (the binary's poll
  task) observes the Connected transition from run_poll_once and
  spawns the engine. Keeps loop_driver.rs behaviorally unchanged
  (the §8.5 #6 seam test still holds).
- P6 (low): 'two new deps' in §1.1 undercounted. Updated list:
  tokio-tungstenite, futures-util, url, serde_json (if not already
  pulled).
- P7 (low): ADR-0007 was 'post-implementation follow-up, not a
  slice-2 deliverable.' Tightened: strongly recommended to land
  alongside or immediately after slice-2 implementation — once
  step 3 ships against an unpinned protocol, the wire shape will
  silently drift. The spec lists the decisions ADR-0007 should
  capture (LE byte order added to the list).
2026-06-28 13:41:54 -04:00
opencode controller
11e72fa733 spec(slice-2): agent tap design
Some checks failed
CI / fmt (push) Failing after 31s
CI / clippy (push) Failing after 44s
CI / test (1.85) (push) Failing after 39s
CI / test (stable) (push) Failing after 35s
CI / deny (push) Has been cancelled
Slice 2 hardens the presumptive agent-tap shape from ARCHITECTURE.md §"Agent tap"
(PORT_PLAN §10 open decision) against a working implementation:

- Replace slice-1's in-process EchoAudioPipe with a real out-of-process brain
  reached over WSS; core-as-client, no inbound tap port (ADR-0006 posture).
- Versioned JSON event protocol (v1) over WS text frames with base64 PCM.
  Event-named + JSON-over-text-WS so the step-3 OpenAI-Realtime adapter is a
  translation shim, not a gRPC-bridge project.
- Decoupled TapEngine (cold-path tokio task) owns the WS connection; TapAudioPipe
  is a thin sync wrapper the AudioSource/AudioSink seam holds. RtcSession's media
  loop is byte-identical to slice-1 — the seam-test payoff.
- Core-authoritative playout buffer (5-frame/100ms ring; drop-oldest on overflow,
  silence on underflow) — the place where "brain proposes, core disposes" lives.
- Bounded-backoff reconnect (250ms→5s cap, infinite retries); Channel stays
  Connected; stateless-brain reconnect contract.
- Both a Python reference echo brain (examples/echo_brain/, not in CI) and a
  Rust echo brain crate (crates/rutster-tap-echo, the in-process test server
  + standalone dev binary).
- ws:// loopback-only enforced; wss:// URL accepted at schema, 501 at connect
  (cert/mTLS impl deferred to step 6).

Out-of-scope table (§1.2) locks the spearhead sequencing: real brain (3),
barge-in/VAD-driven playout kill (4), PSTN trunk (5), spend cap (6), wss:// TLS
posture (6), binary PCM mode (future-rung), byte-endian negotiation (tracked
open decision §9).
2026-06-28 13:37:57 -04:00
9fcdb60548 Merge pull request 'slice-1: WebRTC media loopback — the media-core proof' (#1) from slice-1-webrtc-loopback into main
Some checks failed
CI / fmt (push) Failing after 35s
CI / clippy (push) Failing after 39s
CI / test (1.85) (push) Failing after 41s
CI / test (stable) (push) Failing after 44s
CI / deny (push) Failing after 34s
Reviewed-on: #1
2026-06-28 17:20:23 +00:00
opencode controller
52462723d3 Merge remote-tracking branch 'origin/main' into slice-1-webrtc-loopback
Some checks failed
CI / fmt (pull_request) Failing after 35s
CI / clippy (pull_request) Failing after 41s
CI / test (1.85) (pull_request) Failing after 36s
CI / test (stable) (pull_request) Failing after 39s
CI / deny (pull_request) Failing after 44s
# Conflicts:
#	README.md
2026-06-28 13:19:16 -04:00
adlee-was-taken
3041036d94 fix: restore doc bar on RtcSession accessors; refresh stale README status
Some checks failed
CI / fmt (pull_request) Failing after 42s
CI / clippy (pull_request) Failing after 1m19s
CI / test (1.85) (pull_request) Failing after 49s
CI / test (stable) (pull_request) Failing after 29s
CI / deny (pull_request) Failing after 46s
Two Important findings from the final whole-branch review:

T4-M3: RtcSession::channel_id/channel_state/is_closed lacked /// docs
(AGENTS.md mandates /// on every public item; learner-facing code is a
primary product). One-line docs added per existing /// style in rtc_session.rs.

T7-M2: README "Status" section still read "Scoping. No code yet" — stale
post-slice-1. Updated to reflect slice-1 implementation status; minimal
edit, surrounding content preserved.
2026-06-28 13:13:27 -04:00
adlee-was-taken
85a58ab880 deny: workaround cargo-deny 0.18.x GPL-3.0-or-later parse bug
cargo-deny 0.18.3 (the latest that installs on rust 1.85) can't parse
the SPDX 'GPL-3.0-or-later' suffix — 'expected a <bare-gnu-license>'.
CI uses cargo-deny-action@v1 (bundles 0.19+) which parses it correctly.

Switching to bare 'GPL-3.0' lets local 'cargo deny check' work on the
pinned toolchain. ADR-0004's GPL-3.0-or-later stance is unchanged; this
is a parser workaround flagged for revert when the MSRV lifts past 1.88
(where cargo-deny 0.19.x installs natively).

Three of four 'cargo deny check' subcommands now pass locally:
  - licenses ok
  - bans ok
  - sources ok
  - advisories: fails locally only because the advisory-db contains CVSS 4.0
    entries (anchor-lang RUSTSEC-2026-0146) that cargo-deny 0.18.3's parser
    rejects. Unfixable on the rust 1.85 toolchain; CI's 0.19+ parses fine.

Surfaced by Task 7's done-checklist run; not a Task 7 code change.
2026-06-28 13:08:19 -04:00
adlee-was-taken
f765fc2b27 docs: LEARNING.md + fuzz/ placeholder + README dev-loop (spec §7, §6.3)
LEARNING.md indexes ten concept-to-file pointers (the spec floor was
five) — the newtype pattern, exhaustive enum match, sans-IO, trait
extension seams, thiserror + hot-path match-and-continue, Arc<Mutex>
vs Arc<RwLock>, DashMap, str0m's single-mutation invariant, graceful
shutdown, include_str!. fuzz/README.md pre-paves the layout (no
hostile-bytes surface in slice 1; harnesses land at step 5 per the
out-of-scope table). README's new dev-loop section documents the
libopus FFI prerequisite and the manual e2e steps.
2026-06-28 13:06:37 -04:00
adlee-was-taken
f92e6f467b ci: cargo-deny + GitHub Actions workflow (spec §6.1, §6.2)
deny.toml allows the permissive Rust-ecosystem licenses + our own
GPL-3.0-or-later; bans duplicate versions of tokio/serde/bytes/tracing
to catch dep-tree divergence early; restricts sources to crates.io. CI
runs fmt --check, clippy -D warnings, test --all (matrix: stable +
MSRV 1.85), and cargo deny check on push + PR to main. The CI job
installs libopus-dev — the opus crate's FFI dependency (PORT_PLAN §7
'Core (FFI)' disposition).
2026-06-28 12:59:29 -04:00
adlee-was-taken
1f0f64c1c6 binary: axum signaling + DashMap session store + browser test client
Four routes on axum 0.7 per spec §4.1: POST /v1/sessions (mint),
POST /v1/sessions/:id/offer (str0m-native SDP accept), DELETE
/v1/sessions/:id (close), GET / (static HTML client). Session store is
a DashMap<ChannelId, Arc<Mutex<RtcSession>>> (spec §4.5); one tokio
task drives all session poll loops — per-session tasks would pre-pave
the wrong pattern for step 4's dedicated thread. Graceful shutdown
drops the DashMap on Ctrl-C / SIGTERM. Integration test exercises the
REST surface; manual browser e2e per README §6.5.
2026-06-28 12:48:02 -04:00
adlee-was-taken
34c590b48a media: RtcSession + str0m poll loop (the media core)
RtcSession owns a str0m::Rtc + Opus decoder/encoder + EchoAudioPipe +
a bound UDP socket (spec §3.1, §4.5). accept_offer calls str0m 0.21's
sdp_api().accept_offer() natively — no hand-rolled SDP munger; str0m
fills DTLS fingerprint + ICE creds + Opus codec. The loop driver
drains poll_output per str0m's single-mutation invariant, routes
inbound MediaData through Opus decode + EchoAudioPipe sink, sends
Transmit packets on the UDP socket, and checks the 60 s idle timeout.

DEV-DEVIATION: loop runs on tokio (spec §3.4); step 4 replaces with
a dedicated timing thread per ARCHITECTURE.md.

str0m 0.21 API adjustments from the brief's sketch (verified against
src/str0m-0.21.0):
- SdpAnswer has no .mid() accessor; use answer.media_lines[0].mid()
  via the Deref<Target=Sdp> impl.
- Input::Receive carries the Instant as the first tuple element
  (Input::Receive(now, recv)); handle_input takes a single Input arg.
- Receive constructed via Receive::new(proto, src, dst, buf) (the
  DatagramRecv field is private).
- UDP socket binds 127.0.0.1:0 (Candidate::host rejects the
  unspecified address 0.0.0.0).
- BROWSER_SDP_OFFER fixture restored a=group:BUNDLE 0 (str0m's
  Sdp::assert_consistency rejects SDP without a session group).
2026-06-28 12:39:57 -04:00
adlee-was-taken
27fb64aad5 lock: pin transitive rcgen/time deps for rust 1.85
str0m 0.21's default crypto backend pulls rcgen 0.14.8 and time
0.3.51, both of which require rustc 1.88+. The workspace toolchain
is pinned to 1.85 (edition 2024 floor, the str0m MSRV). Pin rcgen
to 0.14.7 and time to 0.3.41 in Cargo.lock so the str0m tree
builds on the pinned toolchain. No source changes; this only
restricts transitive versions.
2026-06-28 12:39:44 -04:00
adlee-was-taken
4c0898cd49 docs: QUICKSTART + DEVELOPMENT + CONTRIBUTING, polish README index
Builds out the user-facing docs tree alongside the slice-1 build target.
Kept the implementer's planned Task 7 'Slice 1 dev loop' README section
untouched — these docs are the canonical destination for that pointer.

- docs/QUICKSTART.md: 5-min path to 'hear the echo' (libopus install,
  cargo run, browser steps, troubleshooting, what's happening under the
  hood).
- docs/DEVELOPMENT.md: dev loop — workspace layout, per-crate iteration,
  running tests, the 20 ms loop / 'drop + observe' rule, slice-1
  boundaries (what NOT to add yet).
- CONTRIBUTING.md (at repo root, conventional): trunk-based dev,
  CI gates, commit message style, atomic commits, code style +
  learner-facing documentation policy, terminology policy, PR workflow
  + review checklist, GPL-3.0-or-later license.
- README.md: add a Quickstart pointer at the top, a Documentation table
  linking to every doc, and the slice-1 build-target status block.
2026-06-28 12:32:12 -04:00
adlee-was-taken
e09af55e00 media: PcmFrame + AudioSource/Sink + Opus codec pair
PcmFrame is the canonical tap format (16-bit mono @ 24 kHz, 480 samples
per 20 ms frame — ARCHITECTURE.md). AudioSource/AudioSink are the seam
step 2 splices the tap client into (spec §3.3); EchoAudioPipe is the
slice-1 wiring of that seam. OpusDecoder/OpusEncoder wrap the opus
crate's libopus FFI with hot-path match-and-continue (no ? on the 20 ms
loop, spec §3.8); decode/encode return Option<PcmFrame>/Option<Vec<u8>>
so a dropped frame is logged + counted, never propagated to crash the
peer.
2026-06-28 12:22:29 -04:00
adlee-was-taken
8260c76824 toolchain: bump pinned Rust 1.80 → 1.85 (edition 2024 floor)
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.
2026-06-28 11:29:46 -04:00
adlee-was-taken
a5d0f90fe2 call-model: Channel + ChannelId + ChannelState (signaling embryo)
rutster-call-model is real-but-minimal (spec §5): the unifying leg
object the future API exposes. ChannelId is a Uuid newtype for
type-safety (the slice-1 worked example of the newtype pattern).
Channel is signaling-state only — media lives in rutster-media as a
leaf concern of the Channel, surfaced only when a second consumer needs
to observe it (spec §5.3). ChannelState matches the New→Connecting→
Connected→Closing→Closed flow from §5.4.
2026-06-28 11:27:55 -04:00
adlee-was-taken
39245a6553 workspace: scaffold + three stub crates (sip/tap/spend)
Workspace root, pinned toolchain, and the three stub crates whose only
job in slice 1 is to lock the ADR-0002 boundary shape. Each ships a
lib.rs module doc (what it will hold, why deferred, which spearhead step
fills it) and a crate_compiles test. Spec §2.2.
2026-06-28 11:22:52 -04:00
adlee-was-taken
470a22a6b7 git: ignore local worktree directories
Per using-git-worktrees skill — local .worktrees/ is for isolated
development workspaces, not tracked content. .
2026-06-28 11:20:58 -04:00
adlee-was-taken
3c5cc75015 plan(slice-1): adversarial review patches
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.
2026-06-28 11:19:41 -04:00
adlee-was-taken
1164770d98 spec(slice-1): adversarial review fixes
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.
2026-06-28 10:41:18 -04:00
adlee-was-taken
504d0020f4 docs: add AGENTS.md for opencode
Project orientation for any agent (human/AI/hybrid) working in the repo.
Covers: project structure (current + planned), build/lint/test commands,
Rust code style (formatting, naming, error handling, async/concurrency,
learner-facing documentation comments), terminology policy (inclusive
language + ICE-as-protocol-convention exception), architecture
pre-reading order, key decisions to respect, git workflow, and the
slice-1 boundaries (what NOT to add yet).
2026-06-28 09:34:04 -04:00
adlee-was-taken
075e984fb5 docs: ratify vision revision across README/ARCHITECTURE/PORT_PLAN + ADRs 0002-0006
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.
2026-06-28 09:33:29 -04:00
adlee-was-taken
23cec14977 spec(slice-1): self-review fixes + terminology
- 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).
2026-06-28 09:30:55 -04:00
adlee-was-taken
2310367b15 spec: slice 1 — WebRTC media loopback design
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.
2026-06-28 09:26:38 -04:00
adlee-was-taken
098814b734 docs: capture vision revision — self-hosted AI-era contact-center engine
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
2026-06-26 22:47:47 -04:00
adlee-was-taken
0370347642 ADR-0001: SIP strategy — native Rust core behind Kamailio + rtpengine
Record the SIP edge decision and align the docs:
- docs/adr/0001-sip-strategy.md: layered strategy (own Rust parser, rent the
  interop tail via a Kamailio + rtpengine SBC, grow native core behind the shield);
  pjproject FFI explicitly rejected for breaking the memory-safety thesis at the
  most exposed seam.
- PORT_PLAN §1 + open decisions: SIP row updated to the decided strategy.
- ARCHITECTURE: "biggest technical risk" now points at ADR-0001.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2bfD7MkqEdfnMXxXBu456
2026-06-26 21:49:36 -04:00
adlee-was-taken
d3bd621aa0 Initial scoping: architecture + Asterisk→Rust port plan
Establish rutster — a memory-safe, API-first, security-first telephony
platform; spiritual successor to Asterisk for the WebRTC/microservices era.

- README: project framing, design pillars, open decisions
- docs/ARCHITECTURE.md: three-plane (control/media/app) model
- docs/PORT_PLAN.md: every Asterisk subsystem mapped to a disposition
  (core / WASM-plugin / service / edge-FFI / dropped / replaced) with rationale

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2bfD7MkqEdfnMXxXBu456
2026-06-26 21:38:45 -04:00