DEV Community

Cover image for Your AI Is "In Production." That Doesn't Mean It's Production-Ready.
Prasoon Anand
Prasoon Anand

Posted on • Originally published at stackrail.io

Your AI Is "In Production." That Doesn't Mean It's Production-Ready.

Stop shipping LLM features like landing pages. APRF is a gated, machine-readable production readiness framework—with code, YAML gates, and CI you can wire up this week.


Most teams ship an LLM feature the same way they ship a landing page: merge the PR, watch the demo, celebrate.

Then reality shows up.

  • A prompt injection drains a customer's CRM.
  • A coding agent commits secrets.
  • A "support bot" hallucinates a refund policy into a lawsuit.
  • A forgotten API key burns $82k overnight.

None of those failures look like "the model wasn't smart enough."
They look like production systems without production gates.

This post is the developer cut of that argument—plus the parts you can implement: allowlists, approval gates, YAML policy, CI, and a machine-readable attestation. Canonical version lives on StackRail: Your AI Is "In Production." That Doesn't Mean It's Production-Ready.

The question most frameworks don't force you to answer

NIST AI RMF tells you how to think about risk.
ISO/IEC 42001 tells you how to manage an AI system.
SOC 2 tells auditors how to trust your company.

Useful. Necessary. Incomplete for the engineer on call.

The question that actually decides whether you sleep at night is simpler:

Can this AI application safely operate in production?

That's the question behind the AI Production Readiness Framework (APRF) — a vendor-neutral working draft published by StackRail.

It's not a certification.
It's not a partner network.
It's not a 0–100 "readiness score" you put in a board deck.

It's a gated methodology: mandatory checks either pass or they block you. Recommended controls never average into the gate.

What "gated" means (and why it matters)

If you've ever been sold an "AI maturity score," you already know the failure mode:

  • Secrets hygiene is a disaster
  • But your eval dashboard looks pretty
  • Somehow you still get 87% ready

APRF forbids that trade.

vanity_score = mean(all_controls)          # ❌ averages away a missing kill switch
gate_result  = ALL(mandatory_checks.pass)  # ✅ one fail = blocked
capability   = min(pillar_levels)          # ✅ weakest pillar wins
Enter fullscreen mode Exit fullscreen mode

Mandatory checks are pass/fail.
Failures are blockers.
Capability attainment is the minimum across pillars — not a mean.
You never publish a single overall percentage.

If that sounds strict: good. Production is strict.

Mental model: demo path vs gated path

+---------------------------+        +----------------------------------+
|         Demo Path         |        |            APRF Path             |
+---------------------------+        +----------------------------------+

+---------------+                    +----------------------+
| Prompt works  |                    | Pin APRF version     |
+-------+-------+                    +----------+-----------+
        |                                       |
        v                                       v
+---------------+                    +----------------------+
|   Merge PR    |                    | Run mandatory checks |
+-------+-------+                    +----------+-----------+
        |                                       |
        v                                       v
+-------------------------+          +----------------------+
| Ship in production      |          | All gates pass?      |
+-------------------------+          +-----+-----------+----+
                                          |           |
                                      No  |           | Yes
                                          |           |
                                          v           v
                                +----------------+  +----------------+
                                | Block release  |  | Attest & ship  |
                                +----------------+  +--------+-------+
                                                             |
                                                             v
                                                  +----------------+
                                                  | Observe & drill|
                                                  +----------------+
Enter fullscreen mode Exit fullscreen mode

What's in the catalog (as of v0.10)

Piece What you get
8 domains Security, safety, data, model lifecycle, agents, reliability, cost, governance
27 pillars Focused control areas under those domains
Core Profile 40 gates for Tier‑2 customer-facing AI
Regulated Profile 61 gates for Tier‑3 / regulated systems
Lenses Extra mandatories for RAG, Agents, Voice, Coding agents
Spec + attestation Machine-readable JSON + downloadable self-attestation
Crosswalks NIST AI RMF, ISO 42001, OWASP LLM Top 10, SOC 2, AWS WA, SLSA — informative only

Machine-readable source of truth: https://stackrail.io/aprf/spec/

A concrete example: "we have an agent with tools"

APRF doesn't congratulate you. It asks (Core + Agents lens territory):

Gate Requirement (paraphrased) Artifact you should have
TOL-M1 Tool calls authorized server-side, not by model output alone Gateway authz tests + deny logs
TOL-M2 Per-agent tool allowlist; unknown tools denied Allowlist config + negative tests
TOL-M3 High-impact tools behind approval / dual control / policy Impact inventory + bypass tests
HUM-M1 High-impact actions inventoried and gated Gate wiring evidence
AGN-* / cost gates Step budgets, kill switch, spend ceilings Configs, drills, billing alerts

If you can't demonstrate those with artifacts, you don't get a soft yellow score. You get gate fail.

Diagram: model proposes, platform disposes

User
 │
 │ Natural language goal
 ▼
Agent Runtime
 │
 │ proposed_tool + args
 ▼
Tool Gateway
 │
 ├─ Validate allowlist
 ├─ Validate JSON Schema
 │
 ├── Invalid?
 │      └──► DENY (logged)
 │
 └── Valid
        │
        ├── High-impact?
        │      │
        │      ├── Yes → Request approval
        │      │            │
        │      │            ├── Denied → Stop
        │      │            └── Approved → Execute tool
        │      │
        │      └── No → Execute with scoped credentials
        │
        ▼
 Tool (CRM / Shell / Deploy)
        │
        ▼
 Sanitized result
        │
        ▼
Agent Runtime
Enter fullscreen mode Exit fullscreen mode

Practical implementation (this week)

You don't need to "adopt APRF" as a religion on day one. Wire the same ideas into your stack.

1. Tool allowlist + schema validation (TypeScript)

import { z } from "zod";

const tools = {
  search_docs: {
    impact: "read",
    schema: z.object({ query: z.string().min(1).max(500) }),
    run: async ({ query }: { query: string }) => searchDocs(query),
  },
  update_crm_contact: {
    impact: "write",
    schema: z.object({
      contactId: z.string().uuid(),
      fields: z.record(z.string().max(200)).refine(
        (f) => Object.keys(f).length <= 10,
        "too many fields",
      ),
    }),
    run: async (args: { contactId: string; fields: Record<string, string> }) =>
      updateCrm(args),
  },
} as const;

type ToolName = keyof typeof tools;

export async function invokeTool(
  name: string,
  rawArgs: unknown,
  ctx: { agentId: string; approvalToken?: string },
) {
  const allowlist = await loadAllowlist(ctx.agentId); // e.g. ["search_docs"]
  if (!allowlist.includes(name as ToolName) || !(name in tools)) {
    await audit({ event: "tool_deny", reason: "not_allowlisted", name, ctx });
    throw new Error("TOOL_DENIED");
  }

  const tool = tools[name as ToolName];
  const args = tool.schema.parse(rawArgs); // throws → no side effects

  if (tool.impact !== "read") {
    await requireApproval({ tool: name, args, token: ctx.approvalToken });
  }

  return tool.run(args as never);
}
Enter fullscreen mode Exit fullscreen mode

2. Non-bypassable approval (Python sketch)

The failure mode to kill: UI has "Approve", but the agent HTTP path calls the tool directly.

HIGH_IMPACT = {"update_crm_contact", "refund_order", "shell_exec"}

def execute_tool(agent_id: str, name: str, args: dict, approval_id: str | None):
    if name not in allowlist_for(agent_id):
        raise PermissionError("not_allowlisted")

    if name in HIGH_IMPACT:
        decision = approvals.get(approval_id)
        if not decision or decision.status != "approved":
            audit("ungated_attempt", agent_id=agent_id, tool=name)
            raise PermissionError("approval_required")
        if decision.tool != name or decision.args_hash != hash_args(args):
            raise PermissionError("approval_mismatch")

    return TOOLS[name](args)
Enter fullscreen mode Exit fullscreen mode

Bypass test you should actually run in CI:

# Expect 403 / TOOL_DENIED — never a CRM write
curl -sS -X POST "$GATEWAY/tools/update_crm_contact" \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -d '{"contactId":"...","fields":{"email":"attacker@example.com"}}' \
  | grep -E 'approval_required|TOOL_DENIED|403'
Enter fullscreen mode Exit fullscreen mode

3. YAML policy pack (versioned with the app)

Pin the framework version and declare which gates you claim for this service:

# aprf/policy.yaml
aprfVersion: "0.10.0"
profileId: aprf-profile-core
criticality: 2
lenses: [agents]          # adds agent-specific mandatories

system:
  name: support-assistant
  description: Customer chat with RAG + CRM tools

gates:
  # Map check IDs → how CI proves them
  TOL-M1:
    evidence: tests/gateway/authz_deny.test.ts
  TOL-M2:
    evidence: config/agents/*/tools.allowlist.json
  TOL-M3:
    evidence: tests/gateway/high_impact_requires_approval.test.ts
  HUM-M1:
    evidence: docs/high-impact-actions.md
  COST-M1:                 # example: spend ceiling / DoW controls
    evidence: infra/budgets/openai.tf

# Recommended checks can live here but MUST NOT influence gate pass/fail
recommended:
  OBS-R2:
    evidence: dashboards/agent-traces.json
Enter fullscreen mode Exit fullscreen mode

4. GitHub Actions: fail the release on gate miss

# .github/workflows/aprf-gates.yml
name: APRF gates
on:
  pull_request:
  push:
    branches: [main]

jobs:
  gates:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Pin & fetch APRF spec
        run: |
          curl -fsSL https://stackrail.io/aprf/spec/ -o aprf-spec.json
          jq -e '.version == "0.10.0"' aprf-spec.json

      - name: Unit / contract tests for tool gateway
        run: npm test -- tests/gateway

      - name: Policy evidence exists for every mandatory gate
        run: |
          python scripts/check_aprf_evidence.py \
            --policy aprf/policy.yaml \
            --spec aprf-spec.json

      - name: Negative: unknown tool is denied
        run: npm test -- tests/gateway/unknown_tool_denied.test.ts
Enter fullscreen mode Exit fullscreen mode

Evidence checker sketch:

# scripts/check_aprf_evidence.py
import json, sys, pathlib, yaml

policy = yaml.safe_load(open("aprf/policy.yaml"))
spec = json.load(open("aprf-spec.json"))

# Resolve Core (+ lenses) mandatory IDs from the pinned spec in real code.
# Here we only verify declared gate evidence paths exist.
missing = []
for check_id, meta in policy["gates"].items():
    path = pathlib.Path(meta["evidence"])
    if not path.exists():
        missing.append(f"{check_id}{path}")

if missing:
    print("APRF gate evidence missing:")
    print("\n".join(missing))
    sys.exit(1)

print(f"OK: {len(policy['gates'])} gate evidence paths present (aprf {policy['aprfVersion']})")
Enter fullscreen mode Exit fullscreen mode

5. Attestation JSON (what "done" looks like)

Self-attestation is not certification. It is a reproducible artifact for PRs, change tickets, and audits.

Minimal shape (see attestation schema 0.6 and samples):

{
  "$schema": "https://stackrail.io/aprf/attestation-schema/0.6",
  "type": "aprf-self-attestation",
  "aprfVersion": "0.10.0",
  "certificationLevel": "self-attestation",
  "assessedAt": "2026-07-25T12:00:00.000Z",
  "subject": {
    "organization": "Your Co",
    "systemName": "support-assistant"
  },
  "assessor": { "name": "platform-oncall", "role": "Platform engineer" },
  "input": {
    "criticality": 2,
    "profileId": "aprf-profile-core",
    "lensIds": ["agents"],
    "outcomes": [
      { "checkId": "TOL-M1", "passed": true, "evidenceRef": "tests/gateway/authz_deny.test.ts" },
      { "checkId": "TOL-M2", "passed": true, "evidenceRef": "config/agents/support/tools.allowlist.json" },
      { "checkId": "TOL-M3", "passed": false, "evidenceRef": "MISSING: approval bypass tests" }
    ]
  },
  "result": {
    "gate": "fail",
    "blockers": ["TOL-M3"]
  },
  "statement": "Self-attestation against APRF Core + agents lens; not third-party certification.",
  "disclaimer": "Crosswalks to NIST/ISO/SOC2 are informative alignment only."
}
Enter fullscreen mode Exit fullscreen mode

One failed mandatory → gate fail. No averaging. No "87% ready."

Try the reference assessment (15–30 minutes)

We published a Core / Regulated self-assessment with optional lenses. Download the attestation JSON when you're done.

Who this is for

  • Engineers shipping agents / RAG / voice / coding copilots into real traffic
  • Platform / MLOps teams tired of vibes-based "we're careful"
  • Security folks who need AI controls that map to tests and configs, not prose

Who this is not for

Anyone looking for a badge that says "we're compliant with everything."
APRF won't pretend. That's the point.


*APRF is a working draft.
Publisher today: StackRail.
Intended long-term steward: a neutral working group via public RFCs.

Contribute: stackrail.io/aprf/rfc.*

Originally published on StackRail (set as canonical above).

Top comments (0)