Skip to content

Commit 0a2dba6

Browse files
docs(wave-b-c-d-e-outline): post-Wave-A roadmap
Closes the four wave-outline todos in the GAP 03 + GAP 04 plan with a single consolidated roadmap doc. docs/wave_roadmap.md captures: - Wave B (~2-4 wk): GAP 07 (single root CMake + presets) then GAP 06 (engines/ top-level reorg). Independent of every prior gap; GAP 07 must precede GAP 06 because GAP 06 uses the new cmake/plugins.cmake rac_add_engine_plugin() helper. - Wave C (~3-4 wk): GAP 09 (streaming consistency via gRPC-style codegen on idl/voice_events.proto). Depends on GAP 01 (already done); benefits from GAP 08; deletes ≥1,500 LOC of hand-written streaming plumbing across 5 SDKs. - Wave D (~6-10 wk parallel): GAP 08 (delete ~5,100 LOC of duplicated Swift/Kotlin/Dart/RN/Web business logic). Parallelizable across SDKs and domains (voice/auth/download/HTTP/error-handling). - Wave E (optional, ~6-8 wk): GAP 05 (DAG runtime primitives — StreamEdge, GraphScheduler, etc.). Deferred unless a second pipeline is committed; today's voice_agent.cpp single-thread orchestrator works without it, and v2's own voice_pipeline.cpp doesn't use the primitives either. Includes per-wave: scope, expected deliverables w/ file paths, effort estimate from spec, blockers + dependencies, likely todo decomposition (so each wave's detailed plan can start from a known baseline). Mermaid dependency graph + a 'cross-wave constraints' section spelling out backwards-compat, ABI version cumulation, and the CI drift gate contract. Wave A (GAP 03 + GAP 04) is now fully complete. Made-with: Cursor
1 parent b5a14b3 commit 0a2dba6

1 file changed

Lines changed: 178 additions & 0 deletions

File tree

docs/wave_roadmap.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Post-Wave-A roadmap
2+
3+
_Last updated: at the close of Wave A (GAP 03 + GAP 04), branch `feat/v2-gap03-gap04`._
4+
5+
The seven-gap migration on `runanywhere-sdks-main` is structured as five waves. Two are done; four remain. This doc captures the **scope, dependency, and rationale** for each remaining wave at a level deep enough to start a detailed plan; full per-phase plans get written one wave at a time as the prior wave merges.
6+
7+
| Wave | Gaps | Status | Estimate (single eng) |
8+
|------|---------------------------------------|---------------|-----------------------|
9+
| Done | GAP 01 + GAP 02 | merged | (history) |
10+
| A | GAP 03 + GAP 04 | this branch | ~4–6 wk |
11+
| **B**| **GAP 07 + GAP 06** | next | ~2–4 wk |
12+
| **C**| **GAP 09** | after B | ~3–4 wk |
13+
| **D**| **GAP 08** | after C | ~6–10 wk (parallel) |
14+
| **E**| **GAP 05** (optional) | deferred | ~6–8 wk |
15+
16+
```mermaid
17+
flowchart LR
18+
A[Wave A done] --> B[Wave B: GAP 07 + 06]
19+
B --> C[Wave C: GAP 09]
20+
C --> D[Wave D: GAP 08]
21+
A --> C
22+
A --> E[Wave E: GAP 05 optional]
23+
```
24+
25+
---
26+
27+
## Wave B — Build-system + engines/ reorg (~2–4 weeks)
28+
29+
**Goal.** Collapse the 11 `build-*.sh` scripts + scattered `CMakeLists.txt` into a single root `CMakeLists.txt` + `CMakePresets.json` (GAP 07), then move every backend out of `sdk/runanywhere-commons/src/backends/` into a top-level `engines/` directory (GAP 06). Both wholly internal — no public API or behavior change.
30+
31+
### GAP 07 — Single root CMake (~1–2 weeks)
32+
33+
**Source spec:** [v2_gap_specs/GAP_07_SINGLE_ROOT_CMAKE.md](../v2_gap_specs/GAP_07_SINGLE_ROOT_CMAKE.md).
34+
35+
**Expected deliverables:**
36+
- `runanywhere-sdks-main/CMakeLists.txt` (~180 LOC) — top-level project + subdirectory orchestration; replaces the implicit `sdk/runanywhere-commons/CMakeLists.txt`-as-top.
37+
- `runanywhere-sdks-main/CMakePresets.json` (~145 LOC) — 9 preset families covering host (Debug / Release / RelWithDebInfo) × platform (macOS / Linux / Android / iOS / WebAssembly).
38+
- `runanywhere-sdks-main/cmake/platform.cmake` — platform-detection (`RAC_PLATFORM_*` vars) hoisted out of commons.
39+
- `runanywhere-sdks-main/cmake/plugins.cmake``rac_add_engine_plugin(name SOURCES ...)` helper that hides static-vs-shared decision behind one call. Also `rac_force_load(target PLUGINS ...)` wrapping `-Wl,-force_load` / `--whole-archive` / `/INCLUDE:`.
40+
- `runanywhere-sdks-main/cmake/sanitizers.cmake``RAC_SANITIZER=asan|ubsan|tsan` switch.
41+
- `runanywhere-sdks-main/cmake/protobuf.cmake` — wraps `find_package(Protobuf)` + the `rac_idl` build (currently lives inside `idl/CMakeLists.txt`).
42+
- `scripts/build-core-android.sh`, `scripts/build-core-xcframework.sh`, `scripts/build-core-wasm.sh` — three new scripts collapsed from the 11 existing per-SDK ones; each ~150 LOC, all wrapping `cmake --preset` + artifact-copy.
43+
- Slim down `.github/workflows/pr-build.yml` from 601 lines / 17 jobs to ~250 lines once steps become `cmake --preset … && cmake --build --preset … && ctest --preset …`.
44+
45+
**Effort estimate (from spec):** 1–2 weeks (5–10 engineer-days), 1 engineer.
46+
47+
**Blockers / dependencies:**
48+
- Independent of every prior gap. Touches only build configuration; no runtime code.
49+
- Must land BEFORE GAP 06 because GAP 06 uses the new `cmake/plugins.cmake` `rac_add_engine_plugin()` helper.
50+
51+
**Likely todo decomposition (~6 todos when fully planned):**
52+
1. Root `CMakeLists.txt` + project declaration + subdirectory routing.
53+
2. `CMakePresets.json` — host + cross-compile families.
54+
3. Four shared `cmake/*.cmake` helpers.
55+
4. Three `scripts/build-core-*.sh` wrappers.
56+
5. Slim `pr-build.yml` to use the presets.
57+
6. Final gate: collapse the per-SDK `gradle.properties` + `Package.swift` build hooks to call the new scripts; verify `ctest --preset all` passes on a hosted runner.
58+
59+
### GAP 06 — engines/ top-level reorg (~1–2 weeks)
60+
61+
**Source spec:** [v2_gap_specs/GAP_06_ENGINES_TOPLEVEL_REORG.md](../v2_gap_specs/GAP_06_ENGINES_TOPLEVEL_REORG.md).
62+
63+
**Expected deliverables:**
64+
- New top-level `engines/` directory.
65+
- `git mv sdk/runanywhere-commons/src/backends/{llamacpp,onnx,whispercpp,whisperkit_coreml,metalrt} engines/<name>/` for each.
66+
- Each `engines/<name>/CMakeLists.txt` becomes a one-liner:
67+
```cmake
68+
rac_add_engine_plugin(llamacpp
69+
SOURCES llamacpp_backend.cpp rac_llm_llamacpp.cpp ...
70+
RUNTIMES CPU METAL CUDA # populates the metadata array we wrote in GAP 04
71+
)
72+
```
73+
- Public engine headers stay where they are (`include/rac/backends/`) so frontend SDKs see no API change.
74+
- `tools/plugin-loader-smoke/main.cpp` — tiny CLI that dlopen-loads every `engines/<name>/librunanywhere_<name>.so` to prove the reorg didn't break anything.
75+
76+
**Effort estimate (from spec):** 1–2 weeks (5–9 engineer-days).
77+
78+
**Blockers / dependencies:**
79+
- Requires GAP 02 + GAP 07 (`cmake/plugins.cmake`).
80+
- Required by future third-party engine ecosystem work.
81+
82+
**Likely todo decomposition (~5 todos when fully planned):**
83+
1. `git mv` for each of the 5 backends; rewrite path references (`#include` paths in non-engine TUs that referenced `src/backends/*`).
84+
2. Replace each engine's CMakeLists.txt with the `rac_add_engine_plugin()` one-liner.
85+
3. Drop the `add_subdirectory(src/backends/...)` block from `sdk/runanywhere-commons/CMakeLists.txt`; replace with `add_subdirectory(${CMAKE_SOURCE_DIR}/engines/...)`.
86+
4. `tools/plugin-loader-smoke/` smoke binary.
87+
5. Final gate: every CI matrix job (Linux dlopen, iOS static-link, Android static-link) green; `librunanywhere_*.so` filenames unchanged from GAP 03.
88+
89+
---
90+
91+
## Wave C — Streaming consistency (~3–4 weeks)
92+
93+
**Goal.** Replace the 6 hand-written streaming implementations across Swift / Kotlin / Dart / RN / Web with codegen'd idiomatic streaming types. Built on top of `idl/voice_events.proto` already shipped in GAP 01.
94+
95+
**Source spec:** [v2_gap_specs/GAP_09_STREAMING_CONSISTENCY.md](../v2_gap_specs/GAP_09_STREAMING_CONSISTENCY.md).
96+
97+
**Expected deliverables:**
98+
- `idl/voice_agent_service.proto` — gRPC-style service definition (single `stream` rpc returning `VoiceEvent`).
99+
- Swift: `Sources/RunAnywhere/Generated/voice_agent_service.grpc.swift` (`AsyncStream<VoiceEvent>` from `grpc-swift`).
100+
- Kotlin: `commonMain/.../generated/voice_agent_service.grpc.kt` (`Flow<VoiceEvent>` from `grpc-kotlin`).
101+
- Dart: `lib/generated/voice_agent_service.pbgrpc.dart` (`Stream<VoiceEvent>` from `grpc-dart`).
102+
- TS (RN + Web): hand-written template emitting `AsyncIterable<VoiceEvent>` (no official gRPC streaming generator); ~200 LOC of template wired into `idl/codegen/generate_ts.sh`.
103+
- Per-SDK adapter (~100–200 LOC each) wiring the generated client stub to the in-process C callback (no actual gRPC transport — just shared types + iteration semantics).
104+
- Delete ≥1,500 LOC of hand-written `VoiceSessionEvent` / `LiveTranscriptionSession` / `tokenQueue` plumbing.
105+
- `idl/codegen/check-drift.sh` extended to gate the new generated files alongside the GAP 01 ones.
106+
107+
**Effort estimate (from spec):** 3–4 weeks single engineer; 4–5 weeks if the per-SDK adapters parallelize.
108+
109+
**Blockers / dependencies:**
110+
- **Requires GAP 01** (the IDL + codegen toolchain — already done).
111+
- Benefits from GAP 08 because the deletion sweep there removes the hand-written event types this gap replaces.
112+
- Independent of GAP 02–07.
113+
114+
**Likely todo decomposition (~7 todos when fully planned):**
115+
1. Add `idl/voice_agent_service.proto` + extend codegen scripts to emit gRPC stubs.
116+
2. Per-SDK adapter (Swift) — wire C callback → `AsyncStream`.
117+
3. Per-SDK adapter (Kotlin) — wire C callback → `Flow`.
118+
4. Per-SDK adapter (Dart) — wire C callback → `Stream`.
119+
5. Per-SDK adapter (TS RN + Web) — `AsyncIterable` template + emitter.
120+
6. Delete hand-written `VoiceSessionEvent` / `tokenQueue` types in each SDK; verify sample apps build unchanged.
121+
7. Final gate: 5 SDKs use generated streaming types; CI drift gate green; `wc -l` confirms ≥1,500 LOC deletion.
122+
123+
---
124+
125+
## Wave D — Frontend deletion sweep (~6–10 weeks parallel)
126+
127+
**Goal.** Delete ~5,100 LOC of duplicated business logic across Swift / Kotlin / Dart / RN / Web that re-implements C APIs already exposed by `runanywhere-commons`.
128+
129+
**Source spec:** [v2_gap_specs/GAP_08_FRONTEND_LOGIC_DUPLICATION.md](../v2_gap_specs/GAP_08_FRONTEND_LOGIC_DUPLICATION.md).
130+
131+
**Expected deliverables:**
132+
- Map every Swift / Kotlin / Dart / RN / Web hand-written orchestration file to its existing C API counterpart (`rac_voice_agent_*`, `rac_auth_*`, `rac_download_*`, `rac_http_execute`).
133+
- Phase-by-phase deletion (per the spec's 6 phases): voice (3–4 wk), auth (2–3 wk), download (2–3 wk), HTTP (1–2 wk), error handling (1 wk), then cleanup of dead `external fun` declarations.
134+
- Behavioral fixes folded in (e.g. Kotlin's 5-min token refresh window → C's 60-sec window).
135+
- Each per-SDK phase ends with sample-app smoke runs to prove parity.
136+
137+
**Effort estimate (from spec):** 6–10 weeks wall-clock with 2–3 engineers in parallel; 12–18 weeks single engineer.
138+
139+
**Blockers / dependencies:**
140+
- Requires the C ABI surface to be feature-complete + behaviorally correct (post-GAP-09 voice work especially).
141+
- Independent of GAP 02–07 (those make the engine layer cleaner; GAP 08 is about the frontend layer).
142+
143+
**Likely todo decomposition (~12 todos when fully planned, one per [SDK × domain] cell):**
144+
1. Voice: Swift, Kotlin, Dart, RN, Web (5 todos).
145+
2. Auth: Kotlin (the worst offender), Dart (2 todos).
146+
3. Download: Kotlin, Dart (2 todos).
147+
4. HTTP: Kotlin, Dart, RN (3 todos).
148+
5. Final gate: `wc -l` of frontend src dirs shows ≥5,100 LOC deletion; sample apps green; behavioral parity tests pass.
149+
150+
---
151+
152+
## Wave E — DAG runtime primitives (optional, ~6–8 weeks)
153+
154+
**Goal.** Land the `StreamEdge<T>`, `GraphScheduler`, `PipelineNode`, `CancelToken`, `RingBuffer<T>`, `MemoryPool` primitives the v2 branch ships in `core/Core/Graph/`.
155+
156+
**Source spec:** [v2_gap_specs/GAP_05_DAG_RUNTIME.md](../v2_gap_specs/GAP_05_DAG_RUNTIME.md).
157+
158+
**Why optional.** The spec itself flags this as deferrable: today's `voice_agent.cpp` is a single-threaded mutex-guarded orchestrator that works fine without DAG primitives. v2's own `voice_pipeline.cpp` does NOT use `GraphScheduler` either — it spawns six named worker threads manually. The primitives only earn their keep when a SECOND pipeline (e.g. multi-modal RAG, computer-use agent) needs to share scheduling logic with voice.
159+
160+
**When to do this:** Only after committing to ship a second pipeline that would otherwise duplicate voice's threading code.
161+
162+
**Likely todo decomposition (~8 todos when fully planned):**
163+
1. `StreamEdge<T>` (typed bounded queue + 3 overflow policies).
164+
2. `CancelToken` (hierarchical cancellation).
165+
3. `RingBuffer<T>` (lock-free single-producer/single-consumer).
166+
4. `MemoryPool` (per-node arena allocator).
167+
5. `PipelineNode` (one-thread-per-node base class).
168+
6. `GraphScheduler` (DAG topological sort + worker pool).
169+
7. Refactor `voice_agent.cpp` to use the primitives (proves they earn their keep).
170+
8. Final gate: no behavior change in voice agent; CPU + memory profile within 5% of baseline; second pipeline (whichever motivates the work) builds on the primitives.
171+
172+
---
173+
174+
## Cross-wave constraints
175+
176+
- Every wave preserves backwards compatibility: legacy `rac_service_create` + `rac_service_register_provider` continue to work through Waves B–D. Removal would be a separate "GAP 11" cleanup that runs after every SDK has cut over (post-Wave-D).
177+
- ABI version bumps are cumulative: GAP 02 set `RAC_PLUGIN_API_VERSION=1`, GAP 04 (in this wave A) bumped to `2`. Any future field added to `rac_engine_metadata_t` or `rac_engine_vtable_t` bumps to `3` and rejects all v2 plugins.
178+
- The CI drift-check (GAP 01) gates all generated code; every wave that touches `idl/*.proto` (Wave C) must regenerate cleanly.

0 commit comments

Comments
 (0)