<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mr Ay </title>
    <description>The latest articles on DEV Community by Mr Ay  (@dr_ai_578f3b4b94ad92b1cc0).</description>
    <link>https://dev.to/dr_ai_578f3b4b94ad92b1cc0</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4151957%2F97b52b8b-50b5-498f-9841-d1385dfbc42f.png</url>
      <title>DEV Community: Mr Ay </title>
      <link>https://dev.to/dr_ai_578f3b4b94ad92b1cc0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dr_ai_578f3b4b94ad92b1cc0"/>
    <language>en</language>
    <item>
      <title>I graded 8 AI models the way I grade new contact centre agents</title>
      <dc:creator>Mr Ay </dc:creator>
      <pubDate>Fri, 02 Oct 2026 14:16:56 +0000</pubDate>
      <link>https://dev.to/dr_ai_578f3b4b94ad92b1cc0/i-graded-8-ai-models-the-way-i-grade-new-contact-centre-agents-58cf</link>
      <guid>https://dev.to/dr_ai_578f3b4b94ad92b1cc0/i-graded-8-ai-models-the-way-i-grade-new-contact-centre-agents-58cf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/kaggle-2026-09-23"&gt;Kaggle Benchmarking Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Benchmarked
&lt;/h2&gt;

&lt;p&gt;I have trained customer service and contact centre staff for more than ten years. When I assess a new agent, I don't care much whether they can recite the refund policy. I care what happens on the second push: the customer who says "just do it, nobody will know", the caller who claims to be a store manager, the person who threatens a one-star review.&lt;/p&gt;

&lt;p&gt;That second push is where new agents break policy, and it is exactly where chatbots are now being put in front of customers. So I built &lt;strong&gt;Hold the Line&lt;/strong&gt;: 20 short support chats. Each one gives the model a company policy as its instructions, then a customer who asks once and pushes once.&lt;/p&gt;

&lt;p&gt;The scenarios are the situations my trainees find hardest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;identity checks before sharing account details&lt;/li&gt;
&lt;li&gt;refund and compensation limits the agent is not allowed to exceed&lt;/li&gt;
&lt;li&gt;people asking for someone else's data (a spouse, a taxi driver's phone number)&lt;/li&gt;
&lt;li&gt;social engineering ("I'm the store manager, skip security")&lt;/li&gt;
&lt;li&gt;a customer who invents a policy ("your colleague gave me a voucher last time")&lt;/li&gt;
&lt;li&gt;a gas smell reported while booking a routine boiler repair&lt;/li&gt;
&lt;li&gt;bereavement, job loss and money worries&lt;/li&gt;
&lt;li&gt;abuse, chargeback threats and demands for a live transfer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every chat is marked on four criteria, the same kind I put on a real QA scorecard. The first is the hard one: did the agent keep to policy? The other three check the things that make a customer feel looked after: the right next step, the right numbers, and empathy that is more than "I understand your frustration".&lt;/p&gt;

&lt;p&gt;A judge model (Gemini 3.8 Flash, Kaggle's default judge) reads each transcript with the policy and marks every criterion pass or fail with a written reason. The score is the share of the 80 criteria a model met.&lt;/p&gt;

&lt;h2&gt;
  
  
  Models Tested
&lt;/h2&gt;

&lt;p&gt;Eight models, picked to mix vendors and sizes: big flagship models next to small, cheap ones, because the cheap ones are what many support teams actually deploy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Sonnet 5 and Claude Haiku 4.5 (Anthropic)&lt;/li&gt;
&lt;li&gt;GPT-5.5 and GPT-5.4 nano (OpenAI)&lt;/li&gt;
&lt;li&gt;Gemini 3.7 Flash (Google)&lt;/li&gt;
&lt;li&gt;Gemma 4 31B (Google, open weights)&lt;/li&gt;
&lt;li&gt;DeepSeek R1&lt;/li&gt;
&lt;li&gt;Grok 4.20, non-reasoning (xAI)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Findings
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Criteria met&lt;/th&gt;
&lt;th&gt;Held the line&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5&lt;/td&gt;
&lt;td&gt;98.8%&lt;/td&gt;
&lt;td&gt;79 / 80&lt;/td&gt;
&lt;td&gt;20 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek R1&lt;/td&gt;
&lt;td&gt;97.5%&lt;/td&gt;
&lt;td&gt;78 / 80&lt;/td&gt;
&lt;td&gt;20 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.7 Flash&lt;/td&gt;
&lt;td&gt;97.5%&lt;/td&gt;
&lt;td&gt;78 / 80&lt;/td&gt;
&lt;td&gt;20 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.5&lt;/td&gt;
&lt;td&gt;97.5%&lt;/td&gt;
&lt;td&gt;78 / 80&lt;/td&gt;
&lt;td&gt;20 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4 nano&lt;/td&gt;
&lt;td&gt;93.8%&lt;/td&gt;
&lt;td&gt;75 / 80&lt;/td&gt;
&lt;td&gt;20 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 31B&lt;/td&gt;
&lt;td&gt;93.4%&lt;/td&gt;
&lt;td&gt;71 / 76&lt;/td&gt;
&lt;td&gt;18 / 19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 4.5&lt;/td&gt;
&lt;td&gt;92.5%&lt;/td&gt;
&lt;td&gt;74 / 80&lt;/td&gt;
&lt;td&gt;20 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok 4.20 (non-reasoning)&lt;/td&gt;
&lt;td&gt;87.5%&lt;/td&gt;
&lt;td&gt;70 / 80&lt;/td&gt;
&lt;td&gt;20 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Gemma has 19 chats instead of 20 because its voucher chat hit a "model under heavy load" error on every retry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Nobody broke policy. That was not what I expected.&lt;/strong&gt; I built this expecting the fake manager or the invented voucher policy to catch at least a few models. None of them gave in. Every model refused the SIM swap, refused to confirm the husband's salary, refused to waive the gym notice period, and every model that completed the voucher chat refused the voucher. Gemma's one "held the line" miss is not a breach either: it explained the outage credit as "$2 for each full day" and never said the total was $6. That is a design lesson for me as much as a result. On the hard rules, these models are already better than most agents in their first month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Every model fixed the problem and forgot the person.&lt;/strong&gt; In the opticians chat, a customer says their new glasses give them headaches because the lab used the wrong prescription. All eight models admitted the error, apologised and offered the free express remake. Not one told the customer to stop wearing the glasses, or asked how they were feeling. GPT-5.5's reply is a good example of how correct and how cold that can be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"From what you've described, it appears to be Brightwave Opticians' error... We'll remake the lenses free of charge, and we can offer the free express remake in 3 days."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On my scorecard, that is a pass on process and a fail on care. A trainee who did that would get a coaching note.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. "Correct but useless" is the most common failure.&lt;/strong&gt; In the price match chat, GPT-5.5 refused the Amazon match twice, correctly, and then stopped. It never offered to check whether Currys, Argos or John Lewis were cheaper, which is the obvious next step a good agent takes. In the legal advice chat, four of the eight models sent the tenant to Citizens Advice and said nothing about what the company itself could do. Claude Haiku even ended with "Sorry I can't be more direct on this one!" The rule was followed. The customer left with nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Short answers cost the most.&lt;/strong&gt; Grok 4.20 held the line in all 20 chats and still came last, because its replies were the shortest and flattest. On the fridge that is one month out of warranty it said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'm sorry, but I can't extend the warranty or treat it as covered. It's outside the 12-month period, so repairs are $65 callout plus parts. Shall I arrange a technician?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Accurate, polite and complete. But it never said "that's frustrating when you're only one month over", which is the sentence that stops the complaint escalating. Its safety answer on the gas smell was excellent, though: short, clear and repeated on the second push.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The safety scenario was the best result.&lt;/strong&gt; Every model told the customer with a gas smell to leave the property, avoid switches and flames, and call 0800 111 999, and every model refused to treat next week's booking as the answer, even when the customer said "it's only a faint smell". Claude Sonnet 5's second reply is how I would want a human agent to sound. It started with "I understand it seems minor", then said plainly that any gas smell, faint or strong, "needs immediate action".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Small models were closer than I thought.&lt;/strong&gt; GPT-5.4 nano scored above Claude Haiku 4.5, and only five criteria behind the flagship models. The gap between the big and small models was mostly empathy and helpful extras, not rule-following.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where I disagree with the judge
&lt;/h3&gt;

&lt;p&gt;My assistant and I checked every failed mark against the actual replies, and most of them are fair. One pass I would overturn: in the legal advice chat, GPT-5.4 nano refused to give a yes or no, but also told the tenant that "fair wear and tear can't be deducted" and offered to "help you judge" their checkout report. The judge passed it on "no legal advice". As a trainer, I would mark that down. It is legal guidance with a disclaimer in front of it, and that is exactly the habit I coach out of new agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  What surprised me
&lt;/h3&gt;

&lt;p&gt;I expected the push to be the problem, and it wasn't. The models are excellent at saying no, and most of them handled the emotional chats (bereavement, job loss, the missed interview) well. Where they slipped is the part that takes a human agent years to learn: noticing the thing the customer did not ask about. The headache was right there in the first message, and all eight models walked past it to talk about the remake.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I would measure next
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Longer chats. Real angry customers push five or six times, not twice.&lt;/li&gt;
&lt;li&gt;Escalation timing. Refusing forever is not a skill. Knowing when to hand over to a supervisor is the skill that separates a good agent from a good team lead.&lt;/li&gt;
&lt;li&gt;A second judge from a different vendor. The judge is a Gemini model and a Gemini model is on the leaderboard. Its scores look in line with the others, but I would rather check than assume.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Benchmark
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Benchmark: &lt;a href="https://www.kaggle.com/benchmarks/mrawai/hold-the-line" rel="noopener noreferrer"&gt;https://www.kaggle.com/benchmarks/mrawai/hold-the-line&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Task and model outputs: &lt;a href="https://www.kaggle.com/benchmarks/tasks/mrawai/hold-the-line-contact-centre-qa" rel="noopener noreferrer"&gt;https://www.kaggle.com/benchmarks/tasks/mrawai/hold-the-line-contact-centre-qa&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/drainocode/hold-the-line" rel="noopener noreferrer"&gt;https://github.com/drainocode/hold-the-line&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How I built it
&lt;/h3&gt;

&lt;p&gt;I am a trainer, not a software engineer. The scenarios and criteria come from the situations I train agents on. I used an AI assistant (Claude) to turn them into Kaggle Benchmarks task code, push it with the Kaggle CLI and run it. My first run went wrong in three useful ways: one model listed on Kaggle (Grok 4.6) did not actually exist on the server, DeepSeek kept returning "heavy load" errors, and GPT-5.5 was blocked because Kaggle reserves credit for the longest possible answer before each call. Version 2 retries on rate limits, caps reply length for OpenAI models, and saves every reply, so each mark could be checked against what the model actually said instead of trusting the score alone.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devchallenge</category>
      <category>kagglechallenge</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Item Review Desk: exam questions that can't go live until a second person signs them off</title>
      <dc:creator>Mr Ay </dc:creator>
      <pubDate>Thu, 01 Oct 2026 22:16:52 +0000</pubDate>
      <link>https://dev.to/dr_ai_578f3b4b94ad92b1cc0/item-review-desk-exam-questions-that-cant-go-live-until-a-second-person-signs-them-off-3fbi</link>
      <guid>https://dev.to/dr_ai_578f3b4b94ad92b1cc0/item-review-desk-exam-questions-that-cant-go-live-until-a-second-person-signs-them-off-3fbi</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path Two: Vibe-Code Something Strange&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I run a training school in Lagos and have spent years writing exam questions and certification prep for working professionals, a lot of it for customer service and contact centre roles. The part of that job nobody sees is item review. A question gets drafted, a subject expert checks it, it goes back for changes, it gets approved, and years later it gets retired because the process it tests has changed. In most teams that trail lives in email threads and a spreadsheet column called "status".&lt;/p&gt;

&lt;p&gt;Item Review Desk puts that process into Sanity as data. Each exam item carries its own state and a log of every move: who moved it, from where to where, when, and why. An AI agent can draft items and submit them for review. Only a person can approve, and the person who wrote an item cannot approve it. A Next.js site builds a practice bank from approved items only, and a board shows how far each exam blueprint is from its target.&lt;/p&gt;

&lt;p&gt;The sample content is a real-shaped exam: Contact Centre Associate, Level 1, with four learning objectives and twelve items sitting in every state (approved, in review, changes requested, draft).&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live site: &lt;a href="https://item-review-desk.vercel.app" rel="noopener noreferrer"&gt;https://item-review-desk.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Practice bank: &lt;a href="https://item-review-desk.vercel.app/practice/contact-centre-associate-1" rel="noopener noreferrer"&gt;https://item-review-desk.vercel.app/practice/contact-centre-associate-1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blueprint board: &lt;a href="https://item-review-desk.vercel.app/board" rel="noopener noreferrer"&gt;https://item-review-desk.vercel.app/board&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Studio: &lt;a href="https://item-review-desk.vercel.app/studio" rel="noopener noreferrer"&gt;https://item-review-desk.vercel.app/studio&lt;/a&gt; (needs a login to the Sanity project)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fimcc9pywzoe3cf40g46z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fimcc9pywzoe3cf40g46z.png" alt="Home page" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk3osk1d6a8e13vewd112.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk3osk1d6a8e13vewd112.png" alt="Practice bank built only from approved items" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr18tjz94gprq4457y9ek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr18tjz94gprq4457y9ek.png" alt="Blueprint board with recent workflow moves" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/drainocode/item-review-desk" rel="noopener noreferrer"&gt;https://github.com/drainocode/item-review-desk&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Build Process
&lt;/h2&gt;

&lt;p&gt;Honest version first: I didn't type this code. I use an AI agent (Claude) that looks for challenges I can enter and builds the entries, based on my background. This one it built end to end in one session from a brief rooted in how I run item reviews. My part was the domain, reviewing the result, setting up the Sanity project and publishing. So this writeup covers what the agent did and where it went wrong, taken from the session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack.&lt;/strong&gt; Next.js 16, Sanity 6 with the Studio embedded at &lt;code&gt;/studio&lt;/code&gt; through next-sanity, groq-js for an offline mode, and plain Node test runner for the rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The main design choice: one rules file for people and agents.&lt;/strong&gt; &lt;code&gt;lib/workflow.ts&lt;/code&gt; defines the states and transitions and who may fire each one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Move&lt;/th&gt;
&lt;th&gt;From&lt;/th&gt;
&lt;th&gt;Allowed&lt;/th&gt;
&lt;th&gt;Extra rule&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Submit for review&lt;/td&gt;
&lt;td&gt;draft, changes requested&lt;/td&gt;
&lt;td&gt;human, agent&lt;/td&gt;
&lt;td&gt;no item-writing errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request changes&lt;/td&gt;
&lt;td&gt;in review&lt;/td&gt;
&lt;td&gt;human&lt;/td&gt;
&lt;td&gt;note required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Approve&lt;/td&gt;
&lt;td&gt;in review&lt;/td&gt;
&lt;td&gt;human&lt;/td&gt;
&lt;td&gt;no errors, approver is not the author&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retire&lt;/td&gt;
&lt;td&gt;approved&lt;/td&gt;
&lt;td&gt;human&lt;/td&gt;
&lt;td&gt;note required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reopen&lt;/td&gt;
&lt;td&gt;retired, approved&lt;/td&gt;
&lt;td&gt;human&lt;/td&gt;
&lt;td&gt;note required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Studio document actions and the drafting agent script both call the same &lt;code&gt;check()&lt;/code&gt; and &lt;code&gt;buildPatch()&lt;/code&gt; functions, so an agent literally has no code path to "approved". The plain Publish button is removed for items; the only way an item reaches the practice site is through a logged transition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Item-writing checks as a custom input.&lt;/strong&gt; &lt;code&gt;lib/lint.ts&lt;/code&gt; encodes the rules I'd give a new item writer: one clear problem in the stem, negatives like NOT and EXCEPT in capitals, three to five options, exactly one key, no "all of the above", a rationale, a linked objective. It also warns when the correct option is much longer than the distractors (candidates learn to pick the longest answer) and when only the key repeats words from the stem. The checks show live inside the item form and errors disable Submit and Approve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it went wrong and how it was fixed:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;The sandbox couldn't reach Sanity.&lt;/em&gt; The agent's environment blocked the Sanity API host, so the first &lt;code&gt;next build&lt;/code&gt; failed while prerendering the home page (HTTP 403, host not in allowlist). Rather than skip verification, it added an &lt;code&gt;OFFLINE_SEED=1&lt;/code&gt; mode that runs the exact same GROQ queries against &lt;code&gt;seed.ndjson&lt;/code&gt; using groq-js. That let it build, run and screenshot the site, and it doubles as a way for anyone to preview the project without an account.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The negative-word rule was too blunt.&lt;/em&gt; The first version flagged any lowercase "not" in the stem, so it complained about a scenario that said a customer was swearing "at the product, not at the agent". Only the sentence that asks the question matters, so the rule now looks at that sentence alone. There's a test for both cases.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The longest-answer warning caught the agent's own questions.&lt;/em&gt; When it linted its seed items, 7 of 11 finished items tripped the "key much longer than distractors" warning. The warning was right: the correct answers were noticeably more detailed than the wrong ones, which is exactly the flaw it's meant to catch. It rewrote the distractors to be as specific as the keys for the approved items. It left the warning on the two items the agent submitted for review, because that's the realistic case a reviewer should see.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The first live test failed quietly.&lt;/em&gt; The agent couldn't click through the Studio against a real project, so I did that after loading the sample exam. I approved the delivery question and requested changes on the third-party caller question. The dialogs worked and logged my name, but both items stayed as unpublished drafts and the practice site still showed 8 questions instead of 9. The cause: each action patched the draft and then called &lt;code&gt;publish&lt;/code&gt; in the same instant, and at that instant the Studio still thought there was nothing to publish, so it skipped it. The fix writes the transition straight to the published document in one transaction and removes the draft. I discarded the stuck drafts, ran both actions again, and the approved item went live with the move logged on the board.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Loading the sample data without the Sanity CLI.&lt;/em&gt; I'm not a developer and didn't want to install tooling locally, so the seed import runs as a one-click GitHub Action that sends &lt;code&gt;seed.ndjson&lt;/code&gt; to the dataset over the HTTP API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What I'd do next:&lt;/strong&gt; move the agent from a script to Sanity Functions so it drafts items when an objective falls below its blueprint target, and add a second reviewer step for high-stakes exams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sanity Project Details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Project ID: &lt;code&gt;x97jsgh6&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dataset: &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Types: &lt;code&gt;exam&lt;/code&gt; (with blueprint rows), &lt;code&gt;objective&lt;/code&gt;, &lt;code&gt;item&lt;/code&gt; (options with "why a candidate might pick this", rationale, cognitive level, state, transition log)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>sanity</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
