AVINASHNEGI

Build log · Yojana Sathi · Sept 2026

I BUILT A WELFARE BOT THAT REFUSES TO ANSWER

Yojana Sathi — why I picked it, how I built it with AI, the bugs a real phone found, and what one stranger's feedback changed

BY AVINASH NEGI 16 SEPT 2026 · UPDATED 28 SEPT 9 MIN READ

01 SectionThe number that decided the architecture

₹455 a day for a male casual labourer. ₹315 for a female. That's the Periodic Labour Force Survey, 2025. I can point at the PDF.

Now picture the trip:

  • A worker is told a scheme is theirs.
  • They skip a day's work, pay the bus both ways, stand in line at a Common Service Centre.
  • The operator says no.

That wrong answer cost ₹455. Most people never come back for a second try.

  • A wrong yes costs a day's wage.
  • A wrong no costs an entitlement they'll never ask about again.

Two errors, different sizes, different units. Everything in this project follows from that.


02 SectionHow I got here

Yojana Sathi is my second entry for Code for a Billion.

The first was Saans, an air-exposure bot on Telegram. 3,500 lines, zero dependencies, live bot, public repo. I parked it on 30 August.

  • The reason I told myself: the idea already existed in another app.
  • The real reason: Saans was never deployed and never used by one person — and 25% of the score is deployment and impact.

Losing on novelty was a feeling. Losing on "zero users" is a score. Telling those apart is the best thing Saans gave me.

The problem I moved to:

  • India has about 44 crore unorganised workers — construction, domestic work, driving, vending.
  • 31.48 crore are on e-Shram, with 14 central schemes mapped to it.
  • The schemes exist. The rules are scattered, in English, in bureaucratic language — and a failed trip costs a day's wage.

"But myScheme.gov.in exists." It does, and it's good. It's also a web form that assumes literacy, a browser, and knowing what "land holding in hectares" means.

Yojana Sathi is a Hindi or English chat on a ₹6,000 phone that ends in a checklist and an address. Last mile on top of the government site — not a replacement.


03 SectionFour rules, decided before any code

  1. The model never touches eligibility. Eligibility is a pure function — no network, no clock, no randomness. A model may map "I lay bricks" to a job category, always confirmed by the worker. It never sees a threshold or a verdict. A test fails if the rules package imports a model. No API key is a tested setup, not a degraded one.

  2. Unresearched is "TODO", never 0. A zero looks researched, and no validator can tell. A file with a "TODO" still loads, gets flagged, and answers UNKNOWN.

  3. Three verdicts: ELIGIBLE, INELIGIBLE, UNKNOWN. "I couldn't check this — ask this exact question at the centre" costs neither a day nor an entitlement. Unknown is an answer, not a failure.

  4. No name, phone or Aadhaar field. Not "we don't store it" — the fields don't exist. Coarse bands in the log, a random session id, and any group under five people is hidden.

The shape that came out of it:

  • One plain-text rule file per scheme. Every value links to the official sentence it came from, and the date it was read.
  • One rule engine. One conversation, with buttons, in Hindi or English.
  • Three doors into it: Telegram, WhatsApp and a browser page.
  • Python standard library only. SQLite for the log. One small AWS server.

04 SectionThe AI part, honestly

The uncomfortable part first: I did not type most of this code. Claude Code wrote it. I described what I wanted and read the diff. Call it vibe coding — that's fair.

What I did do, and the git log shows it:

  • Decided the four rules above.
  • Read every government source myself. Not one threshold, rupee figure or age band was authored by a model. Every scheme carries my signature, written by a tool that refuses a signature from anything that looks like a bot.
  • Used the bot like a worker, and found the bugs the tests missed.
  • Set up Telegram, AWS and Meta by hand, and fixed what broke on a real server at night.

What worked:

  • Plan before edit. For anything touching two files: read, plan, I approve, then write. A one-line prompt gives a diff harder to review than to write.
  • Commit messages that name who gets hurt. Read a tax "Yes" back before it costs someone a pension. When the message must say the consequence, you notice it.
  • Break the fix on purpose. Revert the fix; the new test must go red. If it stays green, it's decoration. Ninety seconds, most bad tests caught.
  • Two models arguing beat one agreeing. Codex and Claude Code reviewed each other's fixes. Each caught the other's bugs — including a Codex fix that left my exhaustive test green while it tested nothing. Neither was reliably better. The value was the disagreement: every one was something I had to understand to settle.

What didn't:

  • Facts about the real world. Great at "restructure this state machine". Useless at "what does the PM-SYM page say at age 32". You get a plausible number — the exact thing this project exists to prevent.
  • Reviews that read my own comments back to me as discoveries — and scored "domain correctness 7.6" without opening one source.
  • Advice that broke a rule the same reviewer praised. "Route them to the CSC 4 km away" needs a location in the profile. The privacy rule is that there isn't one. I said no, and wrote down why.

05 SectionFive bugs worth telling

1. The README said "don't screen a real worker." The code didn't care.

  • I'd written the warning in bold in the README, atop every scheme file, and in a test. Three places, all prose.
  • Meanwhile the code treated "no TODOs left" as "verified" — and served real verdicts off data one person had copied from a PDF once.
  • Now researched and signed by a named person are separate. Only both together can give a verdict.

A warning in a README protects nobody. If a rule matters, the code that would break it must be the thing that stops.

2. Six bugs in the first hour of real use — none in the engine

  • A null field made Telegram reject a message and silently kill the session.
  • A free-text box looped back to the menu that led to it.
  • No income band for someone earning nothing.
  • English messages with Hindi buttons under them.

Every test checked message text. The bugs were in the buttons. Test what the user touches, not what's easy to check.

3. A double tap wrote the bank answer into the tax field

  • On a cheap phone with weak signal, people tap twice.
  • A stale "Yes, I have a bank account" landed as "Yes, I pay income tax" — which disqualifies someone from a pension.
  • The fix: accept a tap only from the keyboard currently on screen. Tested by driving a real double tap.

4. My impact number was six times too big

  • I was adding PMSBY's ₹2,00,000 accident cover to PM-SYM's ₹36,000-a-year pension.
  • An insurance payout that may never happen and a yearly pension are different money. They're separate numbers everywhere now.
  • The label travels with the figure: entitlement surfaced, not money delivered.
  • A week later the same bug came back one layer up, in the dashboard.

A crash is visible. An inflated number survives all the way to a judge.

5. The things no assistant could do for me

  • AWS: I pasted the console password and the access key into the chat, because the screen showed all three and I didn't know which was secret. All of it was treated as burned and replaced within a minute. Now I know what an access key looks like.
  • Meta: WhatsApp went silent a day after it first worked. The test token expires in 24 hours. The permanent one is four screens deep, mentioned nowhere near the first. One evening.
  • Deploying: parked for days behind Azure verification and a card Fly.io declined — until I tried AWS and found $120 of credit already there. Under an hour, mostly waiting for the server to boot.

Deploy before you polish. Only one of them is measured.


06 SectionOne piece of feedback, one long day

A visitor from Coimbatore wrote: the site was clean, but they couldn't pick Tamil Nadu. Only six states had buttons.

One sentence. It changed the whole front of the product:

  • Every state and union territory, all 36. A clickable India map in the browser; paged buttons on Telegram and WhatsApp.
  • State first, on every channel. Then national schemes, your state's, or both. Then tick some, or "All of these". Another state's pension is never shown.
  • A state with nothing checked yet says so — "No Tamil Nadu schemes are checked yet. Here are the national schemes." — instead of an empty list.

Then the schemes. I researched all 36 states from official pages, and signed 11 more in one day:

  • Each one checked against a government PDF I downloaded myself, saved in the repo with its fingerprint (SHA-256).
  • Sikkim, Punjab, Madhya Pradesh, Uttarakhand, and four national schemes.
  • 21 signed now. Two drafts — Tamil Nadu and Mizoram — stay unanswered, because no official page lists their documents. I won't guess a list.

Reading the sources — and the words — found real bugs:

  • PM Vishwakarma: my loan question let someone with a repaid PMEGP loan through. The official guidelines bar any PMEGP loan in five years. Only MUDRA and SVANidhi are forgiven once repaid.
  • The Uttarakhand widow pension had lost its signature because its rate rested on one aggregator site. A 2022 government order settled it. Re-signed.
  • A Hindi line said "for all three" whatever the number was. English said "for all of them". Nobody had counted.

Coverage came from one stranger's sentence, not from my roadmap.


07 SectionWhere it honestly stands

Live sathi.avinashnegi.com in the browser, and @YojanaSathiBot on Telegram, 24×7 on AWS
Schemes 21 signed — 13 national, 8 state. Every value linked to its official source. 2 drafts parked.
States All 36 selectable. State pensions for Uttarakhand, Sikkim, Punjab and Madhya Pradesh; national schemes everywhere.
WhatsApp Built and verified on Meta's test number. Not public yet — that needs business verification and a second SIM.
Tests Zero dependencies. Every button in both languages, and an independent rule check across 3.1 million verdicts.
Real workers screened Zero

That last row is the honest one. What's missing is person-shaped: a native speaker to read the Hindi aloud, a worker to press Start, a partner at a CSC. None of it is "the code doesn't work".

One risk sits above everything: the hackathon form requires a box saying the project was built in AgentFoundry, the official IDE. This was built locally. I've asked whether that counts — in a public GitHub discussion (no replies) and by email (it bounced). I'm logging every attempt instead of ticking the box. If the answer is no, the project loses a hackathon and keeps everything else.


08 SectionIf you're starting something like this

  • Decide what you won't be wrong about, before the editor. The ₹455 became a design rule.
  • Let the AI type. Don't let it decide, and don't let it know facts. A model that supplies a real-world number is a very confident liar.
  • Use your own thing on a real phone first. Every serious bug here was found that way.
  • Ship before you're ready. One stranger's sentence taught me more than a week of refactoring.