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

> Source: <https://dev.to/prasoonanand/your-ai-is-in-production-that-doesnt-mean-its-production-ready-3ka>
> Published: 2026-07-25 10:39:25+00:00

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.

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.](https://stackrail.io/articles/ai-in-production-not-production-ready/)

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](https://stackrail.io/aprf/).

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.

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

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
```

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.

```
+---------------------------+        +----------------------------------+
|         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|
                                                  +----------------+
```

| 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/](https://stackrail.io/aprf/spec/)

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**.

```
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
```

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

``` js
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);
}
```

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)
```

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'
```

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
# .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
```

Evidence checker sketch:

``` python
# 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']})")
```

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

Minimal shape (see [attestation schema 0.6](https://stackrail.io/aprf/attestation-schema/0.6/) and [samples](https://stackrail.io/aprf/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."
}
```

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

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

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](https://stackrail.io/aprf/rfc/).*

*Originally published on StackRail (set as canonical above).*
