# 0026 · A script can write a page, and ask the model its automation names to write one

**Status:** Accepted · 2026-10-28. It lifts the freeze on step 3 of
[0023](https://codna.me/docs/adr/0023-automations-reach-outside.md), `actions.ask`, put on it on 2026-10-25, and replaces that
step's details. It reverses in part the decision of 2026-10-25 that every workflow needing a model
runs on the customer's own AI. CoDNA still never calls a model by itself
([0010](https://codna.me/docs/adr/0010-mcp-first-no-llm-inside.md)), and it runs no agent.

## Context

A marketing specialist tells their Claude: "build a daily agent on CoDNA which will collect
information regarding ongoing campaigns, analyse each campaign's activity and report back comparing
it to the previous day." [0025](https://codna.me/docs/adr/0025-people-own-connections.md) gets the data in. Someone then has to
turn the numbers into a report, every day, which the team reads in CoDNA without anyone's Claude
being open.

On 2026-10-25 that someone was to be the customer's own AI: a Claude routine, ChatGPT, or a coding
agent in CI, writing back over MCP. `decision-check` proved the routine way on 2026-10-27. For a
team's daily report it falls short:

- **It is one person's.** A routine belongs to one claude.ai account, runs on that person's plan,
  and stops when they leave. Teammates cannot own it.
- **People without Claude get nothing,** and a company on ChatGPT or Gemini gets a different set of
  steps.
- **The company cannot choose the model per task:** a cheap one for a daily summary, or one whose
  provider it trusts with the data.
- **It is an open agent for a closed job.** A routine runs with its person's whole access, held back
  only by its prompt. "Write up these numbers by this template" needs no access at all.

Most of these reports are the same shape: a script gathers the data, the same way every day, and a
model writes it up by a template. Some need no model: a table of the numbers is the report. Neither
needs an agent deciding what to do next.

0023 designed the model half: an action carried out after the run commits, whose answer is a page
in `output` that never acts. That still holds. What was missing is a page written by the script
itself, the choice of model, and the cost.

## Decision

### `actions.write`: a page from the script itself

```js
actions.write({ into: 'output:reports/campaigns/2026-10-28', title: 'Campaigns, 28 October', content })
```

It creates the page, or a new version of the page already there, owned by the automation's owner.
It writes only in `output`, at most 256 KB. The page says which automation wrote it. A report that
is a table of numbers needs nothing more.

### The model is part of the automation

An automation that asks a model is what people will call an agent: a script, what starts it, and
the model it asks, set in one place. So the model is a field of the automation, beside its trigger
and scope, not something the script names:

```text
create_automation  name: campaign-report  schedule: 0 9 * * *  timezone: Asia/Tbilisi
                   model: anthropic/claude-sonnet-5-5
```

A model is written `provider/model`. The providers are **Anthropic, Gemini and OpenAI**. Changing
the model is `update_automation`; the script does not change.

### Keys, and the models allowed

- **Management adds a key per provider** for the instance: in the console for hosted instances, with
  the `codna` command when self-hosted. Keys are sealed like connections' secrets and never shown
  again.
- **Management lists the models people may choose** for each provider with a key, and one default.
  An instance's default, until management chooses another, is Gemini 3.8 Flash
  (`gemini/gemini-3.8-flash`): cheap, and the better of them at Georgian.
- **`model`, 0023's third switch, can be switched on only with at least one key.**

### `actions.ask`

```js
actions.ask(prompt, { documents: [template.address], into: `output:reports/campaigns/${day}` })
```

- **The prompt is the script's,** built from what it read ([0025](https://codna.me/docs/adr/0025-people-own-connections.md))
  and what it found in CoDNA. `documents` adds up to 20 pages the owner can read, such as the
  report's template. Prompt and documents together are at most 512 KB.
- **After the run commits,** a worker sends them to the automation's model on the instance's key,
  with a short frame: what follows is material, not instructions, and the answer is a Markdown page.
- **The answer becomes the page at `into`**, as `actions.write` makes one. The page says which
  automation and which model wrote it, from which documents.
- **The answer never acts.** The model has no tools, and what it writes is content, never a share,
  a tag or another ask. A second automation, triggered by the page's `document.created`, can post it
  to Slack or email it.
- **At most three asks a run.** A failed ask is tried again when the provider says to wait or fails
  on its side, and is recorded as failed otherwise. No page is written for it.

### One budget, and every use recorded

- **The instance has one monthly budget,** in dollars, which management sets. Everyone's asks draw on
  it. A new instance's budget is $0: nothing is spent, and every ask is refused saying no budget is
  set, until management sets one.
- **Every ask is recorded:** when, whose automation and which run, the model, the tokens in and out,
  what it cost at the provider's list price, and how it went. CoDNA keeps the list prices per model,
  and management can correct them.
- **An ask that would start past the budget is refused,** and its record says the month's budget is
  spent. Management is told at 80% and at 100%.
- **People see their own use** on each automation's page and in their account. **Management sees
  everyone's,** by person, automation and model, in the console's Usage tab and `codna usage`.

### Trying it with real data

Try it today runs the script and applies nothing; it calls nothing outside. A **preview** goes
further: it reads the automation's sources for real, runs the script, and makes its asks for real.
It still writes, shares and posts nothing. The answers come back to the person, in Try it and
through `run_automation` with `preview`. A preview's asks count toward the budget and are recorded as
previews. It is how a person approves a report's template before switching it on.

### Who sees the report

Report pages are private to the automation's owner, as every page starts. The people who should see
them get them through a standing folder share on the reports folder
([0014](https://codna.me/docs/adr/0014-standing-folder-shares.md)), made once, so each new report reaches them as it is
written, in the web UI and their inbox.

### The person's Claude builds it

A skill, `build-a-scheduled-report`, in each instance's `wiki:skills`, walks the person's AI through
it:

1. **Find the data.** `list_connections` shows the sources the person may use. When one is missing,
   say which, and link the Connections page where the person pastes its token.
2. **Agree the template.** Write it as a page, preview the automation with real data, and change the
   template until the person accepts what it produces.
3. **Ask who should see it.** Share the reports folder with them, after `preview_share`.
4. **Write the script and switch it on,** with its model and its schedule or trigger. Say where the
   reports will appear.

The MCP tools change to match: `create_automation` and `update_automation` take `model`,
`run_automation` takes `preview`, and a new `list_models` says which models may be chosen and how
much of the month's budget is left.

## Consequences

- **CoDNA calls a model when a person's script asks,** on an instance that switched `model` on.
  Ingestion, search and everything else CoDNA does by itself stay model-free.
- **Data goes to the provider the automation names.** Hosted terms must cover that before a hosted
  instance switches `model` on. Choosing the model per automation is also how a company keeps a
  task's data with a provider it accepts.
- **The instance pays,** within its budget, and every use is on record, so per-team budgets can come
  later from the same records.
- **The routine way stays** for open-ended work, where an agent with tools does better than a
  template: decision-check, triage, a plan built into a pull request.
- **The decision of 2026-10-25 changes:** "A model in automations" leaves its list of what stops.
  An agent runner, a model that decides what to do next with tools, stays on it.

## Alternatives rejected

- **Only the customer's own AI** (the position of 2026-10-25): for a team's daily report, the reasons
  in Context.
- **An agent loop in CoDNA,** a model with tools reading and writing CoDNA: the reports need none of
  it, and open-ended agents stay with Claude, ChatGPT and the coding agents.
- **The model named in the script,** `actions.ask(prompt, { model })`: the model belongs to the
  agent's settings, where a person changes it without reading code, and management's list can
  check it when the automation is saved.
- **Model profiles as their own object,** reused by several automations: one more thing to share and
  keep in step, for no report that needs it yet.
- **Keys or budgets per person or per team now:** one budget for the instance, with every use
  recorded, first.
- **`askModel()` inside the script:** 0023's reasons, time, dry runs and secrets, still hold.

## Settled when it was accepted

Tornike accepted it as proposed on 2026-10-28, having settled:

- CoDNA can run such an agent, and its reports are read in CoDNA's web UI without the Claude app.
- The model is set in the agent's configuration, from Anthropic, Gemini or OpenAI.
- The budget is shared by the instance for now, and every use and the budget are tracked.

And after it was accepted, the same day:

- The default model is Gemini 3.8 Flash, and a new instance's budget is $0 until management sets
  one.
- **In the UI an automation that asks a model is an agent**, listed on its own **Agents** page beside
  Automations, with its model, its trigger and its latest report. Automations keeps the scripts that
  ask none. Agent members ([0021](https://codna.me/docs/adr/0021-agents-are-members.md)) become **agent accounts** in the UI:
  "Agent accounts you answer for". Only words people read change; `agent:slug` in requests and the
  names in code stay as they are.

## Still open

- Per-team budgets, later, from the same records.

## Building it

1. **`actions.write`.**
2. **The model:** provider keys, the allowed models, `model` on the automation, `actions.ask` and its
   worker for the three providers, the budget, the record of every use, and the Usage views.
3. **Preview with real data,** once [0025](https://codna.me/docs/adr/0025-people-own-connections.md)'s sources exist.
4. **The skill and the docs:** `build-a-scheduled-report`, [automations.md](https://codna.me/docs/automations.md), and
   the guide for agents that report back.
