Conversation
…laim Production transcript on 2026-04-29: a buyer wrote "ya envié el dinero" and the classifier re-emitted the previous round's generic opener byte-identical instead of pivoting to a proof-of-payment request. The existing rule already said "ask the buyer for proof / transfer details" but the model still treated the claim as if no party reply had landed. Strengthen the buyer-only-payment-claim branch with: (1) explicit multilingual cues for "I sent the fiat" claims, (2) the concrete next piece of evidence the next clarification MUST request (method, timestamp, reference, redacted screenshot), (3) the observed defect itself as a concrete counter-example, and (4) sample correct wording. The earlier no-repetition rule and the new reactivity guidance together should make the model pivot rather than echo the round-0 opener.
- policy: bump EARLY_MIDSESSION_BYPASS_FOLLOWUPS from 3 to 6.
2026-04-29 Bob run escalated LowConfidence at the 4th mid-session
round (confidences 0.20 → 0.24 → 0.24 → 0.28) when the right
move was to keep asking the buyer for the receipt. Doubling the
budget gives the model room to extract evidence on "buyer claims
paid, no proof yet" loops before the policy declares the session
stuck.
- follow_up: skip the round when the latest non-stale transcript
row is outbound; advance the marker and wait for the party reply
instead of stacking a second clarification on top of an unanswered
one.
- prompts + openai adapter: forbid scolding follow-up openers
("I already asked", "Ya indiqué..."); prefer respectful framings
like "Para entender mejor tu perspectiva...".
- mediation/mod: runtime strip of known scolding preambles as a
belt-and-braces guard against models that ignore the prompt.
- mediation/mod: move the followup-text test module to the file
footer to satisfy clippy::items-after-test-module.
WalkthroughThe changes introduce gating logic to prevent duplicate outbound clarifications in guided mediation by checking the latest non-stale transcript message direction. They strengthen prompt constraints to forbid repetitive or scolding language in follow-up messages, add a database helper to track message directions, and adjust the mid-session bypass window for clarification escalation. Changes
Sequence Diagram(s)sequenceDiagram
participant Evaluator
participant Database
participant Classifier as Reasoning<br/>(Classifier)
participant Dispatcher
Evaluator->>Database: Query latest_nonstale_message_direction()
Database-->>Evaluator: direction: "outbound" | "inbound" | null
alt Latest is Outbound
Evaluator->>Database: Update round_count_last_evaluated<br/>to total_fresh_inbounds (transaction)
Evaluator-->>Dispatcher: Return<br/>(skip classification)
else Latest is Inbound or No Message
Evaluator->>Classifier: Invoke reasoning.classify<br/>(clarification)
Classifier-->>Evaluator: SuggestedAction +<br/>follow-up text
Evaluator->>Dispatcher: Draft and send<br/>outbound message
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 93e6268eca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let tx = guard.transaction()?; | ||
| db::mediation::advance_evaluator_marker(&tx, session_id, total_fresh_inbounds)?; | ||
| tx.commit()?; |
There was a problem hiding this comment.
Do not mark inbound replies evaluated on outbound-only turn check
This branch advances round_count_last_evaluated whenever the latest non-stale row is outbound, which can silently discard fresh inbound replies that arrive with older inner_event_created_at than Serbero's last outbound (e.g., party clock skew). In that case count_fresh_inbounds increases, this code marks those replies as evaluated without running classification, and the user never gets a follow-up. The regression is introduced by combining timestamp-ordered latest_nonstale_message_direction with unconditional marker advancement here.
Useful? React with 👍 / 👎.
The follow-up loop's outbound-wait guard ordered the latest-direction query by `inner_event_created_at DESC` and unconditionally advanced `round_count_last_evaluated` to `total_fresh_inbounds` whenever the top row was outbound. Under party clock skew (or NTP correction, or delayed delivery), a brand-new inbound reply can land with an `inner_event_created_at` older than Serbero's most recent outbound; the timestamp ordering then surfaces the outbound as "latest" and the unconditional marker advance silently marks the unanswered inbound as evaluated, so the user never gets a follow-up. Order by `id DESC` instead — SQLite's insertion order is the right "what arrived last" signal and is immune to inner-event timestamp anomalies. Add a regression test that pins the clock-skew shape (outbound at ts=110 inserted first, inbound at ts=50 inserted second) and asserts the inbound is reported as the latest.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/mediation/mod.rs`:
- Around line 556-578: The sanitizer function
remove_repetitive_followup_preamble currently rewrites some repetitive preambles
but misses the new "I already asked" phrasing family; update the arrays of
prefixes in remove_repetitive_followup_preamble to include English variants like
"I already asked that I need to understand your perspective.", "I already asked
that I need to understand your perspective. Specifically:", "I already asked
that I need to understand your perspective:", and the equivalent Spanish forms
("Ya pregunté que necesito entender tu perspectiva...", "Ya pregunté que
necesito entender tu perspectiva. En concreto:", etc.), ensuring you trim and
preserve the remainder exactly as other branches do, and apply the same
additions to the duplicate implementation around lines 2276-2290; add unit tests
covering each new prefix (both languages and punctuation variants) to assert
they normalize to the expected "To better understand your perspective, ..." /
"Para entender mejor tu perspectiva, ..." outputs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ac968f86-fb12-496a-ac8c-5f43b05f49af
📒 Files selected for processing (8)
prompts/phase3-classification.mdprompts/phase3-message-templates.mdspecs/003-guided-mediation/spec.mdsrc/db/mediation.rssrc/mediation/follow_up.rssrc/mediation/mod.rssrc/mediation/policy.rssrc/reasoning/openai.rs
| fn remove_repetitive_followup_preamble(text: &str) -> String { | ||
| let cleaned = text.trim(); | ||
| for prefix in [ | ||
| "Ya indiqué que necesito entender tu perspectiva. En concreto:", | ||
| "Ya indique que necesito entender tu perspectiva. En concreto:", | ||
| ] { | ||
| if let Some(rest) = cleaned.strip_prefix(prefix) { | ||
| return format!("Para entender mejor tu perspectiva, {}", rest.trim_start()); | ||
| } | ||
| } | ||
| for prefix in [ | ||
| "I already indicated that I need to understand your perspective. Specifically:", | ||
| "I already said that I need to understand your perspective. Specifically:", | ||
| ] { | ||
| if let Some(rest) = cleaned.strip_prefix(prefix) { | ||
| return format!( | ||
| "To better understand your perspective, {}", | ||
| rest.trim_start() | ||
| ); | ||
| } | ||
| } | ||
| cleaned.to_string() | ||
| } |
There was a problem hiding this comment.
Sanitizer misses the newly-forbidden “I already asked” phrasing family.
The helper rewrites some repetitive preambles, but it still won’t catch the explicit "I already asked" style now forbidden by prompt contract. Add those variants and pin them with tests.
💡 Proposed patch
fn remove_repetitive_followup_preamble(text: &str) -> String {
let cleaned = text.trim();
+ for prefix in [
+ "Ya pedí que compartieras tu perspectiva. En concreto:",
+ "Ya pedi que compartieras tu perspectiva. En concreto:",
+ ] {
+ if let Some(rest) = cleaned.strip_prefix(prefix) {
+ return format!("Para entender mejor tu perspectiva, {}", rest.trim_start());
+ }
+ }
for prefix in [
"Ya indiqué que necesito entender tu perspectiva. En concreto:",
"Ya indique que necesito entender tu perspectiva. En concreto:",
] {
if let Some(rest) = cleaned.strip_prefix(prefix) {
@@
}
for prefix in [
+ "I already asked you to share your perspective. Specifically:",
"I already indicated that I need to understand your perspective. Specifically:",
"I already said that I need to understand your perspective. Specifically:",
] {
if let Some(rest) = cleaned.strip_prefix(prefix) {
return format!(
@@
fn leaves_natural_followup_unchanged() {
let text =
"Para entender mejor tu perspectiva, ¿puedes compartir una prueba de transferencia?";
assert_eq!(remove_repetitive_followup_preamble(text), text);
}
+
+ #[test]
+ fn removes_repetitive_english_already_asked_preamble_from_followup() {
+ let text =
+ "I already asked you to share your perspective. Specifically: can you share transfer proof?";
+ assert_eq!(
+ remove_repetitive_followup_preamble(text),
+ "To better understand your perspective, can you share transfer proof?"
+ );
+ }
}Also applies to: 2276-2290
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/mediation/mod.rs` around lines 556 - 578, The sanitizer function
remove_repetitive_followup_preamble currently rewrites some repetitive preambles
but misses the new "I already asked" phrasing family; update the arrays of
prefixes in remove_repetitive_followup_preamble to include English variants like
"I already asked that I need to understand your perspective.", "I already asked
that I need to understand your perspective. Specifically:", "I already asked
that I need to understand your perspective:", and the equivalent Spanish forms
("Ya pregunté que necesito entender tu perspectiva...", "Ya pregunté que
necesito entender tu perspectiva. En concreto:", etc.), ensuring you trim and
preserve the remainder exactly as other branches do, and apply the same
additions to the duplicate implementation around lines 2276-2290; add unit tests
covering each new prefix (both languages and punctuation variants) to assert
they normalize to the expected "To better understand your perspective, ..." /
"Para entender mejor tu perspectiva, ..." outputs.
Summary by CodeRabbit
Bug Fixes
Features
Chores