Applied machine learning, and the infrastructure it runs on.
M.S. Computer Science at Indiana University, graduating May 2027. Software Development Engineer intern at SparkFX, working on agent orchestration in Next.js and TypeScript. Three peer-reviewed conference papers across computer vision, biosignal processing and LLM optimization. Open to new-grad software, machine learning and cloud engineering roles in the US.
The console blocks below are captures, not illustrations — produced on 24 August 2026 by
running each tool against the fixtures committed in its own repository, on a machine
with no Docker and no GPU. Where a project's numbers could not be reproduced that way,
it says so instead of showing something that looks like output.
On 28 July 2026 the Model Context Protocol removed sessions and the initialize handshake, made server/discover mandatory, replaced server-initiated requests with Multi Round-Trip Requests, and put Roots, Sampling, Logging, HTTP+SSE and OAuth Dynamic Client Registration on a twelve-month removal clock. Every server written before that date is now non-conformant in ways its authors have not enumerated.
Sentinel is two halves: a Go broker built natively on the new revision, and a Python harness that scans any MCP endpoint, grades it rule by rule with a specification citation attached to each finding, and inventories the deprecated features still in use with the date each becomes removable.
The interesting part is what it refuses to say. Five normative MUST requirements cannot be settled from outside a server — whether a token audience is really checked, whether an inbound token reaches a downstream dependency, whether a retry is idempotent at the effect rather than in the reply. The harness reports those as INDETERMINATE, excludes them from the gate, and reprints them on every scan. A scanner that graded them as passes would be lying, and the clean report it produced would be worse than no report.
What a black-box scan can and cannot settle. Five MUST requirements sit below the line; the harness reports them as INDETERMINATE and the broker covers them with tests that have the access a scan does not.
the same harness against two servers
$ sentinel scan --endpoint http://127.0.0.1:9000/mcp --gate must # unmigrated
MUST: 2 pass, 25 fail, 5 indeterminate, 0 n/a
SHOULD: 1 pass, 4 fail, 0 n/a
37 rules in 0.42s
5 MUST rule(s) cannot be verified black-box and were excluded from the gate.
exit 1
$ sentinel scan --endpoint http://127.0.0.1:9001/mcp --gate must # conformant
MUST: 27 pass, 0 fail, 5 indeterminate, 0 n/a
SHOULD: 5 pass, 0 fail, 0 n/a
37 rules in 0.29s
5 MUST rule(s) cannot be verified black-box and were excluded from the gate.
exit 0
Exit codes are a contract: 0 passed, 1 the target failed the gate, 2 the scanner could not run. CI has to tell "the server is wrong" from "the scanner broke".
a finding, and a refusal to make one
FAIL MCP/2026-07-28/MUST/header-body-mismatch-rejected
A header disagreeing with the body is rejected with -32020
observed: a header/body mismatch returned -32011 rather than -32020
remediation: Compare Mcp-Method and Mcp-Name against the JSON-RPC body and
return -32020 HeaderMismatch when they disagree. This is what
makes the headers BINDING: a gateway routes on them, so a body
that says something else must not be honoured, or the gateway
authorized a request that never happened.
spec: …/2026-07-28/basic/transports#header-contract
evidence: {'code': -32011, 'message': 'unknown tool'}
???? MCP/2026-07-28/MUST/token-audience-validated
The server rejects tokens not issued for it
why: Settling this needs a token correctly signed by the server's OWN
issuer but carrying a different audience. The harness cannot mint
one, and a token it could forge would be rejected for its signature
— which proves nothing about the audience check.
To settle it: mint such a token with your issuer and confirm the
server refuses it.
Every failure names the rule, what was observed, what to change and the clause it comes from. Every INDETERMINATE says why a scan cannot settle it and what would.
deprecation debt, with removal dates
$ sentinel deprecations --endpoint http://127.0.0.1:9000/mcp
6 deprecated feature(s) in use
IN USE Roots (SEP-2577)
deprecated: 2026-07-28
removable on or after 2027-07-28 (11 month(s) from now)
replace with: explicit tool arguments naming the paths a tool may touch
IN USE HTTP+SSE transport (SEP-2596)
deprecated: 2025-03-26
removable three months after SEP-2596 reaches Final (not yet scheduled)
replace with: Streamable HTTP
Two removal windows, and only one of them is arithmetic. HTTP+SSE is gated on an event that has not happened, so the tool prints the condition instead of inventing a date — a date printed here ends up in someone's plan as a deadline.
13k lines of Go, 5.7k of Python, 36 test files
Detected 29 of the 29 violations the fixture declares it seeds — nothing missed, nothing flagged that was not seeded — and 0 failures against the conformant fixture
golangci-lint, go test -race, mypy and ruff enforced in CI
No model API key required anywhere, so CI is fast and never externally flaky
Answers one question: did this change make the agent better or worse, and can you reproduce that answer tomorrow. Every trial runs in its own container with its own workdir volume, and the suite ships a contamination probe with a deliberate failing direction — isolation is asserted rather than assumed, because a probe that cannot fail proves nothing.
It never trusts the container. Assertions run on state extracted to the host, after the container is gone. In 2026 researchers broke several major agent benchmarks through exactly that hole — agents writing a conftest.py that rewrote every result to passed, or replacing /usr/bin/curl to emit fake output.
Every verdict carries a minimum detectable effect: this run could only have caught a drop of 0.183 or larger; resolving a 0.030 tolerance would take about 262 tasks. A PASS from an underpowered suite is not evidence that nothing broke, and a gate that reports those identically teaches people to trust it exactly when it is least reliable.
The container is never trusted. State is extracted to the host and the container destroyed before any assertion runs, and the gate reports the smallest regression the run could actually have detected.
the unit layer, on a machine with no Docker
$ uv run pytest -m unit -q
........................................................................ [ 31%]
........................................................................ [ 63%]
........................................................................ [ 95%]
........... [100%]
SKIPPED [1] tests/integration/test_budget_halt.py:98:
the Docker daemon is not reachable; integration tests need it
226 passed, 1 skipped, 46 deselected in 3.20s
The skip is the point: the tests that need real container isolation refuse to run without it rather than quietly passing. 46 deselected are the integration and end-to-end layers.
Six Lambda services, and a README that admits what is missing
A ride-sharing backend decomposed into six Lambda-backed services behind an HTTP API Gateway, with a separate WebSocket API for live location, DynamoDB for state, Cognito for auth, and the environment declared in Terraform rather than clicked together in a console.
Ride hailing is the textbook always-on backend, which normally means paying for servers that sit idle between requests. This is the same system built so that nothing runs between rides.
Six services behind an HTTP API, a separate WebSocket API for live location. Dashed edges are declared in the repository but not provisioned by the Terraform as it stands.
Stated plainly: An architecture and service-layer study, not a deployed product. The Terraform provisions the data and edge layer but does not yet declare the Lambda functions, routes or IAM roles, so it does not deploy end to end. The repository says so on its front page.
Five model variants over the same seven-class problem, written to be compared: a CNN-GRU with key-frame selection, a MobileNet baseline, a 3D ResNet with a Vision Transformer, and a physics-informed model that adds temporal continuity, energy and momentum terms to the classification loss.
A bat swing is a smooth trajectory, so a model whose per-frame beliefs jump around is wrong even when its final answer is right. The physics terms penalise exactly that.
Stated plainly: Research code behind a peer-reviewed conference paper, released as-is. The video dataset is not redistributable and no trained checkpoints or result artifacts are committed, so the accuracy reported in the paper cannot be reproduced from this repository alone.
How much of a model should you freeze? Search for it
Usually that decision is a guess — freeze everything but the last few layers. This treats the freezing schedule as a search problem instead, running a genetic algorithm and the Whale Optimization Algorithm over freezing percentage and crossover operator for RoBERTa on SST-2.
Two findings held across every configuration: SBX crossover wins, and less freezing wins.
Best result 94.67% accuracy (Whale Optimization, SBX crossover, 30% layer freezing)
Stated plainly: The genetic-algorithm experiment is committed as a notebook; the Whale Optimization implementation is not in the repository, though its results are.
A signal problem before it is a modelling problem. Neither the deltoid nor the pectoral channel says much alone, so the pipeline computes windowed RMS, integrated area, skewness and kurtosis per channel and classifies the balance between them.
It is here because of what re-reading it found rather than what it achieved. Auditing your own published work and publishing the correction is a slower way to be right, and the only one that compounds.
Stated plainly: No accuracy is quoted here, and the repository now explains why. The label is a threshold on a quantity that is also one of the model's input columns, so a three-line rule reproduces every label exactly — a network reported at 92.5% was scoring below a rule it could see. Consecutive windows also overlap by 96.7% before a random train/test split. The README leads with all of this now: the finding is more useful than the figure was.
Community Builder: connecting communities such as HOAs and resolving member issues through AI agent orchestration with automatic delegation.
Reported 30% reduction in operational cost and manual handling through that orchestration, and 20% faster page loads through caching.
Next.js 16 App Router, TypeScript, Tailwind v4.
January — April 2025
Software Developer Intern · PROLIM Solutions India
Built 7+ REST APIs with role-based access control and dashboards for a manufacturing management system spanning three production lines, used daily by 20+ operators.
Cut batch processing from 20–25 minutes to 8–10, reduced manual data entry 50% and errors 30%, and shrank release rollout from five days to two.
Earned the Mendix Rapid Developer certification.
June — August 2024
Software Developer Intern · LiRC Tek Solutions
Automated transportation-management rate-confirmation processing in Python, cutting per-order handling time 45%.
Built regex parsers that eliminated 80% of manual document review at 95% extraction accuracy.