The machine that produces buyer-side AI procurement decisions, schema-conformant and ready to publish.
A FastAPI service that ingests a buyer's evaluation rubric plus a set of vendor Kinetic Gain Protocol Suite declarations and returns a draft AI Procurement Decision Card (spec #11 of the Suite). The Decision Card is the canonical machine-readable carrier for NIST AI RMF-aligned procurement outcomes under OMB M-24-10 — see the crosswalk doc.
This is the first repo that composes the Kinetic Gain Protocol Suite with the Decision Intelligence Engines portfolio:
Vendor publishes: Buyer publishes (this service produces):
───────────────────────── ────────────────────────────────────────
AEO Protocol Card ┐
Tool Disclosure │
Clinical AI Card ├──> AI Procurement Decision Card
Student AI Disclosure │ (status / rubric / conditions /
Agent Card │ documents reviewed / rationale)
…the other six specs… ┘
pip install procurement-decision-api
procurement-decision-api # listens on http://0.0.0.0:8088Or via Docker:
docker run -p 8088:8088 ghcr.io/mizcausevic-dev/procurement-decision-api:latestThen draft a decision:
curl -s http://localhost:8088/decisions/draft \
-H 'content-type: application/json' \
-d '{
"decision_id": "SPRINGFIELD-DEC-2026-001",
"buyer": {
"name": "Springfield Unified School District",
"type": "school-district",
"jurisdiction": "US-CA"
},
"decision_maker": {
"role": "Director of Educational Technology",
"name": "Dr. Jane Doe",
"authority": "Board Resolution 2026-04"
},
"vendor_name": "AcmeTutor Inc.",
"product_name": "AcmeTutor 3.0",
"vendor_id": "https://acmetutor.example/.well-known/aeo.json",
"fetch_targets": [
{ "type": "aeo", "url": "https://acmetutor.example/.well-known/aeo.json" },
{ "type": "tutor-card", "url": "https://acmetutor.example/.well-known/tutor-card.json" },
{ "type": "student-ai-disclosure", "url": "https://acmetutor.example/.well-known/student-ai-disclosure.json" }
],
"policy_uris": [
"https://springfield.edu/.well-known/aup.json"
],
"rubric": [
{ "id": "ferpa-compliance", "result": "pass", "weight": 1.0 },
{ "id": "coppa-compliance", "result": "pass", "weight": 1.0 },
{ "id": "no-training-on-student-data", "result": "pass-with-condition", "weight": 1.0,
"notes": "Disclosure asserts no-training; require contractual confirmation." },
{ "id": "bias-audit-completed", "result": "partial", "weight": 0.8,
"notes": "Audit current but due for refresh by 2026-09." }
],
"conditions": [
{ "id": "no-training-restriction",
"description": "Vendor SHALL NOT use Springfield USD student-provided content for model training.",
"enforcement": "contractual" },
{ "id": "bias-audit-refresh",
"description": "Vendor SHALL deliver a refreshed third-party bias audit by 2026-12-01.",
"enforcement": "audit" }
]
}' | jqThe response includes:
draft— the full, schema-conformant Decision Card (ready to sign + publish at/.well-known/decisions/<id>.json)documents_fetched[]— each vendor URL with its retrieval timestamp + sha256 content hashfetch_errors[]— per-target retrieval errors (the draft doesn't fail wholesale on one missing URL)inferred_status—trueif the service inferred the decision status from the rubric
- Fetches every URL in
fetch_targetsconcurrently with httpx, capped at 2 MB / 10 s per document, and computes a canonical sha256 hash over each (sorted keys, no whitespace). - Infers
decision.statusfrom the rubric if you didn't supplyproposed_status. The inference rules:- Any
fail→rejected-with-remediation - Any
partialorpass-with-condition→approved-with-conditions - All
pass→approved - Empty / all
n/a→pending
- Any
- Composes a default rationale from the rubric results if you didn't supply
rationale_template. - Validates the Decision Card against the same conditional rules the upstream zod schema enforces:
status∈ {approved-with-conditions,rejected-with-remediation} →conditionsmust be non-emptystatus=withdrawn→withdrawalblock requiredpublication.is_public=true→publication_urirequired
- Returns the Draft Decision Card. Review, edit, sign, publish.
| Method | Path | Purpose |
|---|---|---|
| GET | / |
Service info + relevant links |
| GET | /healthz |
Liveness probe (always 200 if the process is running) |
| POST | /decisions/draft |
Produce a Draft Decision Card |
| POST | /decisions/validate |
Validate an existing Decision Card against the v0.1 schema |
| GET | /docs |
Interactive OpenAPI documentation (Swagger UI) |
| GET | /openapi.json |
Machine-readable API schema |
AI procurement under OMB M-24-10 and NIST AI RMF requires agencies to publish reviewable decisions about vendor AI systems. Today, those decisions sit in PDFs and procurement databases — invisible to vendors trying to win future RFPs and invisible to citizens whose data is being processed.
The AI Procurement Decision Card spec defines a machine-readable carrier for those decisions. This service is the tool that produces them at scale: a reviewer fills in the rubric, points at the vendor's published declarations, and gets back a schema-valid card ready to publish at /.well-known/decisions/<decision_id>.json.
For procurement teams, this means a decision becomes a queryable, searchable, audit-friendly artifact — and the vendor's published declarations are cited by URL and content hash, so any drift after the decision is detectable.
┌────────────────────────────────────────────────────────────┐
│ FastAPI app (lifespan-managed) │
│ │
│ POST /decisions/draft │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ fetcher.fetch_documents (async, httpx) │ │
│ │ - timeout 10s per doc │ │
│ │ - 2 MB size cap │ │
│ │ - canonical sha256 hash │ │
│ │ - per-target error collection │ │
│ └────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ rubric.infer_status │ │
│ │ rubric.compose_rationale │ │
│ │ rubric.weighted_score │ │
│ └────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ drafter.draft_decision_card │ │
│ │ - validates conditional rules │ │
│ │ - assembles history events │ │
│ └────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ DraftResponse │
└────────────────────────────────────────────────────────────┘
Pydantic v2 models mirror the JSON Schema 2020-12 spec exactly, including the conditional rules (which run as @model_validator(mode="after") hooks).
git clone https://github.com/mizcausevic-dev/procurement-decision-api
cd procurement-decision-api
pip install -e ".[dev]"
# Run the test suite (mocks the vendor HTTP layer; no internet required)
pytest -q
# Lint, format, typecheck
ruff check src tests
ruff format src tests
mypy src
# Run the service
python -m procurement_decision_api
# or
uvicorn procurement_decision_api.app:app --reload --port 8088This service composes naturally with the rest of the Kinetic Gain ecosystem:
- Input documents can be fetched directly from any vendor's
/.well-known/paths, or validated first viakg-validate-actionin your CI. - Output Decision Cards can be inspected by
mcp-kinetic-gain(tools:decision_card_inspect,decision_card_validate). - Inline validation in the browser is available at validator.kineticgain.com — paste the produced draft, get inline error markers.
MIT. The Kinetic Gain Protocol Suite specifications this service produces are also MIT; reference implementations like mcp-kinetic-gain are AGPL-3.0.
- Spec repo:
ai-procurement-decision-spec - Hosted validator: validator.kineticgain.com
- MCP server:
mcp-kinetic-gain— install withnpx -y mcp-kinetic-gain - GitHub Action:
kg-validate-action - NIST AI RMF crosswalk: suite.kineticgain.com/docs/nist-rmf-crosswalk.md
- Apex: kineticgain.com