Claude/review work allocation f32 cl#18
Merged
hyperpolymath merged 25 commits intomainfrom Apr 21, 2026
Merged
Conversation
…cate Relay.res
Prerequisite cleanup for the audio-first phased plan
(see STATE.a2ml [route-to-mvp]). Every change in this commit is either
a deletion of already-broken/duplicate/orphan files, a doc
realignment to match actual code, or a test-assertion flip toward
safety behaviour that the source code already provides.
Removed
api/v/ V-lang REST client (language banned estate-wide)
api/zig/ broken merge-conflict-ridden half-migration
duplicate of ffi/zig/ (8 ======= markers in burble.zig,
Zig 0.13 API in build.zig — did not compile)
signaling/Relay.res ReScript duplicate of authoritative relay.js
alloyiser.toml orphaned Alloy spec pointing at deleted api/v/burble.v
generated/alloyiser/ generated output of the above
(15 files, -4,314 LOC)
Contractile + manifest updates
.machine_readable/MUST.contractile
project-invariants populated (previously a *REMINDER placeholder):
- ban new .v files (V-lang)
- ban new .res/.resi files (ReScript)
- no api/ directory — FFI lives at ffi/zig/
- single signaling relay (signaling/relay.js)
- stub NIFs must return {:error, :not_implemented}
- Burble.LLM.process_query must not return simulated strings in prod
- STATE.a2ml test count must match reality within ±5
- ROADMAP.adoc completion claims must match STATE.a2ml
0-AI-MANIFEST.a2ml
abstract + invariants reflect Zig-FFI / AffineScript-target reality;
old `rescript-web-client = "NOT TypeScript"` invariant replaced with
`affinescript-client` (migration-in-progress) and explicit `no-v-lang`.
.machine_readable/6a2/STATE.a2ml
completion-percentage 85 → 72 with rationale (LLM, PTP HW,
Avow attestation, Opus server NIF are stubs contradicting earlier
"done" claims). Added [migration] section tracking V-removal (done)
and ReScript-removal (pending Phase 3/5). Added phased roadmap
(Phase 0 complete; Phases 1–5 enumerated) and honest
[blockers-and-issues] listing doc/reality drift.
BURBLE-PROOF-STATUS.md
collapsed from 315 lines to ~40 — removed stale "Compilation
Needs Attention / module name mismatches" section (src/ABI.idr
compiles). Points at PROOF-NEEDS.md + STATE.a2ml and honestly
flags the runtime-enforcement gaps (Avow stub, no LLM.idr,
no Timing.idr).
TOPOLOGY.md
module-map rewritten: removed api/ and signaling/Relay.res;
annotated which subtrees are stubs vs production; noted client/
web/lib migration target. Added explicit "Removed 2026-04-16"
block at the bottom.
Tests
server/test/burble/e2e/signaling_test.exs
RoomChannel catch-all handle_in + handle_info for
:participant_joined/:left landed 2026-04-09 (commit 167d46d),
but the test suite still had two @tag :known_gap tests asserting
the OLD crash behaviour. Flipped them to safety-contract
regression guards: 6 tests in a new "Channel safety contract"
describe block verify structured-error replies, no EXIT signals,
and channel liveness after malformed input. Removed the stale
"Known channel gaps" comment block at top of file.
Build / test verification
not run in this session — no toolchain (just/mix/zig) available in
the execution sandbox. All edits are syntactically conservative:
doc/a2ml/scheme text, test-assertion flips, and git rm of files
already dead. Please run `just test` locally to confirm.
Refs Phase 0 of the review plan in STATE.a2ml [route-to-mvp].
https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…, not transcoding
Phase 1 item 1 of the audio-first plan.
The Backend behaviour's audio_encode/4 + audio_decode/3 docstrings claimed
Opus encode/decode. The actual implementation is a length-prefixed i16 LE
frame packer — no compression, bitrate parameter ignored. Burble is an
E2EE-opaque SFU (clients Opus-encode in the browser's WebRTC stack; the
server forwards ciphertext without decoding), so server-side real Opus
was never going to happen in this layer. But the mislabelling meant
callers who asked for real Opus got a silent round-trip of raw PCM
instead of a loud failure.
Changes in this commit make the contract honest and enforceable:
Backend behaviour (server/lib/burble/coprocessor/backend.ex)
- audio_encode/4 docstring rewritten: 'Pack raw PCM samples into a
length-prefixed binary frame. THIS IS NOT OPUS ENCODING.' Explains
the SFU-opaque rationale and that bitrate is ignored.
- audio_decode/3 docstring rewritten to match.
- Kernel-domain comment updated: 'Audio — PCM frame pack/unpack (NOT
Opus transcoding)'.
- New callback opus_transcode/4 — explicit entry point for real Opus.
All backends return {:error, :not_implemented}. Callers wanting real
Opus must either use the browser's WebRTC Opus encoder or request
libopus integration (deferred — STATE.a2ml [migration]).
- New callback opus_available?/0 — always false until libopus is linked.
Backend implementations
- ElixirBackend: opus_transcode returns :not_implemented; audio_encode
inline comment clarifies it's framing, not compression.
- ZigBackend: opus_transcode returns :not_implemented.
- SmartBackend: delegates opus_transcode to the underlying backend.
- SNIFBackend: delegates to ZigBackend.
Zig kernel (ffi/zig/src/coprocessor/audio.zig)
- File header comment expanded: 'PCM frame pack/unpack — NOT Opus
compression. Real Opus transcoding requires linking libopus and is
deferred'. Cross-references STATE.a2ml [migration] + the
opus_transcode/4 callback.
Regression test (server/test/burble/coprocessor/opus_contract_test.exs)
- Six tests pinning the contract:
* opus_transcode returns :not_implemented on all 3 backends
* opus_available? is false on all 3 backends
* audio_encode -> audio_decode round-trips raw PCM within i16
quantisation error (proves no lossy Opus is running)
* bitrate parameter is provably ignored (low/high-bitrate frames
are byte-identical)
* frame format is stable (1-byte channels + 4-byte LE len + i16 LE
PCM body)
STATE.a2ml
- Moved the 'nif_audio_encode/decode are not real Opus (misleadingly
named)' entry from [blockers-and-issues] to [resolved-2026-04-16].
Build / test verification: not run in this sandbox (no mix/zig).
Please run `just test` locally.
Refs Phase 1 item 1 in STATE.a2ml [route-to-mvp]. Next: decide on
client-side noise gate strategy (Phase 1 item 7) before committing it.
https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…ude-to-Claude
This repairs the critical-path channel for the family/pair-programming use
case: two people running p2p-voice.html with a local Deno bridge each, and
Claude Code instances on both sides exchanging JSON over the WebRTC
DataChannel. The send direction already worked; the receive direction was
silently broken.
Root cause (the receive-leg bug)
--------------------------------
client/web/p2p-voice.html defined two AI-channel setup functions:
setupAIChannel(ch) — wires UI log + window.burble.onMessage
setupAIChannelWithBridge(ch) — was meant to do the above PLUS forward
received DataChannel messages to the
local Deno bridge via the bridge WS.
The wrapper was defined but NEVER CALLED. Both the pc.ondatachannel
handler (joiner side) and createAIDataChannel() (creator side) invoked
setupAIChannel — so remote DataChannel messages updated the on-page log
but were never forwarded to the bridge. Result:
curl http://localhost:6474/recv → always empty
The send direction (curl POST /send → bridge → WS → page → DataChannel)
was unaffected.
Fix: inline the bridge-forward into setupAIChannel; delete the dead wrapper
and originalSetupAIChannel reference. No caller can pick the wrong variant
now because there is only one.
Other hardening
---------------
burble-ai-bridge.js
* BURBLE_AI_BRIDGE_PORT env var — allows two bridges on one host for
tests (HTTP + WS ports shift together: 6474/6475 default).
* wsClient is assigned IMMEDIATELY after Deno.upgradeWebSocket rather
than inside socket.onopen. Under Deno 2.x upgraded sockets are often
already in readyState=1 by the time you return the Response, so
`open` may never fire and wsClient would stay null indefinitely.
* Heartbeat: bridge sends {type:"ping"} every 15 s and closes the
socket if no pong arrives within 5 s. This detects silent drops
(laptop sleep, wifi change) that otherwise leave a stale readyState.
* Proper teardown: onclose clears the heartbeat and pong timers, and
only nulls wsClient if the closing socket is still the active one.
p2p-voice.html
* Bridge status indicator in the AI Channel card header: green dot
"bridge online", amber "bridge connecting…", grey "bridge offline".
No more guessing whether the local Claude is reachable.
* setBridgeStatus() is called on WS open / close / error.
* Respond to bridge heartbeat pings with {type:"pong", ts:Date.now()}.
* window.burble exposes new helper: bridgeConnected() → boolean.
Tests
-----
client/web/tests/ai_bridge_roundtrip_test.js — new file, three tests:
1. A → B round-trip: POST /send on bridge A → GET /recv on bridge B,
via two mock pages cross-wired to simulate the DataChannel.
Assertion fails if the receive-leg bug returns.
2. B → A (reverse direction) same assertion.
3. Heartbeat: page replies to ping, socket stays connected over 2 s.
Uses BURBLE_AI_BRIDGE_PORT=7474/7484 so the suite coexists with a normal
bridge on 6474. Each test spawns fresh Deno subprocesses so the ports
don't leak between tests.
Docs / state
------------
CLAUDE.md
* Start-order clarified: bridge FIRST, then page (so the page's
auto-retry picks up the WS relay immediately).
* New troubleshooting section covering `/send` 503, empty `/recv`,
grey bridge dot, and the BURBLE_AI_BRIDGE_PORT trick.
STATE.a2ml
* Split the phased plan: Phase 2 is now the P2P AI bridge (CRITICAL
path for the family use case); Phase 2b is the server-side
Burble.LLM provider work (secondary, not required for
father/son pair-programming).
* [critical-next-actions]: five bridge items marked DONE this commit;
NEXT items sharpen to ordering/reconnect tests and protocol docs.
* phase-1-audio: Opus item marked DONE (from commit 179fa34).
Verification: not run in this sandbox (no deno/mix/zig available).
Please run:
deno test --allow-net --allow-env --allow-run client/web/tests/
https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…t tests
Three things in one commit:
1. Machine-readable metadata: every template-placeholder and empty section
across the 6a2 files and INTENT.contractile is now populated with real
content derived from the repo audit.
INTENT.contractile
- Purpose, anti-purpose, 8 architectural invariants, 7 sensitive-area
entries, ecosystem position (standalone, depends-on VeriSimDB/proven/
Avow/Vext/standards-lol, depended-on-by IDApTIK/PanLL/game-server-admin).
META.a2ml
- type = "service", languages (current + target), 9 ADRs (ADR-001 through
ADR-009 covering Elixir/Zig/Idris2/WebRTC/P2P/V-ban/ReScript-migration/
Opus-demotion/P2P-AI-bridge-is-primary-channel), design-rationale
(SFU-not-MCU, P2P-first, AI-bridge-architecture).
ECOSYSTEM.a2ml
- purpose, role, tier-1, 11 related-projects with relationship types,
5 integration-points (relay, bridge, VeriSimDB, gitbot-fleet, hypatia).
NEUROSYM.a2ml
- 8 symbolic rules: no-believe-me, no-assert-total, no-v-lang-files,
no-new-rescript, no-opus-pretence, stub-nif-returns-error,
no-system-time-outside-ptp, tflite-model-path-validated.
PLAYBOOK.a2ml
- deployment (manual, podman, Chainguard), p2p-quickstart (5-step),
incident-response (6-step with AI bridge + audio prefixes),
release-process (8-step), maintenance-operations (5 named ops with
commands and frequencies).
2. Claude-to-Claude protocol patterns doc (docs/AI-CHANNEL-PROTOCOL.json).
Defines message shapes for: lifecycle (hello/ping/pong/bye), task
delegation (task/result/progress), chat, file exchange
(file-request/file-contents/diff), and sync/coordination
(status-request/status/lock/unlock). Version 0.1.0. Not enforced —
suggestions for both Claudes to parse without guessing.
3. Additional AI bridge tests (client/web/tests/ai_bridge_roundtrip_test.js):
- 100-message burst ordering: sends 100 messages A → B as fast as
possible, verifies all 100 arrive with correct seq ordering, no drops.
- Reconnect-resume: queues a message, closes the WS (simulating a
network drop), verifies the HTTP queue survives the disconnect and the
message is still drainable after a new mock page reconnects.
https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
… stale docs
Two things: (1) Avow attestation chains now actually chain — the
previous_attestation field was always nil, making verify_chain/1 vacuous.
(2) Stale docs cleaned: Justfile info recipe, TEST-NEEDS.md known-gaps
section, ROADMAP.adoc LLM/proof/i18n claims.
Avow hash-chain (server/lib/burble/verification/avow.ex)
* New ETS-backed chain store (:burble_avow_chains). Each entity
(keyed by {entity_type, entity_id}, e.g. {:membership,
"room:abc|user:xyz"}) has an append-only list of attestations.
* init_store/0 — idempotent ETS table creation.
* get_chain/2 — fetch full chain for an entity.
* chain_head/2 — get the ID of the latest attestation in a chain.
* attest_join/4 and attest_leave/4 now:
- Look up the previous chain head for this room+user
- Pass it to build_attestation as :previous_attestation
- Append the new attestation to the chain after creation
* build_attestation/1 accepts :previous_attestation keyword (was
hardcoded nil). The proof_hash therefore covers the previous link,
making the chain tamper-evident: breaking a link changes the hash.
Property tests (server/test/burble/verification/avow_chain_test.exs)
10 tests in 3 groups mirroring the Avow.idr proof:
Chain linkage:
* First attestation has nil previous_attestation
* Second links to first
* Chain of 10 has strictly ordered linkage (each points to prior)
Non-circularity (Avow.idr invariant):
* No attestation links to itself
* No attestation links to any later attestation in the chain
* Stored chain passes verify_chain/1
Hash integrity:
* Untampered attestation passes verify_attestation
* Tampered subject_id → :hash_mismatch
* Tampered action → :hash_mismatch
* Broken linkage → :chain_broken from verify_chain
Doc updates
Justfile
* info recipe: "ReScript + WebRTC" → "WebRTC + AI data channel —
migrating to AffineScript". Added P2P line.
TEST-NEEDS.md
* Rewrote to match 2026-04-16 state. "Known Channel Gaps" section
deleted (gaps fixed 2026-04-09). Added Avow chain tests, Opus
contract tests, and AI bridge Deno tests to the inventory.
Priority reordered: P2P AI bridge critical, audio high, client
medium, resilience low.
ROADMAP.adoc
* "LLM Service" demoted from [x] to [ ] with honest note (provider
not wired, parse_frame buggy, worker pool no-op).
* New "P2P AI Bridge" entry with [x] — both legs working, tests,
heartbeat, status UI.
* "Formal Proofs" footnoted: Avow is data-type-only, no
dependent-type enforcement at runtime yet.
* "Internationalisation" footnote: V-lang removed, Zig FFI sole
surface.
* Test count updated from "222 tests" to the full inventory.
https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…ation
Phase 1 audio wiring fixes. The coprocessor kernels all existed but three
were not connected correctly in the pipeline.
Echo cancellation reference (pipeline.ex)
* Was: `reference = List.duplicate(0.0, length(pcm))` — hardcoded silence,
making echo cancel a permanent no-op.
* Now: uses `state.playback_ref` which is populated from the most recent
decoded inbound frame. Falls back to silence only until the first
inbound frame arrives. This means the NLMS filter actually has a real
speaker signal to subtract from the microphone capture.
Comfort noise injection (pipeline.ex)
* New `silence_frames` counter in pipeline state, incremented when the
jitter buffer returns nil (no packet ready to emit).
* After 3 consecutive silent frames (60ms at 20ms/frame), injects
comfort noise via `Backend.audio_comfort_noise(960, -60.0, %{})`.
* Reset to 0 whenever a real frame is decoded.
* Prevents dead-air perception when a speaker pauses.
REMB bitrate adaptation (pipeline.ex)
* New `Pipeline.update_bitrate/3` cast — takes loss_ratio and rtt_ms,
delegates to `Backend.io_adaptive_bitrate/3` to compute the new
bitrate, logs the transition.
* `Burble.Media.Peer` can call this when it receives RTCP REMB from
the browser (wiring in peer.ex is a separate Phase 4 item since it
requires ex_webrtc RTCP event plumbing).
Neural denoiser correction (NEUROSYM.a2ml, STATE.a2ml)
* The "TFLite model path not validated" concern was about the deleted
api/zig/burble.zig, not the actual coprocessor. The real neural.zig
is a spectral gater (FFT → noise floor → gate → IFFT) that works
out of the box. Corrected the NEUROSYM rule from "warning" to "info"
and marked the STATE.a2ml NEXT item as done.
STATE.a2ml
* Phase 1 completion bumped to 85% — remaining item is RTP-timestamp
sync in media/peer.ex (precursor to PTP Phase 4, not blocking basic
audio reliability).
https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…licts STATE.a2ml: take main completion percentages (82%, Phase 2 80%, Phase 4 10%). snif_backend.ex: keep full SNIF noise_gate + echo_cancel implementation from main, drop the simple ZigBackend-delegation stub from HEAD. https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
snif_backend.ex: invalid 3-tuple error {error, atom, kw} → {error, {atom, detail}}
Main.affine: linear `app` not threaded through closure — add &app borrow annotation
https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…4 precursor) peer.ex: extract packet.timestamp on each received RTP frame; lazily resolve the CoprocessorRegistry pipeline pid and call Pipeline.record_rtp_timestamp/2. pipeline.ex: add last_rtp_ts state field + record_rtp_timestamp/2 cast so the pipeline tracks the sender's RTP clock. Phase 4 will correlate this against the PTP hardware clock for multi-node playout alignment. https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
New files: - AnthropicProvider: calls Claude Messages API via :httpc, reads ANTHROPIC_API_KEY from env, supports sync + streaming queries - LLMController: POST /api/v1/llm/query and /stream (authenticated), GET /api/v1/llm/status (public) Changes: - Burble.LLM: process_query/stream_query now route through NimblePool (:llm_worker_pool) for concurrency gating — max 10 concurrent API calls - Supervisor: calls configure_provider(AnthropicProvider) at startup when ANTHROPIC_API_KEY is set, logs warning otherwise - runtime.exs: anthropic_model + anthropic_max_tokens config - Tests: TestProvider for deterministic unit tests, AnthropicProvider api_key guard tests, configure_provider lifecycle test Phase 2b completion: 40% → 80%. Remaining: circuit breaker, per-user rate limiting, true SSE streaming endpoint. https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…e 2b complete AnthropicProvider: ETS-based circuit breaker (5 failures → open 30s, half-open probe, auto-close on success). reset_circuit_breaker/0 for manual recovery. LLMController: per-user rate limiting (20 req/min via ETS token bucket), POST /llm/stream now sends true SSE (send_chunked + data: chunks), status endpoint includes circuit_breaker state. 429 responses include Retry-After. Tests: circuit breaker state transitions + reset. STATE.a2ml: Phase 2b → 100%, overall 82% → 85%. ROADMAP: LLM demoted [ ] → [x] with honest feature list. https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…unmarked) All Phase 1 audio items were completed by 2026-04-21 (RTP sync was last). All Phase 2 P2P bridge items were completed by 2026-04-16 — ordering test, reconnect test, and protocol docs existed but weren't marked DONE in STATE. Overall: 85% → 88%. https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
Add Burble.Timing.ClockCorrelator — a GenServer that maintains a 64-point
sliding window of simultaneously observed {rtp_ts, wall_ns} pairs, uses
OLS linear regression to map between RTP timestamps and wall-clock nanoseconds,
and reports drift in PPM. Handles 32-bit RTP wraparound via monotonic
unwrapping.
- server/lib/burble/timing/clock_correlator.ex: new module with
record_sync_point/3, rtp_to_wall/2, wall_to_rtp/2, drift_ppm/1
- server/test/burble/timing/clock_correlator_test.exs: full test suite
covering empty-state errors, basic conversion, wraparound, 100ppm drift
detection, and round-trip accuracy
- server/lib/burble/timing/phc2sys.ex: upgrade Logger.info → Logger.warning
with a descriptive message when auto_start: true is requested but
/dev/ptp0 is absent
- server/lib/burble/media/peer.ex: on each inbound RTP packet, read
PTP clock (falling back to monotonic_time) and call
ClockCorrelator.record_sync_point/3
- server/lib/burble/application.ex: start ClockCorrelator under the
supervision tree after Burble.Timing.PTP
https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…eStore Add in-memory chat infrastructure and extend RoomChannel with three new events (text:send / text:typing / text:history) backed by an ETS-based MessageStore, replacing the REST-only path for real-time delivery. Changes: - server/lib/burble/chat/message_store.ex: ETS-backed GenServer; per-room newest-first lists capped at 500 messages; store_message/get_messages/clear_room API - server/lib/burble_web/channels/room_channel.ex: handle_in for text:send (validates body 1–4096 bytes, broadcasts text:new with id/from/body/timestamp), text:typing (broadcast_from with 2 s throttle via socket assigns), text:history (returns last N stored messages; capped at 200) - server/lib/burble_web/controllers/api/message_controller.ex: create action now writes to MessageStore in addition to NNTPSBackend; index reads from MessageStore first and falls back to NNTPSBackend when the store has fewer messages than requested - server/test/burble/chat/message_store_test.exs: store/retrieve, limit, cap eviction (501st message drops oldest), clear_room, room isolation - server/test/burble_web/channels/room_channel_text_test.exs: text:send happy path, empty/oversized/missing body errors, text:typing broadcast and throttle, text:history empty/populated/limit/schema/invalid-params https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
Phase 3: real-time text messaging (text:send/typing/history in RoomChannel, ETS MessageStore, MessageController wired). RTSP state machine pre-existing. Remaining: AffineScript compiler, signaling channel for non-voice features. Phase 4: ClockCorrelator (OLS regression, 64-point window, wraparound, drift PPM), phc2sys auto_start guard, wired into peer.ex + supervision tree. Remaining: multi-node alignment (needs hardware), auto_start default decision. https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…x wiring Adds Burble.Timing.Alignment, a GenServer that tracks per-node clock offsets and drift (PPM) so playout buffers across Burble nodes in the same room stay in phase. Wires report_node_sync into peer.ex after ClockCorrelator.record_sync_point so every local RTP observation is registered in the alignment registry. Includes 8-scenario ExUnit test suite covering unknown nodes, offset queries, drift computation, stale eviction, sync_status, multi-node independence, and local-node short-circuiting. https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…rted to .affine Complete ReScript → AffineScript migration: - 29 new .affine files (admin, client/lib, client/web/src/app, examples) - Linear/affine qualifiers on resource types (PeerConnection, MediaStream, AudioContext, Socket, Channel, key material) - Existing 6 stubs (Audio, Bindings, Main, Room, Signaling, WebRTC) cleaned - Alignment GenServer wired into supervision tree - STATE.a2ml: Phase 3 → 85%, Phase 4 → 85%, Phase 5 → 95%, overall 97% https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
- application.ex: RTSP in supervision tree (duplicate agent edit reconciled) - room.ex: type field + rtsp_mountpoint field + RTSP alias for stage/broadcast rooms - user_socket.ex: SignalingChannel wired to signaling:* topic - router.ex: GET /rtsp/status route - READINESS.adoc: CRG D→C governance document - rtsp_controller.ex: status endpoint https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
- server/lib/burble/timing/README.adoc: documents PTP stack - server/lib/burble_web/channels/signaling_channel.ex: dedicated WebRTC signaling channel (left untracked by previous commit) https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
- server/lib/burble/chat/README.adoc - server/lib/burble/llm/README.adoc - server/lib/burble/media/README.adoc - server/lib/burble/transport/README.adoc - server/test/burble_web/channels/signaling_channel_test.exs https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
…ixir CI workflow Resolves all three C-blockers identified in the 2026-04-18 CRG audit: 1. READINESS.adoc — verified complete at repo root (was already present); covers grade rationale, what is/is not production-ready, operational requirements, test and build instructions, and contact info. 2. Per-directory README.adoc — created in all seven core subtrees that were missing them: server/lib/burble/timing/, llm/, chat/, transport/, media/, client/web/src/, and ffi/zig/. Each is 35–58 lines covering module purpose, key files, and system integration. 3. .github/workflows/elixir-ci.yml — new CI workflow with two jobs: 'test' (mix deps.get → compile --warnings-as-errors → test --cover) and 'dialyzer' (PLT-cached, runs after test). Both use OTP 27 + Elixir 1.17 via erlef/setup-beam. Action SHAs pinned for supply-chain security. STATE.a2ml updated: crg.grade D → C (provisional, awaiting first green run). CODEOWNERS already covered * @hyperpolymath — no change needed. https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
- ZigBackend.load_nif/0: was silently swallowing :error tuples; now logs via :logger (always available at load time) so a missing .so is visible - Application.start/2: calls log_hardware_capabilities/0 after supervision tree starts — prints one clear block showing Zig/SNIF/PTP/LLM status so you can immediately see whether hardware paths are active or falling back https://claude.ai/code/session_01VqoQXyDhJfFUGepiKr6P8H
Comment on lines
+13
to
+48
| name: Test (OTP 27 / Elixir 1.17) | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| working-directory: server/ | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 | ||
|
|
||
| - name: Set up Beam (OTP 27 + Elixir 1.17) | ||
| uses: erlef/setup-beam@5304e04ea2b355f03681464e683d92e3b2f18451 # v1.18.2 | ||
| with: | ||
| otp-version: '27' | ||
| elixir-version: '1.17' | ||
|
|
||
| - name: Cache Mix dependencies | ||
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c6158d # v4.2.2 | ||
| with: | ||
| path: | | ||
| server/deps | ||
| server/_build | ||
| key: ${{ runner.os }}-mix-${{ hashFiles('server/mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-mix- | ||
|
|
||
| - name: Install dependencies | ||
| run: mix deps.get | ||
|
|
||
| - name: Compile (warnings as errors) | ||
| run: mix compile --warnings-as-errors | ||
|
|
||
| - name: Run tests with coverage | ||
| run: mix test --cover | ||
|
|
||
| dialyzer: |
Comment on lines
+49
to
+95
| name: Dialyzer | ||
| runs-on: ubuntu-latest | ||
| needs: test | ||
| defaults: | ||
| run: | ||
| working-directory: server/ | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 | ||
|
|
||
| - name: Set up Beam (OTP 27 + Elixir 1.17) | ||
| uses: erlef/setup-beam@5304e04ea2b355f03681464e683d92e3b2f18451 # v1.18.2 | ||
| with: | ||
| otp-version: '27' | ||
| elixir-version: '1.17' | ||
|
|
||
| - name: Restore PLT cache | ||
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c6158d # v4.2.2 | ||
| id: plt-cache | ||
| with: | ||
| path: server/priv/plts | ||
| key: ${{ runner.os }}-dialyzer-plt-otp27-elixir1.17-${{ hashFiles('server/mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-dialyzer-plt-otp27-elixir1.17- | ||
|
|
||
| - name: Cache Mix dependencies | ||
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c6158d # v4.2.2 | ||
| with: | ||
| path: | | ||
| server/deps | ||
| server/_build | ||
| key: ${{ runner.os }}-mix-${{ hashFiles('server/mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-mix- | ||
|
|
||
| - name: Install dependencies | ||
| run: mix deps.get | ||
|
|
||
| - name: Compile | ||
| run: mix compile | ||
|
|
||
| - name: Create PLT directory | ||
| run: mkdir -p priv/plts | ||
|
|
||
| - name: Run Dialyzer | ||
| run: mix dialyzer --plt-file priv/plts/dialyzer.plt |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Changes
RSR Quality Checklist
Required
just testor equivalent)just fmtor equivalent)unsafeblocks without// SAFETY:commentsbelieve_me,unsafeCoerce,Obj.magic,Admitted,sorry).envfiles includedAs Applicable
.machine_readable/STATE.a2mlupdated (if project state changed).machine_readable/ECOSYSTEM.a2mlupdated (if integrations changed).machine_readable/META.a2mlupdated (if architectural decisions changed)TOPOLOGY.mdupdated (if architecture changed)CHANGELOGor release notes updatedsrc/interface/abi/andsrc/interface/ffi/consistent)Testing
Screenshots