Skip to content

Fix/buyer payment claim react#51

Merged
grunch merged 3 commits intomainfrom
fix/buyer-payment-claim-react
Apr 30, 2026
Merged

Fix/buyer payment claim react#51
grunch merged 3 commits intomainfrom
fix/buyer-payment-claim-react

Conversation

@grunch
Copy link
Copy Markdown
Member

@grunch grunch commented Apr 29, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Fixed duplicate clarification messages being sent consecutively in dispute resolution.
    • Improved handling of repetitive or disrespectful language in follow-up responses.
  • Features

    • Enhanced evidence-gathering requirements for payment disputes to request specific transaction details.
    • Strengthened safeguards to prevent scolding or accusatory language in follow-up communications.
  • Chores

    • Adjusted mid-session evaluation logic and escalation thresholds to optimize dispute handling flow.

grunch added 2 commits April 29, 2026 15:40
…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.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Walkthrough

The 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

Cohort / File(s) Summary
Prompt & Guidance Updates
prompts/phase3-classification.md, prompts/phase3-message-templates.md, src/reasoning/openai.rs
Tightened constraints for follow-up clarifications to require concrete, specific questions and forbid repetitive/scolding phrasing (e.g., "I already asked"). Updated classification prompt output contract to enforce polite formatting.
Specification & Control Flow
specs/003-guided-mediation/spec.md
Added FR-127a rule: evaluator must gate clarification dispatch based on the most recent non-stale transcript row direction to prevent duplicate outbound messages and track evaluation state via round_count_last_evaluated.
Mediation Logic & Database
src/db/mediation.rs, src/mediation/follow_up.rs, src/mediation/mod.rs
Introduced DB helper latest_nonstale_message_direction() to determine outbound status; extended advance_session_round() with outbound-latest guard to skip evaluation and update idempotency marker; added helper to remove repetitive Spanish/English preambles from follow-up text.
Policy Constant
src/mediation/policy.rs
Increased EARLY_MIDSESSION_BYPASS_FOLLOWUPS from 3 to 6 to extend the clarification bypass window before escalation, with updated rationale citing recent evaluation runs.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • mostronatorcoder

Poem

🐰 A guard at the gate, so wise and true,
Checks if a message just flew through,
No double-ask, no scolding tone,
Let one word speak—no gears overthrown!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Fix/buyer payment claim react' is vague and generic, using non-descriptive terminology that fails to convey the specific changes in this pull request. Replace with a more specific title that describes the main change, such as 'Improve buyer payment claim clarification guidance and prevent duplicate outbound messages' or similar.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/buyer-payment-claim-react

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +191 to +193
let tx = guard.transaction()?;
db::mediation::advance_evaluator_marker(&tx, session_id, total_fresh_inbounds)?;
tx.commit()?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between b38ced2 and c9824af.

📒 Files selected for processing (8)
  • prompts/phase3-classification.md
  • prompts/phase3-message-templates.md
  • specs/003-guided-mediation/spec.md
  • src/db/mediation.rs
  • src/mediation/follow_up.rs
  • src/mediation/mod.rs
  • src/mediation/policy.rs
  • src/reasoning/openai.rs

Comment thread src/mediation/mod.rs
Comment on lines +556 to +578
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()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

@grunch grunch merged commit 963d37e into main Apr 30, 2026
2 checks passed
@grunch grunch deleted the fix/buyer-payment-claim-react branch April 30, 2026 12:01
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