Skip to main content
Say a small travel company in Pune wants an agent to reimburse employee expenses. For example: an employee submits a ₹4,800 taxi bill from a client visit, and the agent files the reimbursement. The finance lead wants a rule that says, in plain words, “reimburse claims up to ₹25,000 when the receipt is attached, never above that, and only when I have signed off.” Writing that rule in a prompt is easy. Writing it so a machine applies it the same way every time, and so the agent cannot talk its way past it, is the work of a policy.

The problem

A rule in a prompt is advice. The agent reads it, weighs it against everything else in its context, and usually follows it. “Usually” is not good enough for money. And even a rule written as code has a quieter problem: it decides on facts, and somebody has to supply those facts. If the agent supplies “the receipt is attached” and “the amount is 4,800”, the rule is only as honest as the agent. So a first policy has to answer two questions, not one. What is the rule? And who vouches for each fact the rule reads?

The idea

In Parmana, a policy is a JSON file at policies/name/version/policy.json. It has a name, a version, and an ordered list of rules. Each rule pairs a condition with an outcome: approve or reject, with a reason written for the person who will read it. Rules run top to bottom and the first one that matches decides. If none matches, the request is refused. A sketch of the finance lead’s rule, shortened:
This is an illustration, not a shipped policy. The shipped examples are in the repository’s policies/ folder, such as customer-refund 1.2.0, and the guide below uses high-value-payment from tutorial 14.

How Parmana does it

Every fact must be accounted for. For each fact a rule reads, the policy must say how it is held to account, or the policy does not load:
  • A copy of a request field goes in boundSignals. claimAmount must equal the amount in the action that will actually run, or the request is refused before any rule is evaluated.
  • A person’s decision goes in approvalSignals. financeApproved counts as true only with a valid signed approval from a trusted approver, for this action, this resource and this amount, used once.
  • Anything else goes in unboundSignalReasons, with a reason. Nothing checks it. The reason is there so a reviewer sees the gap, and such a fact should only ever refuse.
No approve rule without a person. Every rule whose outcome is approve must require an approval signal with is_true, either as its whole condition or directly inside its top level all. A nested any does not count, because another branch could match. A policy that breaks this fails validation. This holds for every policy, reads included. The agent’s facts can refuse; only a person can approve. Types are declared. signalsSchema gives each signal a type: boolean, number or string. A request that sends the amount as text is refused before any rule runs. End with a reason. The engine refuses an unmatched request anyway, but an explicit last rule records why, in words the agent and the auditor can act on. Check before proposing. npx vitest run packages/policy/tests/unit/ReferencePolicies.test.ts loads every policy file through the same validator the server uses. The usual failures are a fact with no binding or reason, and an approve rule without an approval signal. Then the file goes through maker checker. One person proposes it, a different credential approves it with a step up signature, and only then is it the version in effect. Until it is approved it authorizes nothing in production.

What it does not do

  • It does not make a fact true. receiptAttached above is the agent’s word. Declaring a reason is honest, not safe. A fact can be asked of the system that owns it through signalSources, but no shipped policy does that yet, and no real business system is connected.
  • An approval covers the action, the resource and, where declared, the amount. It does not fix every other parameter.
  • It does not judge whether your rule is wise. A limit of ₹25,000 that should have been ₹2,500 passes every check.
  • Rule order is your job. First match wins. The validator warns about rules that can overlap, but it is advisory and says plainly when it cannot tell.
  • It does not remove the person from small actions. Every approved action needs a signed approval, at any amount. There is no automatic path, by design.

The takeaway

A first policy is short. The discipline is in the edges: say where each fact comes from, let the agent’s own facts only refuse, and let a person be the only thing that turns a refusal into a yes. Read more: Write your first policy, Chapter 4: Policies, Policies and the decision.