# How we auto-generate the weekly metrics report

The reporting agent we run on Kortix — connected to our Postgres database and Slack. Every Monday it queries the metrics, writes the report with commentary on what moved, and posts it.

Canonical page: https://kortix.com/use-cases/weekly-report

Every team has a weekly metrics report, and someone spends part of their Monday
building it: running the same queries, dropping the numbers into a template, and
writing a line or two about what changed. It's routine, it's on a schedule, and
it's exactly the kind of thing that gets skipped the week it's most needed.

We run a reporting agent on Kortix that builds it. A Monday cron queries the
metrics from our Postgres database, writes the weekly report with commentary on
what moved and why, and posts it to Slack. Access to the database is read-only.
This write-up covers how the setup works: the trigger, the session model, and the
guardrails.

- **Team:** Kortix
- **Runs on:** A Monday cron
- **Connected systems:** Postgres · Slack
- **Mode:** Cron-driven · read-only database access

## The problem

The weekly report is low-skill, high-consistency work: the same queries, the same
layout, every week. Done by hand it eats an hour of someone's Monday, and the week
things are busiest is the week it's most likely to slip — which is usually the
week the numbers most needed a look.

The common fixes are incomplete. A static dashboard shows the numbers but doesn't
say what moved or why, so someone still has to read it and write the summary. A
scheduled SQL job can post the figures but not the commentary. The interpretation
— what changed, whether it matters — is the part that takes a person, and it's the
part that gets dropped.

## What we built

On Kortix, a Monday cron triggers a reporting agent. Each run spawns an isolated
session (a cloud sandbox) with scoped, read-only access to the Postgres database
and permission to post to one Slack channel. The agent runs the metric queries,
compares them against the prior weeks, writes commentary on what moved, and posts
the report to Slack. It cannot write to the database.

## How it works

### Connect a Monday cron as the trigger

A scheduled **trigger** fires the project every Monday morning. Each firing spawns
a fresh **session** in its own sandbox. One run, one disposable machine, so
nothing carries over between weeks and the report is built from scratch each time.

### Give the agent the report playbook

What the report contains lives as **skills** and **memory** that travel with the
agent: the metric definitions, the queries, the layout, and what counts as a
notable move worth calling out. As the metrics we care about change, we write it
down and the agent picks it up on the next run.

### Connect the systems the report needs

Through scoped **connectors**, brokered server-side so no raw token reaches the
model, the agent can:

- **Query Postgres read-only** — run the metric queries against a read-only role,
pulling this week's numbers and the prior weeks for comparison.
- **Compute the deltas** — compare against recent history to find what moved and
by how much, inside the sandbox.
- **Write the commentary** — turn the deltas into plain-language notes on what
changed and whether it's worth attention.
- **Post to Slack** — the report and its commentary land in the team channel as a
single message.

### Set the guardrails

The database connector is **read-only**: the agent queries but cannot insert,
update, or delete, and its role is scoped to the metrics tables. It posts to one
Slack channel and nothing else. Credentials are encrypted in the secrets manager
and injected at runtime, scoped to the agents you grant them to.

### Let each Monday build its own report

With that in place, every Monday the agent runs the queries, computes the deltas
against recent weeks, writes commentary on what moved, and posts the report to
Slack before the team logs on — the numbers and a plain-language read of them in
one message. No one spends their Monday assembling it.

> **The pattern**
> A Monday **trigger** spawns a session with scoped **connectors**: a read-only
> role into Postgres and a single Slack channel out. The report playbook is encoded
> as **skills** and **memory**. The agent queries, interprets, and posts — with no
> path to write to the database.

## Guardrails

The agent reads production data, so the access is scoped and contained:

- **Isolation.** Every run happens in its own isolated sandbox. The session queries
  the metrics and drafts the report; only the Slack message is written back out.
- **Scoped secrets.** The Postgres and Slack credentials are encrypted in the
  secrets manager and injected into the sandbox at runtime, scoped to the agents you grant them to.
- **Read-only database access.** The database role can select and nothing else —
  no insert, update, or delete — and it's scoped to the metrics tables. The report
  cannot change the data it reports on.
- **Everything is code.** The agent's configuration, skills, and permissions are
  files in the repo, versioned and changed through a reviewed **change request**
  rather than a dashboard setting.

## The outcome

- **Every Monday:** The report built and posted before the team logs on
- **With commentary:** What moved and whether it matters, not just numbers
- **Read-only:** Production metrics queried, never written

The weekly report stops depending on someone having a free Monday, and it arrives
with a read of what changed rather than a table to interpret. The team starts the
week looking at the movement that matters instead of assembling the numbers by
hand.
