Stage 6: learn_call_flow rebuilt on the learner, docs truth sweep #6

Merged
r merged 1 commits from feature/stage6-learner-and-truth into feature/stage5-data-layer 2026-07-10 17:49:22 +00:00
Owner

Stage 6 of the architecture-review roadmap (stacked on #5) — the final stage. The auto-learned IVR flows that are core to the vision actually work now, and the docs stop describing a system that doesn't exist.

learn_call_flow rebuilt (F1, F9)

The old tool crashed on a wrong constructor and a nonexistent method; worse, run_exploration built its discovered_steps list and threw it away — nothing ever fed the learner. Now:

  • Exploration discoveries live on the call (ActiveCall.exploration_steps) and persist into the CallRecord's metadata JSON (durable per Stage 5).
  • The MCP tool loads a completed call's discoveries and runs the kept CallFlowLearner with its real contract: CallFlowLearner(llm_client=get_llm()) — heuristic step labels when no LLM is available, LLM-enhanced when there is one.
  • New number → build_flow + save_learned_flow; existing flow for the number → merge_discoveries refines it (timeouts averaged, usage counters bumped). The CallFlow↔row mapping stays in call_persistence.py.

Verified over the wire: a real streamable-HTTP MCP client lists 15 tools and learn_call_flow answers against the live DB.

Test gaps closed (F10)

  • tests/test_learner.py (5): discoveries → linked DTMF/HOLD/TRANSFER steps, exploration data survives with the persisted record, learn-then-refine end-to-end through the in-memory MCP client on real SQLite, no-exploration-data and unknown-call answers.
  • tests/test_websocket.py (3): socket refused with 4401 without the token, authorized connect gets the synthetic trunk-status event then the replayed history, per-call stream filters to its call.
  • Suite: 142 passed + the 2 pre-existing failures on main.

Docs truth sweep (F10)

  • README: 15 tools (learn_call_flow row added), MCP is streamable HTTP (not SSE), Python 3.12+, PostgreSQL + Alembic (the SQLite-fallback claim is gone), media pipeline explicitly marked stub-mode until pjsua2 bindings are installed, Alembic + honest-/health checked off in the roadmap.
  • docs/mcp-server.md rewritten against the actual surface: hangup (not the fictional end_call), real parameters for every tool, the 3 real resources (the doc listed 5, 4 of which never existed), correct /mcp/ streamable-HTTP connection config with bearer auth.
  • architecture/development/configuration drift fixed (SSE refs, SQLite refs, Python version).

pyproject cleanup

Pruned dependencies with zero imports anywhere: websockets, librosa, soundfile, python-multipart. alembic is now genuinely used (Stage 5).


This completes the six-stage plan: #1 MCP real + safe → #2 concurrency correctness → #3 structure → #4 honest health → #5 data layer → #6 learner + truth. Merge order is bottom-up (#1 first, each PR's base retargets to main as its parent merges — Gitea does this automatically on branch deletion).

🤖 Generated with Claude Code

Stage 6 of the architecture-review roadmap (stacked on #5) — the final stage. The auto-learned IVR flows that are core to the vision actually work now, and the docs stop describing a system that doesn't exist. ## learn_call_flow rebuilt (F1, F9) The old tool crashed on a wrong constructor and a nonexistent method; worse, `run_exploration` built its `discovered_steps` list and **threw it away** — nothing ever fed the learner. Now: - Exploration discoveries live on the call (`ActiveCall.exploration_steps`) and persist into the CallRecord's `metadata` JSON (durable per Stage 5). - The MCP tool loads a completed call's discoveries and runs the kept `CallFlowLearner` with its **real** contract: `CallFlowLearner(llm_client=get_llm())` — heuristic step labels when no LLM is available, LLM-enhanced when there is one. - New number → `build_flow` + `save_learned_flow`; existing flow for the number → `merge_discoveries` refines it (timeouts averaged, usage counters bumped). The CallFlow↔row mapping stays in `call_persistence.py`. Verified over the wire: a real streamable-HTTP MCP client lists **15 tools** and `learn_call_flow` answers against the live DB. ## Test gaps closed (F10) - `tests/test_learner.py` (5): discoveries → linked DTMF/HOLD/TRANSFER steps, exploration data survives with the persisted record, learn-then-refine end-to-end through the in-memory MCP client on real SQLite, no-exploration-data and unknown-call answers. - `tests/test_websocket.py` (3): socket refused with 4401 without the token, authorized connect gets the synthetic trunk-status event then the replayed history, per-call stream filters to its call. - Suite: **142 passed** + the 2 pre-existing failures on main. ## Docs truth sweep (F10) - **README**: 15 tools (learn_call_flow row added), MCP is streamable HTTP (not SSE), Python 3.12+, PostgreSQL + Alembic (the SQLite-fallback claim is gone), media pipeline explicitly marked stub-mode until `pjsua2` bindings are installed, Alembic + honest-/health checked off in the roadmap. - **docs/mcp-server.md** rewritten against the actual surface: `hangup` (not the fictional `end_call`), real parameters for every tool, the 3 real resources (the doc listed 5, 4 of which never existed), correct `/mcp/` streamable-HTTP connection config with bearer auth. - **architecture/development/configuration** drift fixed (SSE refs, SQLite refs, Python version). ## pyproject cleanup Pruned dependencies with zero imports anywhere: `websockets`, `librosa`, `soundfile`, `python-multipart`. `alembic` is now genuinely used (Stage 5). --- This completes the six-stage plan: **#1** MCP real + safe → **#2** concurrency correctness → **#3** structure → **#4** honest health → **#5** data layer → **#6** learner + truth. Merge order is bottom-up (#1 first, each PR's base retargets to main as its parent merges — Gitea does this automatically on branch deletion). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
r added 1 commit 2026-07-10 17:45:59 +00:00
The call-flow learner finally gets fed: exploration mode records its
IVR discoveries on the call (ActiveCall.exploration_steps) instead of
throwing them away, persistence stores them in the call record's
metadata, and the rebuilt learn_call_flow MCP tool turns a completed
exploration call into a stored flow via CallFlowLearner — correct
constructor (llm_client from get_llm, heuristic labels when the LLM
is unavailable), build for a new number, merge/refine when a flow
already exists. save_learned_flow/update_flow_from_model keep the
CallFlow↔row mapping in call_persistence.

Test gaps closed: tests/test_learner.py (discoveries→linked steps,
exploration persistence, learn-then-refine through the in-memory MCP
client, no-data and unknown-call answers) and tests/test_websocket.py
(4401 without token, trunk-status-then-replay on connect, per-call
stream filtering).

Docs aligned to code: README (15 tools incl. learn_call_flow, HTTP
not SSE, Python 3.12+, PostgreSQL+Alembic — no SQLite fallback, media
pipeline marked stub-mode until pjsua2 installed, Alembic and honest
/health checked off); docs/mcp-server.md rewritten against the actual
tool surface (hangup not end_call, real params, 3 real resources,
/mcp/ streamable HTTP + bearer auth); architecture/development/
configuration drift fixed.

pyproject: pruned never-imported deps (websockets, librosa,
soundfile, python-multipart).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
r merged commit 90620fbf80 into feature/stage5-data-layer 2026-07-10 17:49:22 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: r/hold-slayer#6