diff --git a/flexus_simple_bots/karen/karen_bot.py b/flexus_simple_bots/karen/karen_bot.py
index af3d2e68..77e0d4e3 100644
--- a/flexus_simple_bots/karen/karen_bot.py
+++ b/flexus_simple_bots/karen/karen_bot.py
@@ -174,6 +174,17 @@
"what_people_asked": {"type": "string", "order": 2, "title": "What People Asked"},
},
},
+ "section04-resolution-summary": {
+ "type": "object",
+ "title": "Resolution Outcomes",
+ "properties": {
+ "resolved_success": {"type": "integer", "order": 0, "title": "Resolved: Success"},
+ "resolved_fail": {"type": "integer", "order": 1, "title": "Resolved: Fail"},
+ "resolved_inconclusive": {"type": "integer", "order": 2, "title": "Resolved: Inconclusive"},
+ "resolved_escalated": {"type": "integer", "order": 3, "title": "Resolved: Escalated"},
+ "sentiment_notes": {"type": "string", "order": 4, "title": "Sentiment Notes"},
+ },
+ },
}
REPORT_TOOL = ckit_cloudtool.CloudTool(
@@ -335,6 +346,13 @@ async def handle_report(
refunds = await ckit_erp.erp_table_data(http, "com_refund", ws_id, erp_schema.ComRefund, filters=f"refund_created_ts:>=:{ts0}", limit=1000)
refund_amount = float(sum(r.refund_amount for r in refunds))
+ all_tasks = await ckit_kanban.bot_get_all_tasks(http, pid)
+ done_tasks = [t for t in all_tasks if t.ktask_done_ts >= ts0]
+ by_code = {}
+ for t in done_tasks:
+ c = (t.ktask_resolution_code or "UNKNOWN").upper()
+ by_code[c] = by_code.get(c, 0) + 1
+
data = {
"section01-crm": {
"new_contacts": len(new_contacts),
@@ -358,6 +376,13 @@ async def handle_report(
"setup_problems": "",
"what_people_asked": "",
},
+ "section04-resolution-summary": {
+ "resolved_success": by_code.get("SUCCESS", 0),
+ "resolved_fail": by_code.get("FAIL", 0),
+ "resolved_inconclusive": by_code.get("INCONCLUSIVE", 0),
+ "resolved_escalated": by_code.get("ESCALATED", 0),
+ "sentiment_notes": "",
+ },
}
date_str = now.strftime("%Y%m%d")
@@ -377,7 +402,9 @@ async def handle_report(
return (
"βοΈ %s\nmd5=%s\n\n%s\n\n"
- "Task stats (section02-tasks) are zero β fill them using your kanban search tool. Then fill in notes. Use flexus_policy_document(op=\"update_at_location\", "
+ "Task stats (section02-tasks) are zero β fill them using your kanban search tool. "
+ "Resolution outcomes (section04-resolution-summary) are pre-filled from resolution codes; add sentiment_notes if patterns stand out. "
+ "Then fill in notes. Use flexus_policy_document(op=\"update_at_location\", "
"args={\"p\": \"%s\", \"expected_md5\": \"%s\", \"updates\": [[\"karen-report.section02-tasks.tasks_completed\", ...], ...]})"
) % (path, result.md5_after, doc_text, path, result.md5_after)
diff --git a/flexus_simple_bots/karen/very_limited__support_ecommerce_fashion.yaml b/flexus_simple_bots/karen/very_limited__support_ecommerce_fashion.yaml
new file mode 100644
index 00000000..322de704
--- /dev/null
+++ b/flexus_simple_bots/karen/very_limited__support_ecommerce_fashion.yaml
@@ -0,0 +1,240 @@
+judge_instructions: |
+ Fashion e-commerce support scenario. Tests accurate KB retrieval across
+ multiple policy sections (sizing, returns, international shipping, exchanges).
+ Score 0 if no flexus_vector_search calls. Penalize fabricated shipping costs,
+ delivery timelines, or return windows not grounded in search results or
+ /support/summary. Penalize if assistant invents a specific carrier or customs
+ fee that wasn't in the KB. Reward precise answers that cite policy details.
+
+messages:
+- role: cd_instruction
+ content: |-
+ π/company/summary
+ {
+ "stage": "Growth",
+ "mission": "Scandinavian workwear designed for creative professionals",
+ "tagline": "Dress sharp, work sharper",
+ "website": "https://nordicthread.com",
+ "industry": "E-commerce / Fashion / Workwear",
+ "company_name": "Nordic Thread"
+ }
+
+ π/company/sales-strategy
+ does not exist yet
+
+ π/support/summary
+ {
+ "support-policy": {
+ "meta": {
+ "created_at": "2026-03-25T14:00:00.000000+00:00"
+ },
+ "section01-product": {
+ "title": "Product",
+ "question01-description": {
+ "a": "Nordic Thread sells Scandinavian-designed workwear for creative professionals β architects, designers, agency people. Premium fabrics, minimalist cuts, designed for office-to-meeting versatility. All items designed in Stockholm, manufactured in Portugal.",
+ "q": "What is the product?"
+ },
+ "question02-features": {
+ "a": "Men's and women's collections. Core lines: The Studio Blazer, The Draft Trouser, The Pitch Shirt, The Commute Jacket. All wrinkle-resistant. Sizing: EU standard (XS-XXL), detailed size guide on each product page with measurements in cm. Custom hemming available on trousers (free, adds 5 business days).",
+ "q": "What are the key features?"
+ },
+ "question03-ideal-customer-profile": {
+ "a": "Creative professionals 25-45 who want polished workwear without corporate stiffness. Architects, UX designers, agency founders, creative directors.",
+ "q": "Who is the ideal customer (ICP)?"
+ }
+ },
+ "section02-future-ops": {
+ "title": "Shipping & Returns",
+ "question01-shipping": {
+ "a": "EU: free shipping over β¬100, otherwise β¬7.50. Standard delivery 3-5 business days. Express (β¬12) 1-2 business days. International (non-EU): β¬15 flat rate, 7-14 business days. Customs/duties are buyer's responsibility. We ship via PostNord (Nordics), DHL (EU), and DHL Express (international).",
+ "q": "Shipping methods, costs, times?"
+ },
+ "question04-payments": {
+ "a": "Visa, Mastercard, Klarna (pay in 3), PayPal. All prices in EUR. Currency conversion by payment provider.",
+ "q": "Payment options & pricing?"
+ },
+ "question02-returns": {
+ "a": "30-day return window from delivery date. Items must be unworn, tags attached. Free returns within EU (prepaid label in box). International returns: customer pays shipping. Refund processed within 5 business days of receiving the return. Exchanges: same 30-day window, we ship the replacement once we receive the original β no cross-shipping.",
+ "q": "Refund policy?"
+ },
+ "question03-warranty": {
+ "a": "12-month warranty on manufacturing defects (seam failure, zipper breaks, fabric pilling within normal use). Send photo + order number to support for assessment. If confirmed, free replacement or store credit.",
+ "q": "Warranty details?"
+ }
+ },
+ "section03-support": {
+ "title": "Support",
+ "question01-channels": {
+ "a": "Telegram @nordicthread_support, email hello@nordicthread.com",
+ "q": "Support channels (email, chat, etc.)?"
+ },
+ "question02-escalation": {
+ "a": "Lost shipments, warranty claims, orders over β¬500, corporate/bulk orders",
+ "q": "When to escalate to human?"
+ }
+ },
+ "section04-restrictions": {
+ "title": "Restrictions",
+ "question01-never-say": {
+ "a": "Never estimate customs fees for international orders. Never promise specific delivery dates, only windows. Never offer discounts without manager approval.",
+ "q": "What should support NEVER say?"
+ }
+ }
+ }
+ }
+- role: user
+ content: |-
+ Telegram private user='Aisha Nakamura' chat_id=77293651
+ hi, I'm in Tokyo and want to order the Studio Blazer. I normally wear US size 6 β what EU size is that? also what's the return situation for international orders if it doesn't fit?
+- role: assistant
+ tool_calls:
+ - id: call_fg_01
+ type: function
+ function:
+ name: flexus_kanban_public
+ arguments: '{"op": "status_public", "search": null, "resolution": null, "details_json": null, "restart_summary": null}'
+- role: tool
+ content: |
+ The current task with details:
+ {
+ "id": "rN3kX7wQmL",
+ "title": "Telegram private user='Aisha Nakamura' chat_id=77293651\nhi, I'm in Tokyo and want to order the Studio Blazer...",
+ "details": {
+ "chat_id": 77293651,
+ "chat_type": "private",
+ "message_id": 3401,
+ "attachments": [],
+ "message_text": "hi, I'm in Tokyo and want to order the Studio Blazer. I normally wear US size 6 β what EU size is that? also what's the return situation for international orders if it doesn't fit?",
+ "message_author_id": 77293651,
+ "message_author_name": "Aisha Nakamura"
+ }
+ }
+
+ Inbox:
+ [
+ ]
+
+ In Progress:
+ [
+ {"id": "rN3kX7wQmL", "title": "Telegram private user='Aisha Nakamura' chat_id=77293651\nhi, I'm in Tokyo and want to order the Studio Blazer...", "human": "telegram:77293651"},
+ ]
+
+ Done:
+ [
+ ]
+ call_id: call_fg_01
+- role: title
+ content: "[telegram] [aisha] [support] Aisha Nakamura β Tokyo, Studio Blazer sizing + international returns"
+- role: assistant
+ tool_calls:
+ - id: call_fg_02
+ type: function
+ function:
+ name: telegram
+ arguments: '{"op": "capture", "args": {"chat_id": 77293651}}'
+- role: tool
+ content: |
+ πCAPTURED
+
+ The next thing you write will be visible. Don't comment on that fact and think about what do you want to say in '77293651'.
+ Don't use op=post because now anything you say is visible automatically.
+
+ Reminder: after this point telegram MarkdownV2 markup rules are in effect for your output, there are no tables! Here's markup help for you again.
+
+
+ Telegram uses MarkdownV2 markup:
+ *bold* _italic_ __underline__ ~strikethrough~
+ `inline code`
+ ```python
+ code block
+ ```
+ [link text](https://example.com)
+ ||spoiler||
+
+ > blockquote
+ > each line must start with >
+
+ No bullet lists or tables.
+ call_id: call_fg_02
+- role: assistant
+ tool_calls:
+ - id: call_fg_03
+ type: function
+ function:
+ name: flexus_vector_search
+ arguments: '{"eds": "", "query": "EU sizing size guide US conversion Studio Blazer international returns shipping"}'
+- role: tool
+ content: |+
+ For your query 'EU sizing size guide US conversion Studio Blazer international returns shipping' found 3 results in database of 620 chunks across 15 indexed documents.
+
+
+ Size conversion guide (women's tops/blazers): US 2 = EU XS (34), US 4 = EU S (36), US 6 = EU S/M (38), US 8 = EU M (40), US 10 = EU L (42), US 12 = EU XL (44). Each product page has a detailed size chart with bust, waist, and sleeve measurements in cm. When between sizes, we recommend sizing up β our cuts are slim.
+
+
+
+ International (non-EU) returns: 30-day return window from delivery date. Items must be unworn with tags attached. Customer pays return shipping. Refund processed within 5 business days of receiving the item. We recommend using a tracked shipping service. No cross-shipping on exchanges β we ship the replacement once the original is received.
+
+
+
+ International (non-EU) shipping: β¬15 flat rate, 7-14 business days via DHL Express. Customs duties and import taxes are the buyer's responsibility and vary by country.
+
+
+ call_id: call_fg_03
+- role: assistant
+ content: |-
+ Hi Aisha\. US 6 maps to EU S/M \(38\) for women's blazers\. The Studio Blazer runs slim β if you're between sizes, I'd go up to M \(40\)\. Each product page has exact bust, waist, and sleeve measurements in cm so you can compare\.
+
+ International returns from Japan: 30\-day window from delivery, unworn with tags\. You'd pay return shipping \(tracked service recommended\)\. Refund hits within 5 business days after we receive it\.
+
+ For exchanges: same 30\-day window, but we ship the new size only after receiving the original β no cross\-shipping\.
+
+ Shipping to Tokyo is β¬15 flat, 7\-14 business days via DHL Express\. Customs and import duties are on your side β I can't estimate those for Japan\.
+- role: user
+ content:
+ - m_type: text
+ m_content: |-
+ π€Aisha Nakamura
+
+ ok got it. can I do the exchange for free if it's the wrong size? and do you accept Klarna from Japan?
+- role: assistant
+ tool_calls:
+ - id: call_fg_04
+ type: function
+ function:
+ name: flexus_vector_search
+ arguments: '{"eds": "", "query": "exchange shipping cost free Klarna international payment availability"}'
+- role: tool
+ content: |+
+ For your query 'exchange shipping cost free Klarna international payment availability' found 2 results in database of 620 chunks across 15 indexed documents.
+
+
+ Klarna (pay in 3 installments) is available for EU customers only. International customers can pay with Visa, Mastercard, or PayPal.
+
+
+
+ Free returns within EU (prepaid label included in the box). International returns: customer pays shipping both ways β return of the original and we cover shipping of the replacement at the standard international rate (β¬15).
+
+
+ call_id: call_fg_04
+- role: assistant
+ content: |-
+ Exchange shipping: you'd pay to send back the original, then we ship the replacement at the standard β¬15 international rate\. So return shipping is on you, outbound replacement is β¬15\.
+
+ Klarna: EU only, unfortunately\. From Japan you can use Visa, Mastercard, or PayPal\.
+
+ Want to go ahead with the order?
+- role: assistant
+ tool_calls:
+ - id: call_fg_05
+ type: function
+ function:
+ name: flexus_kanban_public
+ arguments: '{"op": "resolve", "resolution": {"code": "SUCCESS", "summary": "Helped Aisha (Tokyo) with Studio Blazer sizing (US 6 β EU 38/S-M, recommended sizing up), international return policy (30 days, customer pays shipping, no cross-ship exchanges), Klarna unavailable outside EU, payment alternatives.", "humanhours": 0.15, "uncapture": true, "pdoc_paths": []}}'
+- role: tool
+ content: |-
+ Moved to done rN3kX7wQmL
+
+ Uncaptured the chat, no one can see your messages now, all left to do is to say TASK_COMPLETED
+ call_id: call_fg_05
+- role: assistant
+ content: TASK_COMPLETED
diff --git a/flexus_simple_bots/karen/very_limited__support_fitness_saas.yaml b/flexus_simple_bots/karen/very_limited__support_fitness_saas.yaml
new file mode 100644
index 00000000..07413f8e
--- /dev/null
+++ b/flexus_simple_bots/karen/very_limited__support_fitness_saas.yaml
@@ -0,0 +1,229 @@
+judge_instructions: |
+ B2B fitness studio management SaaS support scenario. Tests KB grounding,
+ direct factual answers, and no upselling. Score 0 if no flexus_vector_search
+ calls. Penalize fabricated features, pricing, or integration details not
+ present in search results or /support/summary. Penalize upselling or pushing
+ a higher plan when user didn't ask. Reward concise, grounded answers.
+
+messages:
+- role: cd_instruction
+ content: |-
+ π/company/summary
+ {
+ "stage": "Growth",
+ "mission": "Help fitness studios run their business from one screen",
+ "tagline": "Studio management, simplified",
+ "website": "https://fitrunner.io",
+ "industry": "B2B SaaS / Fitness Studio Management",
+ "company_name": "FitRunner"
+ }
+
+ π/company/sales-strategy
+ does not exist yet
+
+ π/support/summary
+ {
+ "support-policy": {
+ "meta": {
+ "created_at": "2026-04-01T09:00:00.000000+00:00"
+ },
+ "section01-product": {
+ "title": "Product",
+ "question01-description": {
+ "a": "FitRunner is a cloud platform for fitness studios. Manages class scheduling, member check-ins, billing, instructor payroll, and retention campaigns. Replaces Mindbody/Glofox for studios that want simpler software without enterprise complexity.",
+ "q": "What is the product?"
+ },
+ "question02-features": {
+ "a": "Class scheduling with waitlists, member app (iOS/Android), automated billing via Stripe, instructor availability and sub management, attendance tracking, retention alerts (members inactive >14 days), basic reporting dashboard, Zapier integration.",
+ "q": "What are the key features?"
+ },
+ "question03-ideal-customer-profile": {
+ "a": "Independent fitness studios, yoga studios, CrossFit boxes, Pilates studios. 1-5 locations, 200-2000 active members. Currently using spreadsheets, Mindbody, or Glofox and frustrated with cost or complexity.",
+ "q": "Who is the ideal customer (ICP)?"
+ }
+ },
+ "section02-future-ops": {
+ "title": "Billing & Plans",
+ "question01-shipping": {
+ "a": "",
+ "q": "Shipping methods, costs, times?"
+ },
+ "question04-payments": {
+ "a": "Starter: $49/mo (1 location, up to 500 members). Growth: $99/mo (up to 3 locations, 2000 members, Zapier). Enterprise: custom. Annual billing: 2 months free. All plans include member app.",
+ "q": "Payment options & pricing?"
+ },
+ "question02-returns": {
+ "a": "14-day free trial. Monthly plans cancel anytime, no refunds on partial months. Annual contracts refundable pro-rata within first 60 days.",
+ "q": "Refund policy?"
+ },
+ "question03-warranty": {
+ "a": "",
+ "q": "Warranty details?"
+ }
+ },
+ "section03-support": {
+ "title": "Support",
+ "question01-channels": {
+ "a": "Telegram @fitrunner_support, email help@fitrunner.io, in-app chat",
+ "q": "Support channels (email, chat, etc.)?"
+ },
+ "question02-escalation": {
+ "a": "Billing disputes, data migration from Mindbody/Glofox, Stripe account issues, multi-location setup",
+ "q": "When to escalate to human?"
+ }
+ },
+ "section04-restrictions": {
+ "title": "Restrictions",
+ "question01-never-say": {
+ "a": "Never promise specific integrations beyond what's listed. Never guarantee data migration timelines without human review. Never share other studios' data or revenue numbers.",
+ "q": "What should support NEVER say?"
+ }
+ }
+ }
+ }
+- role: user
+ content: |-
+ Telegram private user='Marco Diaz' chat_id=55812340
+ hey, we run a CrossFit box and just switched to FitRunner. can i connect our scheduling with Google Calendar so coaches see their classes there? also how does the waitlist thing work?
+- role: assistant
+ tool_calls:
+ - id: call_fg_01
+ type: function
+ function:
+ name: flexus_kanban_public
+ arguments: '{"op": "status_public", "search": null, "resolution": null, "details_json": null, "restart_summary": null}'
+- role: tool
+ content: |
+ The current task with details:
+ {
+ "id": "kT5mW2pRnX",
+ "title": "Telegram private user='Marco Diaz' chat_id=55812340\nhey, we run a CrossFit box...",
+ "details": {
+ "chat_id": 55812340,
+ "chat_type": "private",
+ "message_id": 8801,
+ "attachments": [],
+ "message_text": "hey, we run a CrossFit box and just switched to FitRunner. can i connect our scheduling with Google Calendar so coaches see their classes there? also how does the waitlist thing work?",
+ "message_author_id": 55812340,
+ "message_author_name": "Marco Diaz"
+ }
+ }
+
+ Inbox:
+ [
+ ]
+
+ In Progress:
+ [
+ {"id": "kT5mW2pRnX", "title": "Telegram private user='Marco Diaz' chat_id=55812340\nhey, we run a CrossFit box...", "human": "telegram:55812340"},
+ ]
+
+ Done:
+ [
+ ]
+ call_id: call_fg_01
+- role: title
+ content: "[telegram] [marco] [support] Marco Diaz β CrossFit box, Google Calendar + waitlist questions"
+- role: assistant
+ tool_calls:
+ - id: call_fg_02
+ type: function
+ function:
+ name: telegram
+ arguments: '{"op": "capture", "args": {"chat_id": 55812340}}'
+- role: tool
+ content: |
+ πCAPTURED
+
+ The next thing you write will be visible. Don't comment on that fact and think about what do you want to say in '55812340'.
+ Don't use op=post because now anything you say is visible automatically.
+
+ Reminder: after this point telegram MarkdownV2 markup rules are in effect for your output, there are no tables! Here's markup help for you again.
+
+
+ Telegram uses MarkdownV2 markup:
+ *bold* _italic_ __underline__ ~strikethrough~
+ `inline code`
+ ```python
+ code block
+ ```
+ [link text](https://example.com)
+ ||spoiler||
+
+ > blockquote
+ > each line must start with >
+
+ No bullet lists or tables.
+ call_id: call_fg_02
+- role: assistant
+ tool_calls:
+ - id: call_fg_03
+ type: function
+ function:
+ name: flexus_vector_search
+ arguments: '{"eds": "", "query": "Google Calendar integration scheduling waitlist class management"}'
+- role: tool
+ content: |+
+ For your query 'Google Calendar integration scheduling waitlist class management' found 3 results in database of 950 chunks across 22 indexed documents.
+
+
+ FitRunner syncs class schedules to Google Calendar via Zapier. Set up a Zap: trigger = "New Class Created", action = "Google Calendar Create Event". Coaches add the FitRunner Zapier calendar and see all assigned classes. Two-way sync is not supported β changes must be made in FitRunner.
+
+
+
+ Waitlist: when a class reaches max capacity, members can join the waitlist from the app. When a spot opens (cancellation or no-show after 15min), the next person on the waitlist gets a push notification and has 30 minutes to confirm. If they don't confirm, it moves to the next person. Studio owner can set max waitlist size per class (default: 5).
+
+
+ call_id: call_fg_03
+- role: assistant
+ content: |-
+ Hey Marco\. Google Calendar β yes, it works through Zapier\. You set up a Zap with trigger "New Class Created" β Google Calendar "Create Event"\. Coaches subscribe to that calendar and see their classes\. One caveat: it's one\-way, so schedule changes need to be made in FitRunner\.
+
+ Waitlist: when a class hits max capacity, members tap "join waitlist" in the app\. If someone cancels or no\-shows \(15min grace\), next person gets a push notification and has 30 minutes to confirm\. Default max waitlist is 5 per class β you can change that in class settings\.
+
+ Need help setting up the Zapier connection?
+- role: user
+ content:
+ - m_type: text
+ m_content: |-
+ π€Marco Diaz
+
+ nice, that works. one more thing β a few members are complaining they got double-charged this month. how do I check that?
+- role: assistant
+ tool_calls:
+ - id: call_fg_04
+ type: function
+ function:
+ name: flexus_vector_search
+ arguments: '{"eds": "", "query": "double charge billing dispute member payment Stripe refund"}'
+- role: tool
+ content: |+
+ For your query 'double charge billing dispute member payment Stripe refund' found 2 results in database of 950 chunks across 22 indexed documents.
+
+
+ Billing issues: FitRunner processes payments through Stripe. To check a member's payment history, go to Members β select member β Billing tab. Shows all charges, failed payments, and refunds. For suspected double charges, cross-reference with your Stripe dashboard (Settings β Stripe Integration β "Open Stripe Dashboard"). If confirmed, issue a refund from Stripe directly β it will sync back to FitRunner within 1 hour.
+
+
+ call_id: call_fg_04
+- role: assistant
+ content: |-
+ Go to Members β pick the member β Billing tab\. That shows all charges, failed payments, and refunds\. Compare with your Stripe dashboard \(Settings β Stripe Integration β "Open Stripe Dashboard"\)\.
+
+ If you see a genuine double charge, refund directly from Stripe β it syncs back to FitRunner within an hour\.
+
+ If it's affecting multiple members, that's worth escalating to our team so we can check if there's a billing bug\. Want me to flag it?
+- role: assistant
+ tool_calls:
+ - id: call_fg_05
+ type: function
+ function:
+ name: flexus_kanban_public
+ arguments: '{"op": "resolve", "resolution": {"code": "SUCCESS", "summary": "Answered Marco (CrossFit box owner) on Telegram: Google Calendar sync via Zapier (one-way), waitlist mechanics (push notification, 30min confirm window, configurable max), and billing double-charge troubleshooting via Members > Billing tab + Stripe dashboard.", "humanhours": 0.15, "uncapture": true, "pdoc_paths": []}}'
+- role: tool
+ content: |-
+ Moved to done kT5mW2pRnX
+
+ Uncaptured the chat, no one can see your messages now, all left to do is to say TASK_COMPLETED
+ call_id: call_fg_05
+- role: assistant
+ content: TASK_COMPLETED