# Policies and analytics

> Author deterministic executable Policy and emit bounded custom analytics from Agent or Channel runtime code.

## Before you begin {#before-you-begin}

Use Policy for authority and analytics for observation. Policy code is deterministic and has no network or secrets. Analytics fields are bounded and versioned.

## Steps {#steps}

1. Author executable Policy with `policy()` and return a decision bound to the supplied input hash:

   ```ts
   import { hashValue, policy } from "@constal/sdk";

   export default policy({
     id: "support-boundary", version: "1.0.0",
     async evaluate(request) {
       const allowed = request.input.context?.["resource.operation"] === "ticket.read";
       const decision = allowed
         ? { kind: "allow" as const, explicitDeny: false, constraints: [] }
         : { kind: "deny" as const, explicitDeny: true, constraints: [], reason: "operation denied" };
       return { inputHash: request.inputHash, outputHash: await hashValue(decision), decision };
     },
   });
   ```

2. Register analytics beside the Agent that emits it:

   ```ts
   import { agent, analyticsEvent } from "@constal/sdk";

   const resolved = analyticsEvent({
     id: "support.resolved", version: "1",
     dimensions: ["queue"], metrics: ["duration_ms"],
   } as const);

   export default agent({
     id: "support", version: "1.0.0", model: "model", analytics: [resolved],
     async onMessage(message, ctx) {
       const startedAt = Date.now();
       const turn = await ctx.turn({ system: "Resolve the request.", objective: message });
       ctx.analytics.emit(resolved, {
         dimensions: { queue: "general" },
         metrics: { duration_ms: Date.now() - startedAt },
       });
       return turn.message.content;
     },
   });
   ```

3. Deploy Policy with `constal.policy.json` and evaluate positive and negative normalized inputs before attachment. Deploy the Agent separately. Analytics emission records synchronous intent; tenant code receives no analytics storage or query binding.

## Verify {#verify}

Evaluate one allowed and one denied Policy input, then inspect an accepted Run to confirm the effective Policy hash. Emit a custom event from a controlled invocation and query it after projection. Use the live workflow endpoint—not analytics—when you need authoritative current execution state.

## Next steps {#next-steps}

Use [Author a Policy](/docs/policies/author.md) for decision semantics and [Instrument custom analytics](/docs/analytics/instrument.md) for schema, cardinality, and query guidance.
