# Case Study: GrantFlow

## Key facts
- **Role:** Builder & Maintainer.
- **Current state:** `main` runtime 2.4.0; latest tagged release v2.3.3 (2026-05-13).
- **Scope:** Governed grant workflow API with discovery, scoped credentials, idempotent generation, HITL review, traceability, and exports.
- **Primary outcome:** Public FastAPI repository, deterministic demo, and 17-tool MCP surface covering the agent workflow lifecycle.
- **Evidence:** [GitHub repository](https://github.com/vassiliylakhonin/grantflow), release, demo, quickstart, MCP guide, and trust-report contract.
- **Boundary:** Public artifacts demonstrate implementation, not customer adoption, production deployment, or measured customer outcomes.

## TL;DR
- Built **GrantFlow** — an agent-native grant workflow API for donor-aware proposal operations, governed review, traceability, and export-ready evidence packs.
- It is **not** a grant-writing chatbot. It is the API layer an AI agent can discover, register with, call safely, and audit.
- Built around the operational contract a real agent runtime needs: typed `.well-known` discovery, scoped self-serve credentials, OAuth client-credentials, idempotency keys, deterministic generation, HITL checkpoints, audit events, and `.docx` / `.xlsx` / ZIP exports.
- Positioned for NGO / implementer teams with recurring EU, FCDO, USAID, AFD, JICA, ADB, and UN-style workflows, and for any agent runtime that needs governed grant operations rather than ad-hoc LLM calls.

## Evidence and boundaries
- Public GitHub repository: https://github.com/vassiliylakhonin/grantflow
- Deterministic API demo on Hugging Face Spaces: `curl https://vassilbek-grantflow.hf.space/demo/run`.
- Current `main` runtime version: **2.4.0**. Latest tagged GitHub release: **v2.3.3** (2026-05-13). Active development continues on `main`.
- Universal agent contract: [`AGENTS.md`](https://github.com/vassiliylakhonin/grantflow/blob/main/AGENTS.md) — shortest path for agents to discover, authenticate, and use the API without reading the full repo.
- Agent quickstart: [`docs/agents/quickstart.md`](https://github.com/vassiliylakhonin/grantflow/blob/main/docs/agents/quickstart.md).
- MCP tool-server guide: [`docs/agents/mcp.md`](https://github.com/vassiliylakhonin/grantflow/blob/main/docs/agents/mcp.md).
- Trust report format: [`docs/agents/trust-report.md`](https://github.com/vassiliylakhonin/grantflow/blob/main/docs/agents/trust-report.md).
- Architecture, production boundaries, reference topology, and enterprise access layer documented under `docs/`.

## Project state (self-reported)
- Distribution: public Python repository, FastAPI service, container build, MCP stdio server, optional `streamable-http` MCP transport via `pip install "grantflow[mcp]"`. Python 3.11–3.13 supported.
- Implemented surfaces: five `.well-known` discovery routes (`agent-capabilities.json`, `agent.json`, `agent-policy.json`, `agent-tools.json`, `agent-recipes.json`); full credential lifecycle (`POST /agents/onboarding`, `/oauth/token`, `/introspect`, `/credentials/rotate`, `/credentials/revoke`); revocation responses report `revocation_scope` and `restart_safe` so agents can distinguish Redis-backed from in-process-only revocation; `POST /agents/session`; tenant management (`POST /tenants`, `GET /tenants`, `GET /tenants/{tenant_id}`, `GET /tenants/{tenant_id}/usage`) with advisory daily quotas; preflight gates; Redis-backed deterministic generation with idempotency keys; status / quality / grounding / events endpoints; `trust_summary` block in `/quality` payload (`verdict`, `export_ready`, `grounded`, `critic_passed`, `hitl_resolved`, `citations_present`, `blocking_reasons`, and a `governance_flags` breakdown); machine-readable AI-use disclosure (`GET /status/{job_id}/ai-disclosure`); Go/No-Go funder-fit triage (`POST /decision/bid-no-bid`); HITL checkpoints; exports; `GET /` root index and `GET /demo/run` (no auth required); `GET /donors` with per-donor `submission_requirements`; `GET /llms.txt`.
- MCP tool surface: stdio (`grantflow.mcp.server`) and optional `streamable-http` transports; **17 named tools** covering the full agent lifecycle — agent onboarding, session, introspection, OAuth token exchange, credential rotation and revocation, sandbox registration, ingest, preflight, generation start, status / quality / events, HITL approve and list-pending, export payload, and a sandbox happy-path runner.
- CI: unit tests, mypy (full type-gating restored), ruff, supply-chain checks, demo smoke, HITL smoke, grounded LLM evaluation, docker-compose smoke, nightly grounded tail, synthetic alert delivery check, E2E webhook verification, external agent conformance smoke, contract tests for tenant and quality endpoints, and release-governance checks.
- Donor template coverage: dedicated strategy classes (typed ToC schema, MEL schema, role-specific prompts, RAG namespace) for **USAID**, **EU (INTPA)**, **World Bank / IFC**, **GIZ**, **U.S. State Department**, **FCDO**, **AFD**, **JICA**, and **ADB**; **EU** and **FCDO** are the strongest paths, **USAID** conditional. All other donors in the catalog (40+) use `GenericDonorStrategy` with a shared results framework.
- No customer pilots, production-adoption, customer, or benchmark numbers are claimed. Customer-specific pilot data stays outside the public repository by design; public benchmark assumptions are illustrative demo baselines, not measured customer results.

## Context
- The next operator running a grant proposal cycle may be an AI agent, not a person clicking through a dashboard.
- That agent still needs operational controls: discovery, typed contracts, auth, idempotency, preflight gates, review checkpoints, audit events, and deterministic smoke tests.
- A single LLM endpoint with a "draft a proposal" prompt is not a workflow — it is an unbounded text generator without traceability, governance, or export-ready outputs.
- Donor reviewers and audit teams need traceable evidence; agent runtimes need stable contracts and bounded retries; NGO operators need human checkpoints and review SLAs. All three have to live in one API.

## Challenge
Most agent-assisted proposal workflows are wrappers around a chat model. They produce text fluently. They struggle with the operational shape of real grant work: tenanted access, idempotent generation across retries, donor-specific preflight gates, structured review states, audit events, grounding inspection, and exportable evidence packs.

Buyers cannot ship that into an EU or UN review process. Agent runtimes cannot orchestrate it reliably. Operations teams cannot audit it after the fact.

## Delivery path
- Reframed the project from "AI proposal assistant" to **agent-native grant workflow API**. The core artifact is an OpenAPI surface with `.well-known` discovery, not a chat UI.
- Wrote `AGENTS.md` as the shortest path for AI agents to discover, authenticate, and use GrantFlow without reading the full repo.
- Defined the agent operational contract: discovery → onboarding → preflight → deterministic generation with an idempotency key → status / quality / events → HITL review → export.
- Implemented self-serve credentials (signed API keys with expiry, tenant, and scopes) and OAuth client-credentials, with full lifecycle: onboarding → token exchange → introspection → rotation → revocation. Agent-critical endpoints enforce `tenant_id` and scopes when API-key auth is active.
- Added multi-tenant infrastructure: `POST /tenants`, `GET /tenants`, `GET /tenants/{tenant_id}`, `GET /tenants/{tenant_id}/usage` with advisory daily quotas on jobs and ingest.
- Added MCP tool surface: stdio and `streamable-http` transports; 17 named tools spanning onboarding, credentials, ingest, preflight, generation, status / quality / events, HITL, export, and a sandbox happy-path runner.
- Added `trust_summary` block to the `/quality` payload — `verdict`, `export_ready`, `grounded`, `critic_passed`, `hitl_resolved`, `citations_present`, `blocking_reasons`, and a `governance_flags` breakdown — so a buyer or agent reads the trust state without parsing the full response. `export_ready` is returned only when every gate passes, and — when `llm_mode` was requested — only if at least one stage actually used the LLM (it does not claim LLM assistance for a deterministic-fallback run).
- Added a machine-readable AI-use disclosure record (`GET /status/{job_id}/ai-disclosure`) built only from what the job recorded — `generation_mode` (`deterministic` / `llm_assisted` / `deterministic_fallback`), the models actually invoked, grounding and human-review state, and a paste-ready paragraph — to answer funder AI-disclosure expectations. It is a transparency record, not a compliance certification.
- Added Go/No-Go funder-fit triage (`POST /decision/bid-no-bid`) returning a `BID` / `CONDITIONAL_BID` / `NO_BID` verdict with hard-blocker gates and `must_fix_before_bid` actions, so an agent does not draft against a poor-fit opportunity.
- Added `GET /` root index (JSON pointer to demo, health, docs, agent discovery), `GET /demo/run` (no auth required, result in ~2 seconds), `GET /donors` with per-donor `submission_requirements`, and `GET /llms.txt` for machine-readable project description.
- Stood up a deterministic API demo on Hugging Face Spaces (`vassilbek-grantflow.hf.space`) — no-auth, no-LLM-cost.
- Upgraded idempotency to Redis-backed storage so retries hold across restarts, not only in-memory.
- Implemented HITL checkpoints (architect, table of contents, MEL, logframe), critic findings, review comments with lifecycle status, SLA and portfolio signals, grounding gates, citation checks, and readiness warnings.
- Added structured agent errors for auth, idempotency, and generation startup failures so an agent can branch on them rather than parse free-form messages.
- Added exports to `.docx`, `.xlsx`, and buyer-facing ZIP evidence packs.
- Built CI for supply-chain checks, deterministic smoke, HITL smoke, grounded LLM evaluation, docker-compose smoke, nightly grounded tail, synthetic alert-delivery checks, E2E webhook verification, and contract tests for all tenant and quality endpoints. Restored full mypy type-gating.
- Documented production boundaries explicitly: built-in auth covers controlled deployments; enterprise IAM / OIDC / SAML / RBAC sits at the gateway/platform layer and reuses GrantFlow's onboarding metadata. Customer-specific pilot data stays outside the public repository.
- Defined a canonical pilot path: ICP = NGO / implementer teams with recurring EU / FCDO / USAID workflows; scope = 3–6 representative cases with named owners; exit = Go / No-Go based on cycle-time delta, review-loop delta, and trust in traceability.

## My contribution
- Defined GrantFlow as a governed proposal workflow rather than a grant-writing chatbot.
- Built discovery, authentication, tenant, idempotency, generation, HITL, quality, audit, and export contracts.
- Added donor-aware preflight and bid / no-bid gates before drafting begins.
- Exposed AI-use, grounding, review, and export-readiness state as machine-readable records.
- Documented production, security, and customer-evidence boundaries explicitly.

## Outcome
- Lets an AI agent discover its capabilities, tools, policy, and recipes through five `.well-known` endpoints before any real call.
- Onboards agents with self-serve API keys or OAuth client credentials, in tenanted, scoped, expiring form; rotates and revokes credentials without downtime.
- Manages tenants with advisory daily quotas so multi-org deployments stay isolated.
- Runs donor-aware preflight gates before a generation starts.
- Runs deterministic generation against a Redis-backed idempotency key so retries and reconnects do not duplicate work across restarts.
- Surfaces status, quality (including `trust_summary` with `verdict`, `export_ready`, and a `governance_flags` breakdown), grounding, citations, version, and lifecycle events on stable endpoints.
- Returns a machine-readable AI-use disclosure record (`GET /status/{job_id}/ai-disclosure`) drawn only from what the job recorded — a transparency artifact for funder AI-disclosure expectations, not a compliance certification.
- Scores funder fit before a cycle (`POST /decision/bid-no-bid`) so an agent does not draft against a poor-fit opportunity.
- Pauses at HITL checkpoints and resumes only after explicit approval.
- Exports `.docx`, `.xlsx`, and ZIP evidence packs that are ready for donor review.
- Exposes a no-auth demo endpoint (`GET /demo/run`), a `GET /` root index, and a machine-readable `llms.txt` for agent discovery.
- Travels across runtimes: any HTTP-capable agent, plus MCP runtimes via stdio or streamable-http with 17 named tools.

## Trust-layer update — 2026-05

GrantFlow's trust-layer differs from the markdown-skill repos in the portfolio: it lives in code and contracts, not just documentation. The buyer-facing trust artifacts are now explicit:

- **Output trust** ([`docs/agents/trust-report.md`](https://github.com/vassiliylakhonin/grantflow/blob/main/docs/agents/trust-report.md)) — what `trust_summary.verdict` (`export_ready` / `needs_review` / `needs_revision` / `incomplete`) means, what it does **not** mean, and the recommended agent workflow. Honest limitations are stated: `grounded: true` reflects the runtime grounding gate, not factual correctness; `critic_passed: true` means no open high-severity findings, not zero findings; `export_ready` is a governance signal, not a legal certification.
- **Error contract** ([`docs/agents/error-contract.md`](https://github.com/vassiliylakhonin/grantflow/blob/main/docs/agents/error-contract.md)) — structured `detail.code` / `retryable` / `next_action` shape for every agent-facing endpoint; agents branch on machine-readable codes, not on HTTP status or free-form text.
- **Threat model** ([`docs/agents/threat-model.md`](https://github.com/vassiliylakhonin/grantflow/blob/main/docs/agents/threat-model.md), 2026-05) — ten classes of input/processing-side gaps GrantFlow does **not** catch: prompt-injection in proposal text or RAG content, factual correctness, hallucinated citations that look well-formed, donor template drift, cross-tenant content leakage through shared embeddings, spoofed donor profile, replay within the idempotency window, adversarial inputs at scale, operator misuse of `hitl_enabled=false`. A passing `export_ready` verdict means GrantFlow's governance signals all passed — not that the proposal is correct, safe, or submittable without human review.
- **HITL checkpoints** — physical trust-layer, not just declaration: jobs pause at designated stages until a human resolves them when `hitl_enabled=true`.

## Current update — 2026-07
- **Grounding boundary tightened:** the trust-report docs now state that `grounded: true` measures retrieval-side signals, not factual truth and not whether the generated text integrated evidence correctly. Human review remains required.
- **Revocation boundary exposed:** credential revocation reports whether it is Redis-backed and restart-safe, or in-process only. Multi-replica production should enforce revocation at the gateway layer.
- **Agent conformance smoke:** the repo includes `python -m grantflow.agents.conformance --base-url ...` and CI coverage so an external agent can verify discovery, auth, ingest, preflight, generation, HITL, and error-shape assumptions.
- **Pilot honesty:** the README now says there are no customer pilots yet; the canonical pilot path is 3–6 representative cases with named reviewers and a Go / No-Go decision based on measured cycle-time, review-loop, and traceability deltas.

## What it is not
- Not a grant-writing chatbot.
- Not a one-click "auto-fill my proposal" tool.
- Not a replacement for human review, donor relationship work, or organizational accountability.
- Not a database of donor calls, beneficiaries, or pre-approved language.
- Not a factuality verifier. Grounding gates measure retrieval-side signals and citation presence; they do not prove the draft is true or submittable.
- Not a SaaS product with public customer references in this repository — pilot data stays outside the public repo by design.
- Not an enterprise IAM / OIDC / SAML / RBAC implementation in itself — those concerns sit at the gateway / platform layer.

## Tech stack
- Python (FastAPI) service.
- OpenAPI surface with `x-grantflow-agent-recipes` extension and a dedicated recipes endpoint.
- MCP tool servers: stdio (`grantflow.mcp.server`) and optional streamable-http (`grantflow.mcp.fastmcp_server`) via `pip install "grantflow[mcp]"`.
- Self-serve signed API keys and OAuth client-credentials, with introspection, rotation, and revocation.
- Docker / docker-compose, including a pilot compose file and a production-compose example.
- Makefile-based bootstrap (`make bootstrap-dev`).
- CI: pytest, mypy, ruff, supply-chain checks, demo smoke, HITL smoke, grounded LLM evaluation, docker-compose smoke, nightly grounded tail, synthetic alert delivery check, external agent conformance smoke, release-cut and release-drafter workflows.
- Topics on the public repo: `fastapi`, `mcp`, `agentic-ai`, `ai-agents`, `api-first`, `grant-proposals`, `grant-management`, `proposal-workflow`, `human-in-the-loop`, `traceability`, `donor-workflows`, `nonprofit-tech`, `workflow-automation`, `document-generation`, `openapi`.

## Companion projects
- [Agenda Intelligence MD](https://github.com/vassiliylakhonin/agenda-intelligence-md) — deterministic claim/source packet checks that can flag broken references, declared-quote mismatches, lexical-support gaps, and unmatched numbers before GrantFlow output reaches human review.

## Relevance
This project demonstrates how I think about practical infrastructure for agent-driven nonprofit operations: typed contracts before chat UI, governed credentials before "trust the agent", deterministic generation before clever prompting, HITL and audit events before "just ship it", and exports that hold up in front of an EU or UN reviewer.

The honest scope is also part of the design: no customer pilots are claimed, customer-specific pilot data stays out of the public repository, and the README's "Maturity and pilot offer" section names current strongest donor-template paths rather than customer references.

## Why it matters
Grant operations fail when drafting starts before fit, evidence, ownership, and review rules are explicit.

**opportunity intake → bid / no-bid → source ingest → preflight → governed generation → HITL → quality record → export**

The case demonstrates an agent-facing workflow built around traceability and human accountability. It does not claim that a structurally complete package is factually correct or ready to submit without review.

## Project links
- GitHub repository: https://github.com/vassiliylakhonin/grantflow
- Hosted API demo (Hugging Face Spaces): https://vassilbek-grantflow.hf.space/demo/run
- Latest tagged release (v2.3.3): https://github.com/vassiliylakhonin/grantflow/releases/tag/v2.3.3
- Current `main` runtime version: `2.4.0`
- Credential revocation deployment notes: https://github.com/vassiliylakhonin/grantflow/blob/main/docs/deployment/credential-revocation.md
- Agent quickstart: https://github.com/vassiliylakhonin/grantflow/blob/main/docs/agents/quickstart.md
- MCP tool-server guide: https://github.com/vassiliylakhonin/grantflow/blob/main/docs/agents/mcp.md
- Trust report format: https://github.com/vassiliylakhonin/grantflow/blob/main/docs/agents/trust-report.md
- Architecture: https://github.com/vassiliylakhonin/grantflow/blob/main/docs/architecture.md
- Canonical pilot path: https://github.com/vassiliylakhonin/grantflow/blob/main/docs/canonical-pilot-path.md
- Production boundaries: https://github.com/vassiliylakhonin/grantflow/blob/main/docs/production-boundaries.md
- Related operating proof: [Donor reporting quality and delivery reliability](/case-study-donor-reporting.html)
- Related evidence-packet linter: [Agenda Intelligence MD](/case-study-agenda-intelligence-md.html)
