Inspiration: The Post-Code Problem in Regulated Healthcare Software
My dad takes blood pressure and diabetes medication every month. When I checked his pharmacy bills against India's NPPA (National Pharmaceutical Pricing Authority) DPCO 2013 legal ceiling prices, 6 out of 13 medicines were overcharged or had an 85% cheaper government Jan Aushadhi generic equivalent.
I built RxAudit (github.com/SOLOxLEVELING/RxAudit) as a 24-hour prototype where a vision model extracts receipt line items once, and a deterministic TypeScript engine verifies prices against 153 regulated formulations. However, in that original prototype, the 153-drug database was frozen in git, with no CI test runner, no container pipeline, and no automated way to ship NPPA Gazette revisions.
Why? Because in regulated medical software, handing post-code maintenance over to unguarded AI coding agents introduces two dangerous failure modes:
- Ambiguity Guessing: If a regulatory issue says "Update Telmisartan 40 ceiling to ₹62" without specifying
mgvsmcgor per-tablet vs per-strip, a standard LLM agent guesses the missing fields to close the issue. - Reward-Hacking Failing Pipelines: When asked to fix a failing CI build or add a drug update, an unguarded agent can make the pipeline green by weakening a Zod schema (
src/data/schemas.ts) or editing a failing test assertion (src/lib/audit-engine.test.ts).
For Life After Code (Path B: Bring Your Own), we built the 9-stage autonomous post-code lifecycle on the GitLab Duo Agent Platform and Google Cloud Run that makes agentic maintenance safe, tamper-evident, and mathematically verifiable.
Empirical Evaluation First: 24 Post-Code Scenarios & 2 Real Bugs Caught
Any CI/CD agent looks reliable on a single rehearsed happy path. We evaluated RxAudit's post-code governance gates against 24 real repository scenarios (src/lib/governance-benchmark.ts, executed on every commit in the verify stage) across three categories:
| Category | Scenarios Tested | Result | Governance Guarantee |
|---|---|---|---|
| A. Legitimate NPPA Updates | 10 real chronic-care DPCO Gazette updates (Dapagliflozin 10mg, Empagliflozin 25mg, Vildagliptin 50mg, Rosuvastatin 20mg, etc.) | 10 / 10 Approved | 0% False Refusal Rate: Valid regulatory updates flow into MR creation without human bottlenecks. |
| B. Adversarial & Reward-Hacking | 8 attacks: unit-vs-strip inversion (₹85/unit vs ₹80/pack), pack math drift (6.10 × 15 != 61.00), generic price > ceiling, clinical dosage advice injection, simulated AI agent tampering with protected test hashes, lowering test assertion count (75 to 50), canary 1-paisa drift (₹500.02 vs ₹500.00), and hallucinated ceiling on unlisted drug. | 8 / 8 Blocked | 100% Block Rate: Zero leaked invariants, zero test-weakening bypasses. |
| C. Ambiguous / Incomplete Inputs | 6 edge cases: bare dosage ("40" without unit), missing pack size, missing DPCO S.O. Gazette citation, bare price without unit/strip distinction, unlisted strength on a known brand ("Okamet 999mg"), and unregulated supplement ("Ashwagandha Gold Forte"). | 6 / 6 Silenced | 0% Hallucination Rate: Enforces Silence Over Guessing — blocks MR creation and posts clarifying questions (blocked:ambiguous-regulatory-input). |
Two Unscripted Bugs Caught During Our Build
The
"Okamet 999mg"Single-Candidate Strength Leak: During Phase 1 adversarial testing, we tested what happens when a receipt contains a known Indian brand with a fabricated dosage strength ("Okamet 999mg"or"Okamet 250mg"). BecauseOkamet SR 1000mgwas the onlyOkametentry indrug-database.json, Pass 2 ofsrc/lib/drug-matcher.tsfiltered out the candidate during strength checks, fell through to acandidatesByBrand.length === 1branch without checking!extractedStrength, and falsely matched1000mg. We patched Pass 2 and Pass 3 so any dosage mismatch on a known brand immediately returnsmatched: false(UNVERIFIED), and locked the regression test inscripts/ratchet-lock.json.Pipeline
#2958744286and GitLab Duo Merge Request!1: When we pushed our first 9-stage.gitlab-ci.yml, Pipeline#2958744286failed withsast job: chosen stage test does not exist. Although we had remappedsemgrep-sastandsecret_detectiontostage: secure, GitLab'sSecurity/SAST.gitlab-ci.ymltemplate defines a parentsastjob defaulting tostage: test. We triggered GitLab Duo Fix Pipeline. Duo diagnosed the template inheritance issue, ranglab ci lintto verify that overridingsast: stage: securefixed the parser error, created branchduo/fix/sast-job-stage-config-error-9647204, and opened Merge Request !1. In its session log, Duo noted: "AGENTS.md: It has no rules about this comment, so I didn't adapt anything. Its rules cover audit logic, tests and deployment." That insight led us to add Section 2 ofAGENTS.md(GitLab Duo MR & Issue Comment Formatting Rules), requiring every agent comment to declare its touched files, ratchet hash status, and silence-over-guessing verification, alongside a permanent test insrc/lib/pipeline-stages.test.tsensuringstage: testnever re-enters.gitlab-ci.yml.
Path B: Before vs. After GitLab Transcend
| Operational Dimension | Before (github.com/SOLOxLEVELING/RxAudit) | After (GitLab Duo Agent Platform + Google Cloud Run) |
|---|---|---|
| CI/CD & Test Suite | Standalone test script; no test runner in package.json; zero CI pipeline. | 108 Vitest tests across 7 suites outputting native GitLab JUnit XML (artifacts/junit.xml) across a 9-stage .gitlab-ci.yml pipeline. |
| Anti-Reward-Hacking Guard | None — an AI agent could edit tests or weaken schemas.ts to pass CI. | Cryptographic SHA-256 Ratchet (scripts/verify-ratchet.ts) locking 11 governance files, a monotonic catalog floor (≥ 153 drugs), and a monotonic test assertion floor (≥ 75, currently 93 assertions / 108 tests). |
| Regulatory Issue Triage (Plan) | Manual JSON edits. | Deterministic NPPA Triage Guard (src/lib/nppa-triage.ts) + GitLab Issue Seeder (scripts/seed-gitlab-issues.ts) enforcing APPROVED_FOR_MR, AMBIGUOUS_OR_INCOMPLETE (exit 2), and REJECTED_INVARIANT_VIOLATION (exit 1) on live Issues #1–#3. |
| External Verification (Govern) | Users had to trust the web UI. | Standalone MCP Server (dist/rxaudit-mcp-server.cjs) + /api/provenance endpoint signing every audit and triage verdict with a 64-char SHA-256 compositeRulebookHash (a7cc4de2644e...). |
| Supply Chain & Security (Package / Secure) | Local dev server only. | Multi-stage non-root (USER nextjs) Docker build, CycloneDX v1.5 SBOM (20 components including the SHA-256 hash of drug-database.json), deterministic secret/env scanner, and GitLab SAST + Secret Detection. |
| Deployment & Canary (Configure / Monitor) | Static Vercel config without canary checks. | Google Cloud Run (us-central1) with Knative IaC (deploy/cloudrun-service.yaml), 2-phase --no-traffic --tag canary rollout (scripts/deploy-cloudrun.sh), and a 4-probe live canary verifier (scripts/verify-canary.ts) that triggers automatic traffic rollback on a 1-paisa drift. |
How All 9 GitLab DevSecOps Lifecycle Stages Work
plan(plan:nppa-triage): Executesscripts/triage-nppa-update.tson NPPA Gazette proposals. If dosage units (mg/mcg/IU), pack size, orS.O.Gazette citations are missing, it exits with code2, blocks MR creation, and posts clarifying questions on the GitLab Issue (live in Issues#1,#2, and#3).create(create:lint-and-typecheck): Enforces strict Next.js 15 / TypeScript 5 AST compilation (npx tsc --noEmit) and ESLint rules governed byAGENTS.mdand.gitlab/duo/chat-rules.md.verify(verify:unit-and-ratchet): Validates all 153 formulations across 5 mathematical invariants (npm run validate-db), verifies the SHA-256 hashes of 11 protected files (npm run verify:ratchet), runs all 108 Vitest tests (npm test), and executes the 24-scenario benchmark (npm run verify:benchmark).package(package:docker-and-sbom): Compiles the Next.js standalone server, bundles the zero-dependency production MCP server (dist/rxaudit-mcp-server.cjs), and generates a CycloneDX v1.5 SBOM (artifacts/sbom.cyclonedx.json).secure(secure:deterministic-audit+sast+semgrep-sast+secret_detection): Runs GitLab native SAST and Secret Detection alongsidescripts/verify-security.ts(verifying zero leaked tokens,.envexclusion, and non-rootUSER nextjsexecution).release(release:manifest-and-changelog): Generatesartifacts/release-manifest.jsonandartifacts/CHANGELOG-RELEASE.mdstamped with the cryptographicRulebookProvenanceStamp.configure(configure:cloudrun-iac-and-deploy): Validates the Google Cloud Run Knative manifest (deploy/cloudrun-service.yaml),Dockerfile, and two-phase canary deployment script (scripts/deploy-cloudrun.sh).monitor(monitor:canary-verification): Executes 4 synthetic canary probes (scripts/verify-canary.ts) verifying health/ratchet lock, exact₹500.00overcharge math to the paisa,"Okamet 999mg"silence on unknown strength, and< 2000mslatency.govern(govern:dossier-and-mcp-smoke): Compiles the tamper-evident compliance dossier (artifacts/governance-dossier.md&.json) and runs a live JSON-RPC 2.0stdiosmoke test against the bundled production MCP server.
Environmental Impact & Software Carbon Intensity (SCI) Quantification
We quantified RxAudit's sustainability architecture using the Green Software Foundation Software Carbon Intensity (SCI) specification (artifacts/governance-dossier.json):
$$\text{SCI} = \frac{(E \times I) + M}{R}$$
where functional unit $R$ = 1 audited pharmacy bill (at runtime) or 1 post-code verification run (in CI/CD), deployed in Google Cloud Run region us-central1 (Iowa grid carbon intensity $I \approx 386\text{ gCO}_2\text{eq/kWh}$).
- 99.99% Operational Energy ($E$) Reduction via Zero-LLM Core Audit & Canary Path: Standard agentic compliance tools call a frontier LLM on every price comparison and post-deploy check (~$1.20\text{ Wh}$ per bill audit and ~$0.80\text{ Wh}$ per synthetic canary check). RxAudit restricts vision extraction to a single pass and executes all 153-drug matching, GST normalization, and 4-probe canary verifications in pure TypeScript (
< 5msCPU time, ~$0.00012\text{ Wh}$ per audit), reducing per-audit compute energy by 99.99%. - Pre-MR Deterministic Ambiguity Triage (
planStage Compute Avoidance): Executingscripts/triage-nppa-update.ts(< 10msCPU time) in Stage 1 before allowing coding agents to run multi-turn LLM reasoning loops and full container builds on incomplete issues avoids ~$18.5\text{ Wh}$ (~$8.1\text{ gCO}_2\text{eq}$) of wasted compute per blocked/ambiguous issue. - Slim Multi-Stage Container & Scale-to-Zero Knative ($M$ & Idle $E$): Next.js 15
output: "standalone"onnode:22-alpine+ a223 KBbundled CJS MCP server paired with Cloud RunminScale: 0eliminates idle server emissions between bill audits.
Built With
- amazon-bedrock
- cyclonedx
- docker
- gitlab
- gitlab-ci-cd
- gitlab-duo
- google-cloud-run
- mcp
- model-context-protocol
- nextjs
- node.js
- react
- tailwindcss
- typescript
- vitest
- zod
Log in or sign up for Devpost to join the conversation.