# 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 an executable Policy with `policy()` and return one typed outcome. The execution environment binds it to the request and outcome hashes:

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

   export default policy({
     id: "support-boundary", version: "1.0.0",
     evaluate(input) {
       const allowed = input.context["resource.operation"] === "ticket.read";
       return allowed
         ? { kind: "allow" as const }
         : { kind: "deny" as const, code: "operation-denied", reason: "operation denied" };
     },
   });
   ```

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`. Its required `target` declares one Resource kind and one normalized label selector; executable code does not declare a parallel Resource-pattern attachment. Evaluate positive and negative normalized inputs before promotion. 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.
