Skip to content

Fix contact patch schemas and live voice turn-taking - #76

Open
dimavrem22 wants to merge 22 commits into
mainfrom
fix/full-stack-ci-reliability
Open

dimavrem22 wants to merge 22 commits into
mainfrom
fix/full-stack-ci-reliability

Conversation

@dimavrem22

@dimavrem22 dimavrem22 commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Executive Summary

Fix contact-update schema drift, exact post-call SMS handling, and recurring live-test polling and voice timing failures without relaxing the assertions.

  • Correct partial contact updates and exact SMS body selection.
  • Repair email polling and voice-driver scheduling while preserving delivery proof and bounded deadlines.
  • Release patch version 0.2.13, with consistent package, lockfile, and runtime metadata.

Description

The plugin exposes the intended optional contact-update fields through its actual MCP schema and gives recorded exact SMS bodies precedence over incidental speech-transcription variants. The live harness uses bounded, fully paginated email polling, observed greeting completion, and utterance-aware retry pacing. The hosted caller now uses short conversational turns with actual peer-response gating, rather than replaying a long request whose tail can be interrupted.

The specific fixes and evidence are listed below. Acoustic reliability is verified in live CI, not inferred from fixture-string tests.

Reason

Schema drift could turn profile-only contact edits into identifier updates, and post-call reconciliation could send a transcription variant instead of the recorded exact message. Independently, historical inbox scans and incomplete voice turn-taking caused recurring end-to-end failures.

Decisions

  • Exact SMS source: Prefer the explicit body in the recorded open action because incidental transcription variants are not caller corrections; clear later corrections or cancellation still override it, and accepted sends are never replayed merely to fix wording.
  • Test integrity: Preserve persisted-contact checks, exact correlation, both-leg transcript ownership, current-run markers, durable actions, actual API-accepted SMS evidence, duplicate rejection, and cleanup.
  • Polling and timing: Bound the work rather than increase scenario/job timeouts; retain complete pagination, equal-timestamp rows, retry limits, and bounded teardown.
  • Hosted greeting: Require observed initial speech only in the hosted fixture, where the agent greets the caller; leave generic silent-peer behavior unchanged.

Testing

  • Current revision: 9538374; release version 0.2.13.
  • Offline suite: 632 passed, with 23 credential-gated live tests skipped locally. Critical Python syntax/name checks pass across changed files; full Ruff passes for the new script and changed helper tests; diff checks pass.
  • Actual MCP/host contracts: 7 passed, including notes-only updates, explicit empty identifier arrays, minimal call requests preserving configured voicemail defaults, and explicit overrides through MCP transport.
  • Package, root lock entry, runtime, and advertised MCP server versions agree; uv lock --check --offline passes.
  • Final acceptance: 0/2 fresh complete passes. Earlier full-stack successes used caller scripts that prescribed internal action handling, and the old SMS/A2A assertions could accept weaker evidence. Those runs are superseded and do not establish acceptance. Full-stack runs are held while a shared pre-connect call failure is investigated. The prior natural-user run exposed the identity-context omission fixed below; fresh complete runs will resume after the transport blocker is resolved.
  • Reviewer targets: notes-only contact updates retain identifiers; explicit empty arrays remain explicit; a recorded exact post-call SMS is delivered once after hangup; delayed email replies remain discoverable across every matching page; hosted requests wait for an actual greeting within the existing bound.

Specific fixes

Runtime / product bugs

  1. Optional contact fields became required in MCP. The shorthand schema required all profile and identifier fields, undermining partial updates. Use explicit JSON Schema requiring only contact_id; omitted email/phone lists stay omitted and explicit empty arrays remain valid. Proof: actual MCP transport regressions for notes-only and empty-list updates.
  2. Post-call SMS could copy the wrong transcription variant. A failed live case recorded the exact requested body but sent a different body matching noisy caller speech. Apply the same exact-body precedence to initial reconciliation and bounded correction/recovery, retaining later-correction/cancellation precedence and no replay. Proof: conflicting transcript/action regressions and earlier live actual-SMS passes; final repeated proof remains pending above.

CI / test-harness fixes

  1. Email polling repeatedly scanned lifetime inbox history. Freeze an inclusive five-minute lookback before baseline/send at all three polling sites, then exhaust every matching page. Proof: actual pinned-SDK regression with 101 baseline messages, three result pages, ten minutes of clock advancement, and a delayed reply at the original boundary; baseline IDs and content correlation remain enforced.
  2. The caller could interrupt a greeting—or start before a late greeting arrived. Track nonempty partial/final speech, wait for the quiet interval, and require initial peer speech in hosted mode. Retain the existing 30-second greeting bound and awaited teardown. Proof: actual asynchronous handler regressions cover delayed partial/final greetings, blank/null frames, missing-peer timeout, and unchanged generic behavior; six new cases failed before the initial-peer fix and pass afterward.
  3. Fixed retries could repeat a long request before it finished. Derive the earliest repeat from utterance length plus the quiet gap, without increasing the overall timeout or retry cap. Proof: fake-clock/WebSocket regression fails under the old cadence and passes with the corrected scheduling.
  4. An interrupted caller restarted the whole request instead of completing the conversation. Live received-transcript evidence repeatedly showed missing action details or marker words despite locally submitted text. Add a validated, generic one-to-three-stage caller script: wait for a new real final peer response and quiet before advancing; require actual marker readback for the request stage. Both caller turns express only the user's desired outcome; neither directs action registration or any tool sequence. Retry only the current stage, preserve the original shared 180-second budget and global two-reask cap, and never treat local text submission as received speech. Proof: seven actual asynchronous scheduler regressions failed before the change; tests now cover partial/blank/stale replies, fragmented readback, interruption, phase-owned retries, shared budget/cap, and stop cancellation. The live proof helpers still require both actual call recordings, the durable open action, and a real API-accepted SMS; no transcripts or API-side action evidence are injected. Earlier tool-coached live passes are superseded; repeated complete natural-user verification remains pending above.
  5. The real-SDK regression needed its dependency in every unit job. Install the same pinned SDK in the Python matrix so the polling regression executes in CI. Proof: all three Python unit jobs passed during the fix sequence; final-head checks run again automatically.

Diagnostic improvement

  1. Hosted failures lacked missing-word evidence. Add content-free marker-word counts plus strictly allowlisted stage/retry/interruption metadata to distinguish missing speech from later action/delivery failures. Diagnostic output is bounded and covered by content-exclusion tests. This changes diagnosis only, not pass/fail conditions.

Release metadata

  1. Patch release and stale runtime version. Bump package and lockfile 0.2.12 → 0.2.13; align inkbox_claude.__version__, which still reported 0.2.10, and the MCP server version, which reported 0.2.9, to 0.2.13. Proof: all four values agree and offline lock validation passes.

Test-integrity corrections

  1. Caller scripts prescribed the implementation. Remove action/title/details/tool instructions from hosted speech and the initial call request. Remove tool names and schema identifiers from real-model contact CRUD and A2A requests. Voice mode and voicemail behavior remain normal runtime configuration; no expected answer or scenario solution is added to the product's instructions. The model must discover the correct tools from the installed plugin.

  2. SMS assertions accepted extra prose and ignored unrelated duplicates. Inspect every fresh outbound SMS across the complete bounded inbox window. Require one row with the requested three words (case/punctuation normalization only), the sole intended recipient, a server timestamp at or after the persisted call end, zero sends before hangup, and unchanged duplicate-grace and host-completion proof. Proof: negative cases reject extra prose, merged/missing words, wrong/additional recipients, missing/in-call timestamps, unrelated duplicates, and pre-hangup sends.

  3. A2A could match the caller's original token instead of the answer. Require the actual agent's final message for terminal results and agent-authored messages for progress. Proof: caller-only history fails; caller tokens and earlier agent messages cannot satisfy a wrong final answer. Existing protocol-state, clarification, delegation, cadence, and worker-result gates remain.

  4. A greeting could satisfy requested readback. Require the full current marker in both the agent-owned spoken transcript and caller-owned received transcript. Proof: either missing readback leg fails even when caller intent and the saved action are otherwise present.

  5. Identity answers could omit requested fields. Require the complete phone number, display name, and email; reject masked/truncated phone answers, fail missing named sender fixtures instead of skipping, require the requested SMS acknowledgement rather than arbitrary nonempty text, and require the requested number of tool names (all installed names for the full email listing, three for SMS). Caller requests contain no tool-selection or voicemail-schema hints. Proof: full-number regressions reject masking, truncation, extra digits, and unrelated numeric fragments; the complete offline suite passes.

Additional runtime drift exposed by natural requests

  1. Optional call settings were required by the advertised MCP schema. Without caller-supplied schema hints, live calls overrode the configured voicemail setting. Replace shorthand call schema with explicit required to_number/purpose only; optional settings preserve configured defaults, while explicit overrides remain supported. Proof: the real MCP transport regression failed before the fix because origination was required; it now validates minimal calls, explicit voicemail overrides, and required-purpose rejection. No caller configuration hints were restored.

  2. Identity tool omitted the requested display name. The SDK identity contains it, but the tool returned only handle/email/phone, leaving the model without the requested fact. Expose display_name in the normal identity result. Proof: the regression failed with a missing field before the fix and now passes; strict live identity assertions remain.

  3. Speech evidence accepted merged or substituted words. Remove the historical homophone alias and concatenated-character matching from transcript/action evidence; require contiguous whole words with only case/punctuation normalization. Apply the same boundary rule to driver readback recognition so a partial or merged answer cannot stop retries or advance the conversation. Proof: negative merged/prefix/suffix/reordered/substituted-word cases and actual asynchronous staged-driver checks pass; both transcript legs, durable actions, and exact SMS gates remain required.

  4. Preloaded host identity dropped the configured display name. The corrected identity tool alone did not repair answers based on preloaded session context: live replies contained the full phone/email/handle but omitted the name. Carry the configured display name from gateway startup through session construction into the normal host identity context. Proof: actual startup capture and host-options regressions both failed before the change and pass afterward; no test answer or scenario instruction is injected.

@dimavrem22
dimavrem22 marked this pull request as ready for review September 19, 2026 02:07
@blacksmith-sh

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant