Skip to content

pliuz/15-factor-approval-gates

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 

Repository files navigation

15 Factor Approval Gates

Principles for building reliable, auditable, defendable approval infrastructure for AI agents in production.

You shipped the agent. It worked great for a week. Then it took an action somebody had to apologize for.

The postmortem revealed nobody could actually answer who allowed it to act. Approval handlers were hand-rolled per agent. Audit logs lived in Slack history. The CISO found out during the next pen-test, not before.

This document is the principles we wish someone had handed us before we built the first approval gate. It is opinionated. It is not a checklist. It is the thing your team should argue about before writing any code.

If you build it well, you can defend it to any regulator, replace any approver, and route any future tool through it. If you build it badly, you will find out at the worst possible time.


What is an approval gate? An approval gate is the infrastructure layer that pauses an AI agent before a sensitive action, routes the request to a human — or to a declarative policy — for an approve / edit / reject decision, and records that decision in a tamper-evident audit log. It is the control plane between an agent's intent and its action. The 15 factors below are the principles for building one you can defend to a regulator, replace any approver in, and route any future tool through.

The 15 factors at a glance

# Factor In one line
1 Approval as infrastructure Approval logic lives in a layer the agent doesn't know exists
2 Declarative policies Policies are reviewable data, not branching code
3 Provenance Every decision records why it was allowed (policy / flag / human / grant / override)
4 Tamper-evident audit Append-only + SHA-256 chain; a deleted row breaks the chain
5 Default to deny No policy match → reject, never silent allow
6 Approvers ≠ on-call Route by role, shift, expertise, and load — not to your senior engineer
7 Latency is a metric Measure p50/p95/p99 to decision; a slow gate is an offline agent
8 Idempotency Retries, double-clicks, and replays must not duplicate decisions or events
9 Redact at the edge Redact PII in the SDK; send metadata and a hash, not content
10 Soft-delete agent / immutable audit Agents are revocable; audit events are undeletable
11 API + human UI Every decision works through both a machine API and a human UI
12 Test failure modes Cover Slack-down, SLA-expiry, double-click, partition — harder than the happy path
13 Separate requester from approver The thing that asks cannot be the thing that approves
14 Break-glass is a feature Design the emergency path: explicit, attributable, time-boxed, loud
15 Temporal scope A "yes" has a lifetime; approve-once ≠ approve-forever

The first twelve are the original draft. Factors 13–15 were added in the 2026-06-13 revision after a practitioner review flagged the gaps below (separation of duties, governed emergency bypass, and the lifetime of a "yes"). The manifesto is meant to grow.


1. Treat approval as infrastructure, not application code

Approval as infrastructure means the approval logic lives in a layer the agent does not know exists — the agent calls a function that may pause, return, or raise, and nothing more.

The approval logic that lives inside your agent's code dies when the agent is rewritten. Every team that built a v1 agent in 2024 is now writing a v2 in 2026 — and the approval code is the part nobody wants to port.

Approval belongs in a layer the agent does not know exists. The agent calls a function. The function might pause. The function returns or raises. That is the entire contract the agent should care about.

# Wrong: approval logic in agent code
if action.amount > 500:
    approval = await slack_client.ask(approver, action)
    if not approval.approved:
        raise PermissionDenied()
    bank.transfer(action)

# Right: approval as infrastructure
@gated(policy="finance")
def transfer_funds(amount, destination):
    return bank.transfer(amount, destination)

When you treat approval as infrastructure, you can replace your agent framework next year without touching the governance layer. Compliance teams stop being a release blocker. New agents inherit the policy automatically.

2. Make policies declarative, not procedural

A declarative policy is data, not code: a rule a compliance auditor can read, diff across environments, and query — without reading your codebase.

if amount > 500: get_approval() is procedural. It hides intent in branching logic. It is invisible to your audit team, your CISO, and your future self.

{
  "name": "finance-large-transfers",
  "conditions": { "==": [{"var": "tool"}, "transfer_funds"] },
  "auto_approve": { "<": [{"var": "args.amount"}, 500] },
  "approver_group": "finance",
  "sla_seconds": 300
}

A declarative policy is data. Data can be reviewed in a PR. Data can be diffed across environments. Data can be queried by a compliance auditor without reading code.

If you cannot answer "what policies are currently allowing this action?" with a single database query, you have procedural policies. Stop.

3. Every approval decision has a provenance

Provenance means every approval decision records why it was allowed — which named category and which actor — so any decision is investigable after the fact.

When a CISO asks "why was this action approved?" the answer must be more specific than "the system approved it." Approval has at least these distinguishable provenance categories:

  • Policy match — a declarative rule with a known author and version evaluated to true
  • Tool flag — the tool was registered with requires_approval=false
  • Human decision — a named approver clicked Approve at a specific timestamp
  • Standing grant — a human previously granted a scoped, expiring permission that auto-approves this class of request (see Factor 15)
  • System override — emergency bypass invoked by a known operator (see Factor 14)

If your audit log says "auto-approved" without distinguishing these, you cannot defend the system. A policy-driven auto-approve is reviewable. A tool flag is opaque. A human decision is attributable. A standing grant is time-bounded. A system override is investigable.

Build the provenance into the event payload from day one. Adding it after an incident is too late.

4. The audit chain must be tamper-evident

A tamper-evident audit chain is an append-only event log where each event carries a SHA-256 hash of the previous event's canonical payload, so any deletion or edit breaks the chain at every later event.

Append-only at the database trigger level is the floor. If your "audit log" is a table where rows can be updated or deleted by a service-role connection, you do not have an audit log. You have a log table.

CREATE TRIGGER events_no_update_or_delete
  BEFORE UPDATE OR DELETE OR TRUNCATE ON events
  FOR EACH STATEMENT EXECUTE FUNCTION prevent_event_mutation();

The ceiling is cryptographic chaining: each event includes a SHA-256 hash of the previous event's canonicalized JSON payload. A single missing or modified row breaks the chain at every event after it, detectable by walking the chain from any genesis event.

Provide a verification primitive any consumer can call:

SELECT * FROM pliuz_verify_chain(tenant_id := 'X');
-- broken_links = 0, genesis_count = 1 per tenant

Two honest caveats keep this from being theater of a different kind. First, the trigger stops application-layer tampering; an operator who can disable the trigger — a DBA, a cloud-console admin — can still rewrite history. The defense against that is a signed chain-head anchor the customer archives outside your system, so a rewrite no longer matches their independently-held receipt. Second, a single-query verify is bounded by how much it can re-walk in one call; large chains verify in chunks, or against an archived anchor, not in one unbounded scan. The overclaim is "one query proves everything, forever"; the truth is "one query proves this window, an externally-archived anchor proves nothing was truncated or rewritten before it." Build both, and say which one you are leaning on.

In Pliuz: append-only triggers + a SHA-256 chain over the canonical event, plus signed daily chain-head anchors you can pull and archive off-Pliuz, plus a pliuz_verify_chain primitive. The repos are public — read the audit & verification docs.

5. Default to deny, escalate to allow

Default to deny means a request that matches no policy is rejected — never silently allowed, never routed to an unowned queue; "ask a human" is something you opt into with an explicit catch-all policy.

The cost of asking a human is small (a Slack ping, 30 seconds of attention). The cost of letting an agent act unsupervised on a wrong action is large (incident, postmortem, regulatory exposure, customer churn).

These two costs are asymmetric. Your defaults must respect the asymmetry.

When a policy does not match a request, the right answer is no_match → deny — return an error, not silent passage and not an implicit notify. Make "ask a human" something a tenant opts into with an explicit catch-all policy (a low-priority rule that matches everything and routes to a human), so that an unmatched request means a missing policy, which is a configuration bug you want to surface loudly — not a request that quietly slips through to either execution or an unowned approval queue. When an approver does not respond before SLA, the right answer is expired → reject or expired → escalate — not expired → allow. When the policy engine errors, the right answer is fail-closed — not fail-open.

(An earlier draft of this factor said no_match → notify. That contradicts the title: "notify" is a form of allow-with-a-delay, and it hands work to whichever group happens to be on the unmatched path. Deny-by-default plus an explicit catch-all is the version that survives an adversarial reading.)

The team that defaults to allow finds out about the bug because of an incident. The team that defaults to deny finds out because of a complaint, which is cheaper.

In Pliuz: an unmatched request returns 422, never a silent allow; "notify" is an explicit catch-all policy you opt into.

6. Approvers are not on-call engineers

Approvers are not on-call engineers: route each request by role, time zone, expertise, and load across an explicit approver group — never funnel everything to one person.

If every approval request lands in the inbox of your most senior engineer, three things happen: approvals get ignored, the engineer burns out, and you end up with the worst of both worlds — a system that requires human approval but does not get it.

Approval routing should respect:

  • Role — finance actions to finance, security actions to security, customer actions to support
  • Time zone and on-shift status — do not page an approver at 03:00 unless severity demands it
  • Expertise — domain-specific actions need domain-specific approvers
  • Load distribution — fan-out within a group so no single approver absorbs the queue

Build approver_group as a first-class concept, with explicit membership and round-robin or weighted routing.

Test: if you cannot answer "who is the on-shift approver for policy X right now?" with a single query, your routing lives in source code, not in data — and it will rot the first time someone changes teams, takes a holiday, or quits.

7. Latency to decision is a user-facing metric

Latency to decision is a user-facing metric: measure p50/p95/p99 from request to decision per policy, because a gate that is slow to answer is an agent that is effectively offline.

Approvals that take 4 hours kill agent throughput. Approvals that take 4 days mean your agent is effectively offline. If you do not measure the latency, you cannot fix it.

Track at minimum:

  • p50, p95, p99 latency from request_created to decision_made, sliced by policy and approver_group
  • Time-out rate per policy (how often does the SLA expire?)
  • Decision distribution per approver (are some approvers bottlenecks?)

Treat these like you treat API latency. Surface them in the same dashboard the engineers responsible for the agent watch. When p95 latency exceeds the policy SLA, the on-call should get paged.

Test: if you cannot produce p95 request_created → decision_made for one policy, for last week, with a single query, you are not measuring decision latency — you are hoping it is fine.

The team that does not measure decision latency will eventually have an agent silently waiting for an approver who quit three months ago.

8. Idempotency is not optional

Idempotency means retries, double-clicks, and replays never produce duplicate decisions, events, or pending requests — enforced by a caller-supplied key and strict sequence checks.

The agent retries. The approver clicks the button twice because Slack feels slow. The network blips. The cron job picks up a "pending" record that has actually already been processed. The browser tab reloads the inbox during decision submit.

If you cannot survive duplicate decision submissions without creating duplicate events, your audit log lies. If you cannot survive duplicate approval request creation without creating duplicate pending records, your inbox fills with phantom requests.

Make every approval request require an idempotency key from the caller. Make every decision RPC reject duplicate decisions for the same approval_id. Make every event append validate that the next sequence number is exactly previous + 1.

Idempotency is not a feature you add when you scale. It is the contract from the first request.

9. Redact at the edge, not at the center

Redact at the edge means PII is stripped in the SDK inside the customer's process, so only metadata and a content hash — not raw content — need to reach the approval infrastructure.

PII redaction on the server is a data-residency problem dressed up as a feature. If credit card numbers, medical record IDs, or customer email addresses arrive in your approval infrastructure before being redacted, you are now a sub-processor for personal data — with all the GDPR overhead that implies.

Redact in the SDK that runs inside the customer's process. Send metadata, not content. Let the customer pass the redacted summary to the approval gate; let the agent keep the full payload locally for execution after approval.

When you configure redaction, the customer's PII does not need to cross the wire to you. Your audit chain stores the redacted summary, the approval decision, and a content hash. The customer can prove what was approved without ever sharing what was actually sent.

This is not just a privacy feature — it is a scoping decision. Redacting at the edge drastically minimizes the personal data your approval infrastructure touches, which shrinks your regulatory surface under regimes like GDPR, CCPA, and HIPAA. Be honest about the limit: redaction is a control you must turn on per field, not a default that happens by itself; and metadata (timestamps, amounts, account identifiers, the shape of a request) can still be personal data in some jurisdictions, so "we never see PII" is a claim to make carefully, per field, not a blanket exemption. The goal is minimization you can demonstrate, not a magic boundary that removes you from every regulation everywhere.

10. Soft-delete the agent, immutable the audit

Soft-delete the agent, immutable the audit: a revoked agent is marked, not erased (for forensics), while an appended audit event is impossible to delete (enforced at the database trigger).

When a user revokes an agent, the row should not be hard-deleted. You may need to re-spawn it for forensics. You will need to attribute past actions. You will need to investigate whether the agent was compromised.

UPDATE agents SET revoked_at = NOW() WHERE id = $1;

When an event is appended to the audit log, the row must be impossible to delete. Not "policy says don't." Not "service role technically could." Actually impossible, enforced by database trigger.

These two principles are different. Confusing them — by making the audit "soft-deletable for hygiene" or by making the agent "hard-deleted on revoke" — destroys defensibility in the first case and forensics in the second. Build the two flows separately, with different invariants.

11. Expose every approval decision via API and human UI

Expose every decision through both a machine API and a human UI, with the same semantics, so automation and people can each act on the same approval.

If only humans can override, your compliance reporting requires screen-scraping Slack history. If only APIs exist, humans are not actually in the loop — they are watching a process they cannot interrupt.

Every decision must be available through both interfaces, with the same semantics:

  • Human UI — Slack interactive buttons, web inbox, browser push. Optimized for "I am being interrupted; decide in 15 seconds."
  • Machine API — RPC that takes approval_id, decision, reason, idempotency_key. Optimized for "automated escalation, batch override, compliance test fixture."

The API enables: automated SLA enforcement, integration with case management systems, fixtures for end-to-end tests, batch operations during incidents, programmatic compliance reporting.

The human UI enables: actually getting decisions made by people who do not read JSON for a living.

You need both. If you skip the API, you cannot automate the boring parts. If you skip the human UI, nothing gets approved on Friday afternoon.

12. Test approval failure modes harder than approval success

Test approval failure modes harder than success: the happy path is exercised by traffic, but Slack-down, SLA-expiry, double-submit, and partition are only ever exercised by tests.

The happy path is easy: agent calls function, policy matches, approver clicks Approve, function returns. Your tests cover this in five lines.

The interesting tests are:

  • Slack is down — does the approval queue degrade gracefully, or do all requests block?
  • Approver SLA expires — does expired propagate correctly to the agent, or does the agent hang?
  • Policy engine returns ambiguous — does the request fail-closed or fail-open?
  • Approver clicks Approve twice — does the second click succeed silently or duplicate the decision?
  • Network partition between approval and execution — does the agent execute twice on reconnect?
  • Race condition: two approvers click on the same pending request within 100ms — who wins, and is the loser informed?
  • Database trigger rejects the audit append — does the surrounding transaction roll back the decision?

These are illustrative patterns, not a changelog of any one company's outages — but each is a failure mode real teams have shipped. Your test coverage of these failure modes should exceed your coverage of success — because the success path will be exercised by traffic, and the failure paths only by tests.

13. Separate the requester from the approver

Separation of duties means the identity that creates an approval request must be distinct from the identity that decides it — enforced where it cannot be routed around, not just in the UI.

The agent that wants to act cannot be the thing that approves the action. This sounds obvious until you notice how often the approval path quietly collapses into the request path: a service account that can both create and decide an approval, an "auto-approve for trusted agents" shortcut, an approver group whose only member is the bot's own operator.

Separation of duties is the oldest control in finance, and it is exactly the one agents erode. Two requirements follow:

  • The identity that creates an approval request must be distinct from the identity that decides it. Enforce it in the decision RPC, not just the UI — the database is the only place a fourth code path cannot route around.
  • High-blast-radius actions deserve n-of-m: two finance approvers for a transfer above a threshold, not one. The audit trail records each approver and the order they decided.

Test: if a single compromised credential can both raise and approve a high-risk action, you have a rubber stamp with extra steps. Can you name, from one query, the two distinct humans who approved the last over-threshold action?

In Pliuz: the requesting agent (an API-key identity) and the deciding human (an SSO/Slack identity) are structurally separate namespaces, and the decision RPC denies auditors and archived users there — not just in the UI. n-of-m for high-blast-radius actions is on the roadmap.

14. Break-glass is a feature, not a bypass

A governed break-glass is an explicit, attributable, time-boxed, loudly-notified emergency action — not a service-role key handed out in the dark.

Every system that gates production will, one day at 3am, need someone to act now — the gate itself is down, or the only approver is on a plane. If you did not design the emergency path, your team will build one anyway, in the dark, by handing out the service-role key. That is the worst possible break-glass: unlogged, unbounded, unreviewed.

So design it. A governed break-glass is:

  • Explicit — a distinct action (emergency_override), never a side effect of a missing policy or an expired SLA.
  • Attributable — invoked by a named human, recorded as its own provenance category (see Factor 3), never anonymous.
  • Time-boxed — the elevated grant expires on its own, in minutes, not "until someone remembers to revoke it."
  • Loud — it fires a notification to a second party as it happens, and lands on a review queue afterward. Using break-glass should feel like pulling a fire alarm, not flipping a switch.

The team without a designed break-glass does not have fewer emergencies. It has the same emergencies, handled off the record.

Test: can you produce every emergency override from the last quarter — who, when, why, for how long — with one query? If the honest answer is "we'd have to grep Slack and ask around," you have a bypass, not a feature.

In Pliuz today: "system override" is reserved as a provenance category, but the governed emergency_override flow — time-boxed and second-party-notified — is still on the roadmap, not shipped. We are holding ourselves to the same bar this factor sets.

15. An approval has a temporal scope

An approval has a temporal scope: a "yes" is a decision about one action at one time, and approve-once must be distinguishable from a scoped, expiring, revocable standing grant.

"Approved" is not a permanent state of the world. It is a decision about a specific action at a specific time — and conflating "approve this once" with "approve this forever" is how an idempotency key quietly becomes an eternal permission. (We know: an early version of our own gate treated a repeated, redacted payload as already-approved, so a second transfer to a different recipient inherited the first one's yes. The bug was the absence of this factor.)

Make the lifetime of a "yes" explicit and bounded:

  • Approve once is the default — the grant covers exactly this request.
  • Standing grants (approve-for-an-hour, approve-for-this-session, approve-this-class-of-action) are a separate, deliberate choice, with a declared scope and a TTL, shown in the audit as who granted the standing permission and until when.
  • Every standing grant is revocable and appears in a list a human can see and cancel — a permission you cannot enumerate is a permission you have lost control of.

This is the governed answer to approval fatigue ("I don't want to approve the same €10 refund 400 times"): not an accidental replay, but a scoped, expiring, revocable grant.

Test: for any auto-approved action, can you answer "was this approved just now, or is it riding a standing grant — and if so, who granted it and when does it expire?" If approve-once and approve-forever are indistinguishable in your audit log, you cannot.

In Pliuz: standing grants carry a declared scope, a TTL, and appear in a revocable list — this factor exists because we wrote it after fixing the exact bug above in our own gate.


Practicing what we preach

A manifesto whose author fails its own principles is a liability, so here is Pliuz graded against this document, honestly, as of 2026-06-21. The repos are public; check us.

# Principle Pliuz Note
1 Approval as infrastructure @gated() is exactly the one-decorator contract above
2 Declarative policies JSONLogic, versioned with immutable snapshots; a malformed policy is rejected, not skipped
3 Provenance auto-approved events carry auto_approve_source (policy / tool_flag / standing_grant); a human decision is a distinct decision_made event with a named approver. All categories are distinguishable in the log
4 Tamper-evident audit append-only triggers + SHA-256 chain over the full canonicalized event (payload included), approved args additionally anchored as a content hash; signed daily chain-head anchors you archive outside Pliuz are what make anchoring non-circular and defend against an operator with DB access. pliuz_verify_chain is the in-DB floor and verifies large chains in chunks
5 Default to deny unmatched request → 422; "notify" is an explicit catch-all policy you opt into
6 Approvers ≠ on-call 🟡 role routing, off-shift snooze, temporary delegation, and real escalation-with-notification ship today; weighted round-robin within a group is still roadmap
7 Latency is a metric p50/p95/p99 per policy and group, plus timeout rate, on the dashboard (automated paging on SLA breach is the team's to wire)
8 Idempotency required key, 24h window bound to the agent, payload-collision → 409
9 Redact at the edge SDK-side redaction (opt-in per field) with a strict mode that fails loud on a path that matches nothing
10 Soft-delete agent / immutable audit agents carry revoked_at; events cannot be deleted, enforced at the trigger
11 API + human UI 🟡 Slack + web inbox + a REST API for decisions ship today; a first-class server-to-server decision credential for automated escalation is still maturing
12 Test failure modes 🟡 growing — the escalation/expiry path that used to be silent now notifies and is regression-tested; coverage of the long tail is ongoing
13 Separate requester from approver 🟡 requester (agent) and decider (human) are structurally distinct identities, and the decision RPC denies auditors and archived users there, not just in the UI; n-of-m for high-blast-radius actions is roadmap
14 Governed break-glass 🟡 "system override" is reserved as a provenance category, but no break-glass flow ships yet — the dedicated, time-boxed, second-party-notified emergency_override is roadmap
15 Temporal scope of approval standing grants with declared scope, TTL, and a revocable list — born from fixing factor 15's own cautionary tale

We treat every 🟡 as a prioritized roadmap item, not a footnote. If you find one we scored too generously, open an issue — that is the most useful contribution this document can attract.

Frequently asked questions

How do you add human approval to an AI agent in production?

Treat approval as infrastructure, not application code (Factor 1): the agent calls a gated function that may pause, route the request to a human or a declarative policy, and return the approved — possibly edited — payload. The agent should not contain the approval logic, so you can swap agent frameworks without touching governance.

What is the difference between an audit log and a tamper-evident audit chain?

An audit log is a table of events. A tamper-evident audit chain (Factor 4) is append-only and cryptographically chained: each event carries a SHA-256 hash of the previous event, so deleting or editing any row is detectable. A log you can UPDATE with a service-role connection is not an audit log. To defend against an operator who can disable the trigger, archive a signed chain-head anchor outside the system.

Should an AI agent's approval default to allow or deny?

Deny (Factor 5). The cost of asking a human is small; the cost of an unsupervised wrong action is large. An unmatched request should fail loud as a missing-policy configuration bug, not slip through to execution or an unowned queue.

How is this different from "12-factor agents"?

The 12-Factor App and the 12-Factor Agents pattern describe how to build the app and the agent. The 15 Factor Approval Gates describe how to govern what the agent is allowed to do — the approval gate it must pass before acting, and the audit trail it leaves behind. Different layer: one builds the agent, the other controls and proves its actions.

How do you stop approval fatigue without rubber-stamping?

With a standing grant (Factor 15), not an accidental replay: a scoped, expiring, revocable permission that auto-approves a defined class of request for a defined window, recorded as who granted it and until when. Approve-once stays the default; approve-for-a-while is a deliberate, auditable choice.

Do I have to send PII to the approval service?

No, if you redact at the edge (Factor 9). Redaction runs in the SDK inside your process; you send metadata and a content hash, and keep the full payload locally for execution after approval. Redaction is opt-in per field — turn it on for the fields that matter, and use strict mode so a mistyped field fails loud instead of leaking.

How does this relate to the EU AI Act?

These are framework-agnostic engineering practices for building defendable systems. They happen to map onto human-oversight and record-keeping expectations in regulations like the EU AI Act, but the manifesto is about durable engineering, not about any single regulation or deadline.

Background

This document was written by the team behind Pliuz, the control plane for human approval and audit of AI-agent actions. We built Pliuz because we kept watching teams hit these failures one at a time, in production, each rediscovering principles that other teams had already paid for in incidents.

This document stands on two shoulders: Heroku's 12-Factor App, which shaped a generation of cloud software, and the 12-Factor Agents pattern, which did the same for building reliable LLM agents. Those describe how to build the app and the agent. This describes how to govern what the agent is allowed to do — the approval gate it must pass before acting.

The principles are the contribution; the product is one implementation of them. You can build your own gate — these 15 factors are the spec. Most teams discover, somewhere around Factor 8, that they have signed up to own audit infrastructure forever instead of shipping their actual product. That is the trade Pliuz removes. If you would rather build it yourself, take the factors and go; if you would rather not, the docs are at pliuz.com/docs.

This document is open to contribution. It started at twelve principles; factors 13–15 came from a hard practitioner review. If your team has a sixteenth, learned the hard way, we want it. Open a PR or email founder@pliuz.com.

License

MIT for the text. Cite freely.

Contributors

  • Pliuz team — initial draft, 2026-05-24
  • Pliuz team — 2026-06-13 revision: factors 13–15 (separation of duties, governed break-glass, temporal scope), factor 5/9 honesty pass, actionable tests for 6/7, and the self-assessment table
  • Pliuz team — 2026-06-21 revision: per-factor canonical definitions, at-a-glance table, FAQ, 12-Factor lineage, and a scorecard precision pass (factors 3/4/9/11/13/14 reworded to match shipped behavior exactly)
  • your team here

pliuz.com · GitHub

About

The 15 Factor Approval Gates — principles for building reliable, auditable, defendable human-in-the-loop approval infrastructure for AI agents in production. Maintained by the Pliuz team. MIT — cite freely.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors