Government of Purvanchal · Social Welfare Department Skip to Main Content Track Application Departmental Login

Sewa Setu

Old age pension · Government of Purvanchal

Evaluation evidence

This page renders the published artifact of the live evaluation run against this deployment: scripted persona conversations driven over the real MCP endpoints and the portal, with assertions on the responses and on the database state they leave behind. Nothing here is staged: failures stay visible, each with the regression it guards.

All 13 scenarios passed 122 assertions · 90 agent steps · 1 skipped by design
13scenario(s) passed 0failed 1skipped 122checks 90steps
Run20261018T123830-8d5102
Started2026-10-18T12:38:30
Finished2026-10-18T12:38:34
Environmenthttps://sewasetuai.duckdns.org
Clocklive
MCP · citizenhttp://mcp-citizen:8101/mcp/citizen
MCP · officerhttp://mcp-officer:8102/mcp/officer
Run history (1 run(s))
WhenClockRanPassedFailedBase URL
2026-10-18T12:38:30livecitizen-happy-submit, citizen-underage-refused, citizen-outside-pilot, citizen-duplicate-active, citizen-withdraw-and-reapply, citizen-idempotent-submit, citizen-status-isolation, citizen-window-closed, citizen-token-boundaries, citizen-session-lifecycle, officer-queue-and-case, officer-approve-with-reason, officer-reason-and-stale-guards, officer-program-token-readonly 130https://sewasetuai.duckdns.org

Kamala applies with her assistant and tracks the receipt

passed   citizen · persona: kamala · 12 step(s) · 0.5s

Kamala, 61, in Sonari (pilot). The assistant asks the scheme questions, fills the draft, sends her to the portal review page for the document and declaration, then submits and reads back the receipt.

61, widow, lives in Sonari (pilot block). The happy-path applicant.

What this guards: Guards the end-to-end citizen path: shared validation, revision-bound confirmation, single submission and receipt.
1. What does the pension scheme cover, and can I apply?
tool: get_service_info → citizen endpoint · token session · result {'ok': True} · ok
checkdetail
ok ok=True (expected True)
substring 'minimum_age' excerpt: {"scheme": "Old Age Pension, Government of Purvanchal (Department of Social Welfare)", "pension_rupees_per_month": 250, "minimum_age": 60, "age_measured_on": "submission date (Asia/Kolkata), from the full date of birth", "statutory_sla_days": 15, "deadline": "2026-10-31 23:59 IST", "window_open": true, "window_note": "Submissions are open while server time is before the deadline.", "server_time_is
substring 'deadline' excerpt: {"scheme": "Old Age Pension, Government of Purvanchal (Department of Social Welfare)", "pension_rupees_per_month": 250, "minimum_age": 60, "age_measured_on": "submission date (Asia/Kolkata), from the full date of birth", "statutory_sla_days": 15, "deadline": "2026-10-31 23:59 IST", "window_open": true, "window_note": "Submissions are open while server time is before the deadline.", "server_time_is
substring 'pilot_blocks' excerpt: {"scheme": "Old Age Pension, Government of Purvanchal (Department of Social Welfare)", "pension_rupees_per_month": 250, "minimum_age": 60, "age_measured_on": "submission date (Asia/Kolkata), from the full date of birth", "statutory_sla_days": 15, "deadline": "2026-10-31 23:59 IST", "window_open": true, "window_note": "Submissions are open while server time is before the deadline.", "server_time_is
2. I am Kamala Devi, born 4 Feb 1965, living in Kathoni, Sonari block. My bank details are ...
tool: save_application_draft → citizen endpoint · token session · args {'applicant_name': 'Kamala Devi', 'dob': '04/02/1965', 'gender': 'Female', 'marital_status': 'Widowed', 'husband_name': 'Dhaneswar Devi', 'village': 'Kathoni', 'block': 'Sonari', 'bank_account': '90000001012345', 'ifsc': 'SBIN0003077'} · result {'ok': True, 'draft_id': 'drf_ioJ8mEEPFt_-zC-cnsz-uheB', 'revision': 1} · ok
checkdetail
ok ok=True (expected True)
field missing_fields missing_fields = [] (expected [])
3. What is still missing before I can submit?
tool: get_application_draft → citizen endpoint · token session · result {'ok': True, 'draft_id': 'drf_ioJ8mEEPFt_-zC-cnsz-uheB', 'revision': 1} · ok
checkdetail
ok ok=True (expected True)
field document.document_id document.document_id = None (expected None)
substring 'document_requirement' excerpt: {"ok": true, "draft_id": "drf_ioJ8mEEPFt_-zC-cnsz-uheB", "revision": 1, "fields": {"applicant_name": "Kamala Devi", "dob": "04/02/1965", "gender": "Female", "marital_status": "Widowed", "husband_name": "Dhaneswar Devi", "village": "Kathoni", "block": "Sonari", "bank_account": "90000001012345", "ifsc": "SBIN0003077"}, "missing_fields": [], "field_errors": [], "eligibility": {"eligible": true, "age_
4. Kamala opens the review page and uploads her Voter ID.
portal: upload_document · uploaded age_proof.jpg as doc_PPWbLReuvjDUjm3R7GpDLNCQ; draft is now revision 2
5. She checks the exact revision, ticks the declaration and confirms it on the portal.
portal: confirm_draft · declaration accepted; revision 2 confirmed (change cfm_-dEYf9DUlYlh_V73OGklfY5D)
6. I have confirmed on the portal; submit it please.
tool: submit_application → citizen endpoint · token session · args {'draft_id': 'drf_ioJ8mEEPFt_-zC-cnsz-uheB', 'revision': 2, 'idempotency_key': 'eval-kamala-submit-1'} · result {'ok': True, 'status': 'PENDING', 'application_no': 'SSP26170043', 'confirmation_id': 'cfm_-dEYf9DUlYlh_V73OGklfY5D'} · ok
checkdetail
ok ok=True (expected True)
field status status = 'PENDING' (expected 'PENDING')
substring 'acknowledgment_pdf_url' excerpt: {"ok": true, "application_no": "SSP26170043", "status": "PENDING", "status_label": "Pending departmental decision", "submitted_at": "2026-10-18 12:38 IST", "due_at": "2026-11-02 12:38 IST", "block": "Sonari", "document_recorded": true, "next_action": "share_receipt", "portal_status_url": "https://sewasetuai.duckdns.org/status", "acknowledgment_pdf_url": "https://sewasetuai.duckdns.org/ack/100011.p
substring 'due_at' excerpt: {"ok": true, "application_no": "SSP26170043", "status": "PENDING", "status_label": "Pending departmental decision", "submitted_at": "2026-10-18 12:38 IST", "due_at": "2026-11-02 12:38 IST", "block": "Sonari", "document_recorded": true, "next_action": "share_receipt", "portal_status_url": "https://sewasetuai.duckdns.org/status", "acknowledgment_pdf_url": "https://sewasetuai.duckdns.org/ack/100011.p
7. Can you confirm it is recorded with the department?
tool: get_my_applications → citizen endpoint · token session · result {'ok': True} · ok
checkdetail
ok ok=True (expected True)
substring 'Kamala Devi' excerpt: {"ok": true, "applications": [{"application_no": "SSP26170043", "applicant_name": "Kamala Devi", "mobile": "9000000101", "village": "Kathoni", "block": "Sonari", "status": "PENDING", "status_label": "Pending departmental decision", "submitted_at": "2026-10-18 12:38 IST", "due_at": "2026-11-02 12:38 IST", "decided_at": null, "decided_by": null, "version": 1, "note": "A block officer reviews this ap
substring 'PENDING' excerpt: {"ok": true, "applications": [{"application_no": "SSP26170043", "applicant_name": "Kamala Devi", "mobile": "9000000101", "village": "Kathoni", "block": "Sonari", "status": "PENDING", "status_label": "Pending departmental decision", "submitted_at": "2026-10-18 12:38 IST", "due_at": "2026-11-02 12:38 IST", "decided_at": null, "decided_by": null, "version": 1, "note": "A block officer reviews this ap
8. Database: one pending application, submit audited on the MCP channel, confirmation consumed.
db: application_status · args {'mobile': '9000000101', 'status': 'PENDING'}
checkdetail
database application latest for 9000000101 is 'PENDING' (expected 'PENDING')
9.
db: active_count · args {'mobile': '9000000101', 'count': 1}
checkdetail
database 1 active application(s) (expected 1)
10.
db: audit_has · args {'application_no': 'SSP26170043', 'action': 'SUBMIT', 'channel': 'mcp_citizen'}
checkdetail
database 1 audit row(s) action=SUBMIT channel=mcp_citizen for SSP26170043 (expected at least 1)
11.
db: confirmation_consumed · args {'confirmation_id': 'cfm_-dEYf9DUlYlh_V73OGklfY5D'}
checkdetail
database confirmation consumed=True (expected True)
12.
db: outbox_has · args {'mobile': '9000000101', 'body_like': 'application'}
checkdetail
database 1 queued/delivered notification(s) matching 'application' for 9000000101

Rahima, 58, is refused with the exact date she turns 60

passed   citizen · persona: rahima · 5 step(s) · 0.1s

Rahima is 58. The assistant records her details but submission must fail with an UNDER_MIN_AGE error and her eligible-from date; no application is created.

58, not yet eligible; her birthday shows exactly how long she must wait.

What this guards: Age is checked from the full date of birth on one shared implementation; an under-age attempt can never create an application.
1. I was born on 15 June 1968, can I apply now?
tool: save_application_draft → citizen endpoint · token session · args {'applicant_name': 'Rahima Begum', 'dob': '15/06/1968', 'gender': 'Female', 'marital_status': 'Widowed', 'husband_name': 'Rafiqul Begum', 'village': 'Dighaljan', 'block': 'Rajapara', 'bank_account': '90000001022345', 'ifsc': 'UTBI0PUR2819'} · result {'ok': True, 'draft_id': 'drf_LuYCM5NdsiTfEDFckyQkluHE', 'revision': 1} · ok
checkdetail
ok ok=True (expected True)
substring 'eligible_from' excerpt: {"ok": true, "draft_id": "drf_LuYCM5NdsiTfEDFckyQkluHE", "revision": 1, "fields": {"applicant_name": "Rahima Begum", "dob": "15/06/1968", "gender": "Female", "marital_status": "Widowed", "husband_name": "Rafiqul Begum", "village": "Dighaljan", "block": "Rajapara", "bank_account": "90000001022345", "ifsc": "UTBI0PUR2819"}, "missing_fields": [], "field_errors": [{"field": "ifsc", "code": "INVALID_FO
substring '2028-06-15' excerpt: {"ok": true, "draft_id": "drf_LuYCM5NdsiTfEDFckyQkluHE", "revision": 1, "fields": {"applicant_name": "Rahima Begum", "dob": "15/06/1968", "gender": "Female", "marital_status": "Widowed", "husband_name": "Rafiqul Begum", "village": "Dighaljan", "block": "Rajapara", "bank_account": "90000001022345", "ifsc": "UTBI0PUR2819"}, "missing_fields": [], "field_errors": [{"field": "ifsc", "code": "INVALID_FO
2. The portal review page still lets her upload her document, but no confirmation can be made.
portal: upload_document · uploaded age_proof.jpg as doc_eH86mMOWAQgiHqFWrHXDHGhX; draft is now revision 2
3. Submit it anyway.
tool: submit_application → citizen endpoint · token session · args {'draft_id': 'drf_LuYCM5NdsiTfEDFckyQkluHE', 'revision': 2, 'idempotency_key': 'eval-rahima-submit-1'} · result {'ok': False, 'code': 'CONFIRMATION_REQUIRED', 'message': 'This revision has not been confirmed on the portal review page. Open https://sewasetuai.duckdns.org/review/drf_LuYCM5NdsiTfEDFckyQkluHE, check the details, accept the declaration, then submit.'} · ok
checkdetail
ok ok=False (expected False)
code code='CONFIRMATION_REQUIRED' (expected 'CONFIRMATION_REQUIRED')
4. Why can I not submit?
tool: get_application_draft → citizen endpoint · token session · result {'ok': True, 'draft_id': 'drf_LuYCM5NdsiTfEDFckyQkluHE', 'revision': 2} · ok
checkdetail
ok ok=True (expected True)
field ready_to_submit ready_to_submit = False (expected False)
error code UNDER_MIN_AGE field error codes = ['INVALID_FORMAT', 'UNDER_MIN_AGE'] (expected 'UNDER_MIN_AGE' to be present)
substring '2028-06-15' excerpt: {"ok": true, "draft_id": "drf_LuYCM5NdsiTfEDFckyQkluHE", "revision": 2, "fields": {"applicant_name": "Rahima Begum", "dob": "15/06/1968", "gender": "Female", "marital_status": "Widowed", "husband_name": "Rafiqul Begum", "village": "Dighaljan", "block": "Rajapara", "bank_account": "90000001022345", "ifsc": "UTBI0PUR2819"}, "missing_fields": [], "field_errors": [{"field": "ifsc", "code": "INVALID_FO
5. Database: nothing was created.
db: no_applications · args {'mobile': '9000000102'}
checkdetail
database 0 application(s) exist for 9000000102 (expected none)

Haren lives in Borgaon: honest refusal, portal handoff

passed   citizen · persona: haren · 2 step(s) · 0.1s

The assisted channels serve Sonari and Rajapara only. Haren's block is refused with a plain-language note that the official portal is open to everyone.

72, lives in Borgaon, which is outside the assisted-application pilot.

What this guards: Pilot scope is a channel fact, not an eligibility decision, and the refusal explains the handoff.
1. I live in Borgaon Centre; help me apply.
tool: save_application_draft → citizen endpoint · token session · args {'applicant_name': 'Haren Bora', 'dob': '12/10/1954', 'gender': 'Male', 'marital_status': 'Married', 'village': 'Borgaon Centre', 'block': 'Borgaon', 'bank_account': '90000001032345', 'ifsc': 'CNRB0004417'} · result {'ok': False, 'code': 'OUTSIDE_PILOT', 'message': 'Block Borgaon is outside the assisted-application pilot. Assisted applications through connected applications are available in: Sonari, Rajapara. Outside those blocks the official portal at https://sewasetuai.duckdns.org/apply remains open. This is a channel rollout, not an eligibility decision.'} · ok
checkdetail
ok ok=False (expected False)
code code='OUTSIDE_PILOT' (expected 'OUTSIDE_PILOT')
substring 'Borgaon' excerpt: {"ok": false, "code": "OUTSIDE_PILOT", "message": "Block Borgaon is outside the assisted-application pilot. Assisted applications through connected applications are available in: Sonari, Rajapara. Outside those blocks the official portal at https://sewasetuai.duckdns.org/apply remains open. This is a channel rollout, not an eligibility decision.", "errors": [], "missing_fields": [], "next_action":
substring 'portal' excerpt: {"ok": false, "code": "OUTSIDE_PILOT", "message": "Block Borgaon is outside the assisted-application pilot. Assisted applications through connected applications are available in: Sonari, Rajapara. Outside those blocks the official portal at https://sewasetuai.duckdns.org/apply remains open. This is a channel rollout, not an eligibility decision.", "errors": [], "missing_fields": [], "next_action":
substring 'not an eligibility' excerpt: {"ok": false, "code": "OUTSIDE_PILOT", "message": "Block Borgaon is outside the assisted-application pilot. Assisted applications through connected applications are available in: Sonari, Rajapara. Outside those blocks the official portal at https://sewasetuai.duckdns.org/apply remains open. This is a channel rollout, not an eligibility decision.", "errors": [], "missing_fields": [], "next_action":
2.
db: no_applications · args {'mobile': '9000000103'}
checkdetail
database 0 application(s) exist for 9000000103 (expected none)

Prabhat already has a pending application

passed   citizen · persona: prabhat · 7 step(s) · 0.2s

One active application per mobile. Prabhat's second attempt through the assistant ends with ACTIVE_APPLICATION_EXISTS and a pointer to his existing application.

65, already holds a pending application; tries to open a second one.

What this guards: The mobile-level uniqueness guard holds even when a fresh confirmation is used.
1. Prabhat already submitted through the portal.
setup: create_pending_application · created PENDING application SSP26457967 for Prabhat Deka through the portal services (document attached, declaration confirmed)
2. I want to apply again.
tool: save_application_draft → citizen endpoint · token session · args {'applicant_name': 'Prabhat Deka', 'dob': '20/03/1961', 'gender': 'Male', 'marital_status': 'Married', 'village': 'Rajapara Gaon', 'block': 'Rajapara', 'bank_account': '90000001042345', 'ifsc': 'PUNB0556610'} · result {'ok': True, 'draft_id': 'drf_g7UpCFZC4GSExejdKvrZ5gLm', 'revision': 2} · ok
checkdetail
ok ok=True (expected True)
3. He uploads a fresh document and confirms the revision.
portal: upload_document · uploaded age_proof.jpg as doc_P-_jOVIAbeP7D0KPxjInG53P; draft is now revision 3
4.
portal: confirm_draft · declaration accepted; revision 3 confirmed (change cfm_YHwHojw79ocTVVrxH1WP9rWT)
5. Submit the new application.
tool: submit_application → citizen endpoint · token session · args {'draft_id': 'drf_g7UpCFZC4GSExejdKvrZ5gLm', 'revision': 3, 'idempotency_key': 'eval-prabhat-submit-2'} · result {'ok': False, 'code': 'ACTIVE_APPLICATION_EXISTS', 'message': 'The mobile number already holds application SSP26457967 (PENDING). One active application is allowed per verified mobile number. A PENDING application can be withdrawn in the portal, after which a fresh application may be submitted.', 'application_no': 'SSP26457967'} · ok
checkdetail
ok ok=False (expected False)
code code='ACTIVE_APPLICATION_EXISTS' (expected 'ACTIVE_APPLICATION_EXISTS')
substring 'SSP26457967' excerpt: {"ok": false, "code": "ACTIVE_APPLICATION_EXISTS", "message": "The mobile number already holds application SSP26457967 (PENDING). One active application is allowed per verified mobile number. A PENDING application can be withdrawn in the portal, after which a fresh application may be submitted.", "errors": [], "missing_fields": [], "next_action": "get_my_applications", "retryable": false, "request
6. Database: still exactly one active application.
db: active_count · args {'mobile': '9000000104', 'count': 1}
checkdetail
database 1 active application(s) (expected 1)
7.
db: total_count · args {'mobile': '9000000104', 'count': 1}
checkdetail
database 1 application(s) total (expected 1)

Malati withdraws a pending application and applies afresh

passed   citizen · persona: malati · 12 step(s) · 0.3s

Withdrawal is a confirmed action; after it, a fresh application is allowed and a new processing period starts.

68, pending application she wants to withdraw, then applies afresh.

What this guards: Withdrawal is single-use and confirmation-bound; the guard correctly permits exactly one fresh application afterwards.
1. Malati has a pending application with an error in it.
setup: create_pending_application · created PENDING application SSP26752126 for Malati Sarma through the portal services (document attached, declaration confirmed)
2. I entered the wrong bank account. Withdraw my application.
tool: prepare_withdrawal → citizen endpoint · token session · args {'application_no': 'SSP26752126'} · result {'ok': True, 'application_no': 'SSP26752126', 'confirmation_id': 'cfm_94HPPNsNKvgU5ztjx8fW9ytY', 'version': 1} · ok
checkdetail
ok ok=True (expected True)
substring 'confirmation_url' excerpt: {"ok": true, "application_no": "SSP26752126", "version": 1, "confirmation_id": "cfm_94HPPNsNKvgU5ztjx8fW9ytY", "confirmation_url": "https://sewasetuai.duckdns.org/confirm/cfm_94HPPNsNKvgU5ztjx8fW9ytY", "effect": "Withdrawing cancels application SSP26752126. A fresh application may then be submitted; processing starts again from the new submission date.", "next_action": "confirm_withdrawal_on_porta
substring 'effect' excerpt: {"ok": true, "application_no": "SSP26752126", "version": 1, "confirmation_id": "cfm_94HPPNsNKvgU5ztjx8fW9ytY", "confirmation_url": "https://sewasetuai.duckdns.org/confirm/cfm_94HPPNsNKvgU5ztjx8fW9ytY", "effect": "Withdrawing cancels application SSP26752126. A fresh application may then be submitted; processing starts again from the new submission date.", "next_action": "confirm_withdrawal_on_porta
3. She opens the confirmation link and confirms the withdrawal.
portal: confirm_withdrawal · withdrawal confirmed by the applicant on the confirmation page
4. Now finish the withdrawal please.
tool: withdraw_application → citizen endpoint · token session · args {'confirmation_id': 'cfm_94HPPNsNKvgU5ztjx8fW9ytY', 'expected_version': 1, 'idempotency_key': 'eval-malati-withdraw-1'} · result {'ok': True, 'status': 'WITHDRAWN', 'application_no': 'SSP26752126', 'confirmation_id': 'cfm_94HPPNsNKvgU5ztjx8fW9ytY', 'version': 2} · ok
checkdetail
ok ok=True (expected True)
field status status = 'WITHDRAWN' (expected 'WITHDRAWN')
5.
db: application_status · args {'mobile': '9000000105', 'application_no': 'SSP26752126', 'status': 'WITHDRAWN'}
checkdetail
database application SSP26752126 for 9000000105 is 'WITHDRAWN' (expected 'WITHDRAWN')
6.
db: audit_has · args {'application_no': 'SSP26752126', 'action': 'WITHDRAW', 'channel': 'mcp_citizen'}
checkdetail
database 1 audit row(s) action=WITHDRAW channel=mcp_citizen for SSP26752126 (expected at least 1)
7. Correct the bank account and apply again.
tool: save_application_draft → citizen endpoint · token session · args {'applicant_name': 'Malati Sarma', 'dob': '07/08/1958', 'gender': 'Female', 'marital_status': 'Widowed', 'husband_name': 'Nabin Sarma', 'village': 'Lakhipur', 'block': 'Sonari', 'bank_account': '90000001059999', 'ifsc': 'UCBA0001522'} · result {'ok': True, 'draft_id': 'drf_gaQFg_cFumP7oZLg8K9mf9UW', 'revision': 3} · ok
checkdetail
ok ok=True (expected True)
8.
portal: upload_document · uploaded age_proof.jpg as doc_eQS8Z1HtnjCiu4H2mG7A1NX4; draft is now revision 4
9.
portal: confirm_draft · declaration accepted; revision 4 confirmed (change cfm_fCULwe9a_vYJEVsIlIi1T5GJ)
10. Submit the corrected application.
tool: submit_application → citizen endpoint · token session · args {'draft_id': 'drf_gaQFg_cFumP7oZLg8K9mf9UW', 'revision': 4, 'idempotency_key': 'eval-malati-resubmit-1'} · result {'ok': True, 'status': 'PENDING', 'application_no': 'SSP26375668', 'confirmation_id': 'cfm_fCULwe9a_vYJEVsIlIi1T5GJ'} · ok
checkdetail
ok ok=True (expected True)
field status status = 'PENDING' (expected 'PENDING')
11. Database: two applications, exactly one active.
db: total_count · args {'mobile': '9000000105', 'count': 2}
checkdetail
database 2 application(s) total (expected 2)
12.
db: active_count · args {'mobile': '9000000105', 'count': 1}
checkdetail
database 1 active application(s) (expected 1)

Nirmal's assistant retries a submit that timed out

passed   citizen · persona: nirmal · 8 step(s) · 0.2s

The same idempotency key must return the same receipt without a second application; the same key with a different request must be refused as a conflict.

60, exactly on the age boundary; his assistant retries a submit that timed out.

What this guards: Retries can never create a second application; a changed request under an old key is a conflict, not a second submission.
1. I am Nirmal Das, born 30 September 1966, Bhelukajan, Sonari. That's all my details.
tool: save_application_draft → citizen endpoint · token session · args {'applicant_name': 'Nirmal Das', 'dob': '30/09/1966', 'gender': 'Male', 'marital_status': 'Unmarried', 'village': 'Bhelukajan', 'block': 'Sonari', 'bank_account': '90000001072345', 'ifsc': 'SBIN0003077'} · result {'ok': True, 'draft_id': 'drf_yiL45mm2HphEYAugtAkYTb9u', 'revision': 1} · ok
checkdetail
ok ok=True (expected True)
2.
portal: upload_document · uploaded age_proof.jpg as doc_OinnIslH0FJXXuBVZAT8qnPy; draft is now revision 2
3.
portal: confirm_draft · declaration accepted; revision 2 confirmed (change cfm_TLzOoL-PvoyAPksYFMmeQG0q)
4. Submit.
tool: submit_application → citizen endpoint · token session · args {'draft_id': 'drf_yiL45mm2HphEYAugtAkYTb9u', 'revision': 2, 'idempotency_key': 'eval-nirmal-submit-1'} · result {'ok': True, 'status': 'PENDING', 'application_no': 'SSP26603715', 'confirmation_id': 'cfm_TLzOoL-PvoyAPksYFMmeQG0q'} · ok
checkdetail
ok ok=True (expected True)
5. I did not get a response, send it again.
tool: submit_application → citizen endpoint · token session · args {'draft_id': 'drf_yiL45mm2HphEYAugtAkYTb9u', 'revision': 2, 'idempotency_key': 'eval-nirmal-submit-1'} · result {'ok': True, 'status': 'PENDING', 'application_no': 'SSP26603715', 'confirmation_id': 'cfm_TLzOoL-PvoyAPksYFMmeQG0q'} · ok
checkdetail
ok ok=True (expected True)
field application_no application_no = 'SSP26603715' (expected 'SSP26603715')
6. A different version under the same key?
tool: submit_application → citizen endpoint · token session · args {'draft_id': 'drf_yiL45mm2HphEYAugtAkYTb9u', 'revision': 3, 'idempotency_key': 'eval-nirmal-submit-1'} · result {'ok': False, 'code': 'IDEMPOTENCY_CONFLICT', 'message': 'This idempotency key was already used for a different request. Use a new key for a new request.'} · ok
checkdetail
ok ok=False (expected False)
code code='IDEMPOTENCY_CONFLICT' (expected 'IDEMPOTENCY_CONFLICT')
7. Database: exactly one application; the key is completed.
db: total_count · args {'mobile': '9000000107', 'count': 1}
checkdetail
database 1 application(s) total (expected 1)
8.
db: idempotency_completed · args {'principal': 'citizen:9000000107', 'operation': 'submit_application', 'key': 'eval-nirmal-submit-1'}
checkdetail
database idempotency record status='completed' (expected 'completed')

Rahima cannot see or touch Kamala's application

passed   citizen · persona: rahima · 4 step(s) · 0.1s

Identity comes from the verified token, never from tool arguments. Another citizen's application number returns the same not-found answer as a typo.

58, not yet eligible; her birthday shows exactly how long she must wait.

What this guards: Cross-citizen access returns not-found for both lookups and mutations; errors never confirm that someone else's record exists.
1.
setup: create_pending_application · created PENDING application SSP26494566 for Kamala Devi through the portal services (document attached, declaration confirmed)
2. Look up application $app_no for me.
tool: get_my_applications → citizen endpoint · token session · args {'application_no': 'SSP26494566'} · result {'ok': False, 'code': 'NOT_FOUND_OR_NOT_PERMITTED', 'message': 'No application SSP26494566 for this sign-in.'} · ok
checkdetail
ok ok=False (expected False)
code code='NOT_FOUND_OR_NOT_PERMITTED' (expected 'NOT_FOUND_OR_NOT_PERMITTED')
3. Withdraw it then.
tool: prepare_withdrawal → citizen endpoint · token session · args {'application_no': 'SSP26494566'} · result {'ok': False, 'code': 'NOT_FOUND_OR_NOT_PERMITTED', 'message': 'No application SSP26494566 for this sign-in.'} · ok
checkdetail
ok ok=False (expected False)
code code='NOT_FOUND_OR_NOT_PERMITTED' (expected 'NOT_FOUND_OR_NOT_PERMITTED')
4. Database: Kamala's application is untouched.
db: application_status · args {'mobile': '9000000101', 'application_no': 'SSP26494566', 'status': 'PENDING'}
checkdetail
database application SSP26494566 for 9000000101 is 'PENDING' (expected 'PENDING')

Anima tries after the scheme window has closed

skipped   citizen · persona: anima · 0 step(s)

Run only with the stack's clock frozen past 2026-10-31 23:59 (SEWASETU_NOW). The assistant must refuse submission with the exact deadline, and the portal stays available for status.

63; her scenario runs only when the stack's clock is frozen past the deadline.

What this guards: One window predicate for every channel; the boundary is 23:59:00 IST on the deadline day.
needs a frozen clock: set SEWASETU_NOW (for example '2026-11-01 09:00') on every service and rerun

Wrong audience, wrong role and revoked sign-ins are refused

passed   mixed · persona: kamala · 6 step(s) · 0.2s

Before any tool runs, the endpoints check the token: exact audience, the endpoint's role, and a live unrevoked grant. A revoked sign-in stops working immediately.

61, widow, lives in Sonari (pilot block). The happy-path applicant.

What this guards: Audience-bound tokens with live grant checks: revocation is immediate and a token minted for one service can never act at another.
1. A correctly scoped citizen sign-in connects.
transport: → citizen endpoint (HTTP 200) · token {'subject': 'citizen:9000000101', 'kind': 'session', 'revoked': False, 'audience_override': False, 'grant_id': 'grt_037J7LdG_iD0VDvCe-Sd1pR5tHJjH_u7'} · HTTP 200 as expected
checkdetail
http status HTTP 200 (expected 200)
2. The same sign-in pointed at the officer endpoint is refused (role).
transport: → officer endpoint (HTTP 401) · token {'subject': 'citizen:9000000101', 'kind': 'session', 'revoked': False, 'audience_override': False, 'grant_id': 'grt_Qm11wBhSCdyUpMBDL9jGUj7HLiZxiiON'} · HTTP 401 as expected
checkdetail
http status HTTP 401 (expected 401)
header resource_metadata WWW-Authenticate: 'Bearer error="invalid_token", error_description="Authentication required", resource_metadata="https://sewasetuai.duckdns.org/.well-known/oauth-protected-resource/mcp/officer"'
3. A citizen token minted for another service's audience is refused (audience).
transport: → citizen endpoint (HTTP 401) · token {'subject': 'citizen:9000000101', 'kind': 'session', 'revoked': False, 'audience_override': True, 'grant_id': 'grt_8m-X2UfBvy5y8VGZtLZKtGUdXArJNXXw'} · HTTP 401 as expected
checkdetail
http status HTTP 401 (expected 401)
4. An officer token at the citizen endpoint is refused.
transport: → citizen endpoint (HTTP 401) · token {'subject': 'officer:meera.saikia', 'kind': 'session', 'revoked': False, 'audience_override': False, 'grant_id': 'grt_jvHOZbWrcRUezarIlroKEzUf8JrtzmCp'} · HTTP 401 as expected
checkdetail
http status HTTP 401 (expected 401)
5. A sign-in that has just been revoked is refused at once, not at token expiry.
transport: → citizen endpoint (HTTP 401) · token {'subject': 'citizen:9000000101', 'kind': 'session', 'revoked': True, 'audience_override': False, 'grant_id': 'grt_2lOyRBybrZ1Q-kXHmR8aWLJfWw3lPmvZ'} · HTTP 401 as expected
checkdetail
http status HTTP 401 (expected 401)
6.
db: grant_revoked · args {'grant_id': 'grt_2lOyRBybrZ1Q-kXHmR8aWLJfWw3lPmvZ'}
checkdetail
database grant grt_2lOyRBybrZ1Q-kXHmR8aWLJfWw3lPmvZ revoked_at=2026-10-18 12:38:32.278651

Refresh rotates within the same sign-in; revoke ends it

passed   citizen · persona: kamala · 5 step(s) · 0.2s

Through the real token endpoint: a refresh returns a new access token bound to the same grant with the same absolute end; revoking the refresh token kills the access token immediately.

61, widow, lives in Sonari (pilot block). The happy-path applicant.

What this guards: Rotation keeps one grant and one session end; revocation ends the whole sign-in now, not at the next expiry.
1. Refresh the sign-in through /oauth/token, then revoke it through /oauth/revoke.
custom: mint sign-in · minted a live sign-in (grant grt_FUZoK-GTU6PXmnb_PrX4EWVY9dw4xW08) with a refresh token, as the portal browser would after consent
1.
custom: refresh at /oauth/token · rotated within the same grant grt_FUZoK-GTU6PXmnb_PrX4EWVY9dw4xW08; session end unchanged
checkdetail
token endpoint status HTTP 200 (expected 200): {"access_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc0Y2NhYzlkYzhlMjAzZmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3Nld2FzZXR1YWkuZHVja2Rucy5vcmciLCJzdWIiOiJjaXRpemVuOjkwMDAwMDAxMDEiLCJhdWQiOiJodHRwczovL3Nl
same grant grant_id='grt_FUZoK-GTU6PXmnb_PrX4EWVY9dw4xW08' (expected 'grt_FUZoK-GTU6PXmnb_PrX4EWVY9dw4xW08'); rotation must not start a new sign-in
same session end session_ends='2026-10-18T14:38:32' (expected '2026-10-18T14:38:32'); the absolute session never moves
rotated refresh token a new refresh token was issued
1.
custom: rotated token used over MCP · the rotated access token authenticated a real tool call
checkdetail
rotated token works get_service_info ok=True
1.
custom: revoke at /oauth/revoke · the sign-in was ended (grant grt_FUZoK-GTU6PXmnb_PrX4EWVY9dw4xW08 revoked)
checkdetail
revocation status HTTP 200 (expected 200)
1.
custom: revoked token used over MCP · the revoked sign-in is refused at the door
checkdetail
revoked access token refused HTTP 401 (expected 401); revocation must bite immediately, not at token expiry
grant revoked in the database grant grt_FUZoK-GTU6PXmnb_PrX4EWVY9dw4xW08 revoked_at=2026-10-18 12:38:32.544396

Meera works the Sonari queue and opens one case

passed   officer · persona: meera · 7 step(s) · 0.4s

The queue is nearest-due-first and scoped to her blocks; the case view shows facts, evidence and audit. A historical record with no stored document says so plainly.

Block officer for Sonari. Reviews the queue nearest-due first and decides on recorded facts.

What this guards: Queue scope and the evidence-availability honesty: historical rows are never shown with invented proof.
1. A historical Sonari record exists with no stored document.
setup: create_historical_application · inserted historical row SSPEVAL87B975 directly in SQL (Sonari block, doc_path NULL); it carries no stored document because historical records may predate document storage
2. An application from another block is in the records.
setup: create_baseline_application · submitted a normal, evidence-complete application SSP26379123 in another block (Rajapara) through the portal services
3. What is waiting in my blocks?
tool: get_officer_context → officer endpoint · token session · result {'ok': True} · ok
checkdetail
ok ok=True (expected True)
field officer officer = 'meera.saikia' (expected 'meera.saikia')
substring 'permitted_blocks' excerpt: {"ok": true, "officer": "meera.saikia", "permitted_blocks": ["Sonari"], "pilot_blocks": ["Sonari", "Rajapara"], "queue": {"pending": 54, "past_due": 53}, "tools": ["list_pending_applications: queue nearest due first", "get_application_case: full facts, evidence availability and audit for one case", "prepare_decision: approve/reject with a reason; returns a portal confirmation link", "record_decisi
substring 'audit' excerpt: {"ok": true, "officer": "meera.saikia", "permitted_blocks": ["Sonari"], "pilot_blocks": ["Sonari", "Rajapara"], "queue": {"pending": 54, "past_due": 53}, "tools": ["list_pending_applications: queue nearest due first", "get_application_case: full facts, evidence availability and audit for one case", "prepare_decision: approve/reject with a reason; returns a portal confirmation link", "record_decisi
4. Show me the queue.
tool: list_pending_applications → officer endpoint · token session · args {'limit': 5} · result {'ok': True, 'count': 5} · ok
checkdetail
ok ok=True (expected True)
field scope.permitted_blocks scope.permitted_blocks = ['Sonari'] (expected ['Sonari'])
scope every cases entry has block == 'Sonari' (5 rows checked)
5. Open that historical case.
tool: get_application_case → officer endpoint · token session · args {'application_no': 'SSPEVAL87B975'} · result {'ok': True} · ok
checkdetail
ok ok=True (expected True)
field evidence.available evidence.available = False (expected False)
substring 'Historical records may predate document storage' excerpt: {"ok": true, "case": {"application_no": "SSPEVAL87B975", "applicant_name": "Historic Verification Case", "mobile_masked": "90****90", "dob": "05/05/1950", "age": 76, "village": "Purani Bazar", "block": "Sonari", "bank_account_masked": "****9012", "ifsc": "SBIN0003077", "status": "PENDING", "status_label": "Pending departmental decision", "submitted_at": "2026-10-18 12:38 IST", "due_at": "2026-11-0
6. Open a case outside my blocks.
tool: get_application_case → officer endpoint · token session · args {'application_no': 'SSP26379123'} · result {'ok': False, 'code': 'NOT_FOUND_OR_NOT_PERMITTED', 'message': 'No case SSP26379123 in your permitted blocks.'} · ok
checkdetail
ok ok=False (expected False)
code code='NOT_FOUND_OR_NOT_PERMITTED' (expected 'NOT_FOUND_OR_NOT_PERMITTED')
7. Database: the historical row really has no stored evidence.
db: evidence_unavailable · args {'application_no': 'SSPEVAL87B975'}
checkdetail
database doc_path=None stored_document_rows=0 (evidence unavailable expected)

Meera approves a case with a recorded reason

passed   officer · persona: meera · 9 step(s) · 0.3s

Prepare on the workspace, confirm the exact case/action/reason on the portal, record once. The applicant is notified and the audit names the officer and reason.

Block officer for Sonari. Reviews the queue nearest-due first and decides on recorded facts.

What this guards: Evidence-based decisions: one prepare/confirm/record path, reason mandatory, idempotent record, applicant notified.
1.
setup: create_pending_application · created PENDING application SSP26018461 for Kamala Devi through the portal services (document attached, declaration confirmed)
2. Show me the case.
tool: get_application_case → officer endpoint · token session · args {'application_no': 'SSP26018461'} · result {'ok': True} · ok
checkdetail
ok ok=True (expected True)
field evidence.available evidence.available = True (expected True)
substring 'due_at' excerpt: {"ok": true, "case": {"application_no": "SSP26018461", "applicant_name": "Kamala Devi", "mobile_masked": "90****01", "dob": "04/02/1965", "age": 61, "village": "Kathoni", "block": "Sonari", "bank_account_masked": "****2345", "ifsc": "SBIN0003077", "status": "PENDING", "status_label": "Pending departmental decision", "submitted_at": "2026-10-18 12:38 IST", "due_at": "2026-11-02 12:38 IST", "decided
substring 'version' excerpt: {"ok": true, "case": {"application_no": "SSP26018461", "applicant_name": "Kamala Devi", "mobile_masked": "90****01", "dob": "04/02/1965", "age": 61, "village": "Kathoni", "block": "Sonari", "bank_account_masked": "****2345", "ifsc": "SBIN0003077", "status": "PENDING", "status_label": "Pending departmental decision", "submitted_at": "2026-10-18 12:38 IST", "due_at": "2026-11-02 12:38 IST", "decided
3. The age proof is genuine. Approve it, reason: age proof verified at the block office.
tool: prepare_decision → officer endpoint · token session · args {'application_no': 'SSP26018461', 'expected_version': 1, 'decision': 'APPROVED', 'reason': 'Age proof verified at the block office'} · result {'ok': True, 'application_no': 'SSP26018461', 'confirmation_id': 'cfm_CXSi0miybw3UASCyq1lMqPmJ'} · ok
checkdetail
ok ok=True (expected True)
substring 'confirmation_url' excerpt: {"ok": true, "application_no": "SSP26018461", "decision": "APPROVED", "reason": "Age proof verified at the block office", "expected_version": 1, "confirmation_id": "cfm_CXSi0miybw3UASCyq1lMqPmJ", "confirmation_url": "https://sewasetuai.duckdns.org/confirm/cfm_CXSi0miybw3UASCyq1lMqPmJ", "next_action": "confirm_on_portal_then_record_decision", "note": "The officer must open the confirmation page, ch
4. Meera opens the confirmation page and confirms.
portal: confirm_decision · officer confirmed APPROVED / Age proof verified at the block office for application SSP26018461 on the portal confirmation page (cfm_CXSi0miybw3UASCyq1lMqPmJ)
5. Record it.
tool: record_decision → officer endpoint · token session · args {'confirmation_id': 'cfm_CXSi0miybw3UASCyq1lMqPmJ', 'expected_version': 1, 'idempotency_key': 'eval-meera-approve-1'} · result {'ok': True, 'status': 'APPROVED', 'application_no': 'SSP26018461', 'confirmation_id': 'cfm_CXSi0miybw3UASCyq1lMqPmJ', 'version': 2} · ok
checkdetail
ok ok=True (expected True)
field status status = 'APPROVED' (expected 'APPROVED')
field decided_by decided_by = 'meera.saikia' (expected 'meera.saikia')
6. Record the same confirmation again (a retry).
tool: record_decision → officer endpoint · token session · args {'confirmation_id': 'cfm_CXSi0miybw3UASCyq1lMqPmJ', 'expected_version': 1, 'idempotency_key': 'eval-meera-approve-1'} · result {'ok': True, 'status': 'APPROVED', 'application_no': 'SSP26018461', 'confirmation_id': 'cfm_CXSi0miybw3UASCyq1lMqPmJ', 'version': 2} · ok
checkdetail
ok ok=True (expected True)
field status status = 'APPROVED' (expected 'APPROVED')
7. Database: APPROVED by Meera with the reason in the audit.
db: decision_recorded · args {'application_no': 'SSP26018461', 'decision': 'APPROVED', 'reason_like': 'age proof verified', 'decided_by': 'meera.saikia'}
checkdetail
database SSP26018461 is APPROVED recorded by meera.saikia with reason matching 'age proof verified'
8.
db: audit_has · args {'application_no': 'SSP26018461', 'action': 'APPROVED', 'channel': 'mcp_officer'}
checkdetail
database 1 audit row(s) action=APPROVED channel=mcp_officer for SSP26018461 (expected at least 1)
9.
db: outbox_has · args {'mobile': '9000000101', 'body_like': 'approved'}
checkdetail
database 1 queued/delivered notification(s) matching 'approved' for 9000000101

A decision without a reason or on a stale case is refused

passed   officer · persona: meera · 7 step(s) · 0.2s

No reason means no confirmation. A case that changed after the decision was prepared cannot be recorded: the version guard wins.

Block officer for Sonari. Reviews the queue nearest-due first and decides on recorded facts.

What this guards: A reason is mandatory for every decision, and a prepared action can never be recorded against a case that has moved on.
1.
setup: create_pending_application · created PENDING application SSP26606411 for Kamala Devi through the portal services (document attached, declaration confirmed)
2. Reject it, no reason given.
tool: prepare_decision → officer endpoint · token session · args {'application_no': 'SSP26606411', 'expected_version': 1, 'decision': 'REJECTED', 'reason': ''} · result {'ok': False, 'code': 'VALIDATION_FAILED', 'message': 'A reason is required for every decision and becomes part of the audit. State the ground (for example: age proof verified).'} · ok
checkdetail
ok ok=False (expected False)
code code='VALIDATION_FAILED' (expected 'VALIDATION_FAILED')
substring 'reason' excerpt: {"ok": false, "code": "VALIDATION_FAILED", "message": "A reason is required for every decision and becomes part of the audit. State the ground (for example: age proof verified).", "errors": [{"field": "reason", "code": "REQUIRED", "message": "Give a short reason."}], "missing_fields": ["reason"], "next_action": "prepare_decision", "retryable": false, "request_id": "a89fad279458"}
3. Reject it: the document is for a different person.
tool: prepare_decision → officer endpoint · token session · args {'application_no': 'SSP26606411', 'expected_version': 1, 'decision': 'REJECTED', 'reason': "Document is not the applicant's age proof"} · result {'ok': True, 'application_no': 'SSP26606411', 'confirmation_id': 'cfm_6IPM_B3PzZzR7u4glDTsGL0h'} · ok
checkdetail
ok ok=True (expected True)
4. Meera confirms the exact rejection on the portal while the case is still pending.
portal: confirm_decision · officer confirmed REJECTED / Document is not the applicant's age proof for application SSP26606411 on the portal confirmation page (cfm_6IPM_B3PzZzR7u4glDTsGL0h)
5. Meanwhile the applicant withdraws, so the case moves on.
setup: withdraw_as_applicant · applicant withdrew SSP26606411 through the confirmation page (status is now WITHDRAWN)
6. Record the prepared rejection.
tool: record_decision → officer endpoint · token session · args {'confirmation_id': 'cfm_6IPM_B3PzZzR7u4glDTsGL0h', 'expected_version': 1, 'idempotency_key': 'eval-meera-stale-1'} · result {'ok': False, 'code': 'INVALID_STATE', 'message': 'Application SSP26606411 is WITHDRAWN and cannot be decided now. It was already decided or withdrawn.'} · ok
checkdetail
ok ok=False (expected False)
code code='INVALID_STATE' (expected 'INVALID_STATE')
7. Database: the withdrawal stands; no rejection exists.
db: application_status · args {'mobile': '9000000101', 'application_no': 'SSP26606411', 'status': 'WITHDRAWN'}
checkdetail
database application SSP26606411 for 9000000101 is 'WITHDRAWN' (expected 'WITHDRAWN')

A program token can read the queue but not decide

passed   officer · persona: meera · 6 step(s) · 0.3s

Unattended program tokens are for reading; a case decision requires an interactive named-officer sign-in.

Block officer for Sonari. Reviews the queue nearest-due first and decides on recorded facts.

What this guards: Program tokens are read-only by construction; decisions need a person.
1.
setup: create_pending_application · created PENDING application SSP26084719 for Kamala Devi through the portal services (document attached, declaration confirmed)
2. Weekly report: list the queue.
tool: list_pending_applications → officer endpoint · token program · args {'limit': 5} · result {'ok': True, 'count': 1} · ok
checkdetail
ok ok=True (expected True)
3. The job tries to prepare a decision.
tool: prepare_decision → officer endpoint · token program · args {'application_no': 'SSP26084719', 'expected_version': 1, 'decision': 'APPROVED', 'reason': 'bulk job'} · result {'ok': True, 'application_no': 'SSP26084719', 'confirmation_id': 'cfm_qSf_grqCH7DaM8ZvtXGY11eC'} · ok
checkdetail
ok ok=True (expected True)
4. Even if someone confirms on the portal…
portal: confirm_decision · officer confirmed APPROVED / bulk job for application SSP26084719 on the portal confirmation page (cfm_qSf_grqCH7DaM8ZvtXGY11eC)
5. …the program token cannot record it.
tool: record_decision → officer endpoint · token program · args {'confirmation_id': 'cfm_qSf_grqCH7DaM8ZvtXGY11eC', 'expected_version': 1, 'idempotency_key': 'eval-program-record-1'} · result {'ok': False, 'code': 'PROGRAM_TOKEN_NOT_ALLOWED', 'message': 'Case decisions require an interactive named-officer sign-in, not a program token.'} · ok
checkdetail
ok ok=False (expected False)
code code='PROGRAM_TOKEN_NOT_ALLOWED' (expected 'PROGRAM_TOKEN_NOT_ALLOWED')
6. Database: the case is still pending.
db: application_status · args {'mobile': '9000000101', 'application_no': 'SSP26084719', 'status': 'PENDING'}
checkdetail
database application SSP26084719 for 9000000101 is 'PENDING' (expected 'PENDING')