Agentic reporting · a real run

A report written by an AI agent

We gave an agent a CSV file and one question. It had no editor and no shell, only the tools of bino mcp. A person reviewed the PDF twice. Everything it did is on this page.

93 tool calls3 turns, 2 human reviews0 manifests edited by handbino 0.100.0 · claude-opus-5-5
The video uses the real data, the real transcript and the real PDFs of this run. Captions are available in English.

The task

One question, one rule

The input is 60 rows of generated test data: monthly revenue 2025 for a fictional company, by region, actual and plan. Claude Code ran in headless mode with only the bino MCP tools allowed, so every manifest in the project was written through bino’s own validation.

  • bino mcp as the only MCP server, standalone (--no-proxy)
  • No file, edit or shell tools; only mcp__bino__* and reading MCP resources
  • No user settings or memory loaded (--setting-sources project)
  • Model claude-opus-5-5, Claude Code 2.1.306, bino 0.100.0
prompt.mdmarkdown
You are working in a bino report project. The current directory is the project root
(it has a bino.toml). The only input is `data/sales_2025.csv`: monthly revenue in EUR for
Kestrel Outdoor GmbH in 2025, per sales region (North, South, East, West, Export), as
actual and as plan.

Build a one-page IBCS report for the monthly management meeting that answers:
"Did we hit the 2025 revenue plan, and which region drives the deviation?"

The page should have:
- a message title that states the main finding, taken from the data,
- a chart of actual vs. plan by month with the plan variance,
- a table by region with actual, plan and the variance to plan (absolute and relative),
- one or two short sentences of commentary that are true for this data.

Rules:
- Work only through the bino MCP tools. You have no file or shell tools.
- Read the bino://skills/bino-concepts, bino://skills/bino-authoring and
  bino://skills/bino-ibcs resources first.
- Use introspect_source on the CSV before you write the DataSource.
- Check every manifest with validate_draft before you write it, and check the data
  of your DataSets with get_rows.
- Name the ReportArtefact `sales_2025_report` and render it with the build tool.
- After the build, call get_layout_state and fix what it reports. Rebuild until the
  page is clean.
- End with a short summary: the files you wrote and the main finding.

How it went

Three turns, two reviews

The agent cannot see the PDF. After each build a person looked at it and wrote down what was wrong. That is the same loop the bino Claude Code plugin asks for: “validates and builds” is not the same as “correct”.

Turn 1

The prompt

The PDF after turn 1: the chart shows values like 1.2 and the table shows 5, 6 and 23, although the header says kEUR.

The agent read the bino skills, probed the CSV, wrote a DataSource, three DataSets, a page and the artefact, and built the PDF. get_layout_state found that the chart was far taller than its 320 px slot, and the agent fixed that over several builds. Every check passed, but the numbers on the page were in the wrong unit.

Turn 2

Review 1

The PDF after turn 2: values in kEUR, a wider chart, but labels with decimals that overlap.

A person read the PDF and listed seven problems in plain words. The agent found the cause (the engine added its own unit prefix to values that were already in kEUR), fixed the units, removed an average line and used more of the page.

Turn 3

Review 2

The final PDF: revenue by month with the plan variance, and a table by region with actual, plan and variance.

Three points were left: decimals in the chart, overlapping percent labels, and unused space. The agent rounded the monthly values, dropped the relative variance from the chart and kept it in the table, and used the full width.

review-1.mdmarkdown
I opened the PDF. It builds, but I would not send it to the management meeting yet:

1. The numbers do not match the unit. The header says kEUR, but the chart labels show
   1.2 to 2.5 and the table shows 5, 6, 23. A reader cannot tell what unit they see.
2. The absolute variance row (ΔPL) in the chart shows +0.0, 0.0 and -0.1. It tells the
   reader nothing.
3. A pink line with the label "Ø AC" runs through the ΔPL row. I do not know what it
   means here. Remove it unless it carries a message.
4. Labels overlap: several ΔPL% labels run into each other, and "Revenue by month"
   sits on top of "PL".
5. The chart uses about half the page width and the bottom third of the page is
   empty. Use the space.
6. The total row of the table shows +0 for ΔPL, but the total variance is +96 kEUR.
7. "Plan met" is too strong for +0.4%. Say it more carefully.

Fix these with the same tools, rebuild, run get_layout_state again, and tell me what
you changed. If something is an engine limit and you need a workaround, say so.
review-2.mdmarkdown
Much better. The units, the table and the wording are right now. Three things are left:

1. The monthly values in the chart have a decimal (1,198.9, 2,129.5 ...). The labels are
   cut off and run into each other. Show whole kEUR, like in the table.
2. The ΔPL% labels still overlap each other (for example -0.75% and -0.17%, +0.67% and
   +0.99%). Make them readable.
3. The chart still stops at about 72% of the page width, and the area under the table is
   empty. The chart and the table can both use more of the page.

Same rules as before: fix it with the tools, rebuild, run get_layout_state, and tell me
what you changed.

The result

The final page

The final PDF: revenue by month with the plan variance, and a table by region with actual, plan and variance.
Rendered by bino build from the manifests below.Open the PDF
pages/sales_2025_page.yamlyaml
apiVersion: bino.bi/v1alpha1
kind: LayoutPage
metadata:
  name: sales_2025_page
  description: Revenue 2025 actual vs plan, by month and by region
spec:
  pageFormat: a4
  pageOrientation: landscape
  pageLayout: custom-template
  pageGridGap: "12"
  titleBusinessUnit: Kestrel Outdoor GmbH
  titleMeasures:
    - name: Revenue
      unit: kEUR
  titleScenarios: [ac1, pl1]
  titleVariances: [dac1_pl1_pos]
  titleDateStart: "2025-01-01"
  titleDateEnd: "2025-12-31"
  titleDateFormat: month
  titleDateLink: interval
  titleOrder: categoryindex
  messageText: '2025 revenue was slightly above plan (+96 kEUR, +0.4%): South (+329 kEUR) and Export (+167 kEUR) offset East (-411 kEUR, -11.4%)'
  footerText: "Source: data/sales_2025.csv"
  children:
    - kind: ChartTime
      spec:
        dataset: sales_2025_monthly
        dateInterval: month
        scenarios: [ac1, pl1]
        variances:
          - dac1_pl1_pos
        measureUnit: kEUR
        unitScaling: 200
        measureScale: _
        scale: 1.85
        showOverlayAvg: false
        showOverlayMedian: false
        percentageScaling: 4
    - kind: Table
      spec:
        dataset: sales_2025_region
        type: sum
        order: categoryindex
        orderDirection: asc
        scenarios: [pl1, ac1]
        variances: [dac1_pl1_pos, drac1_pl1_pos]
        barColumns: [dac1_pl1_pos, drac1_pl1_pos]
        measureType: currency
        measureUnit: kEUR
        percentageScaling: 4
        unitScaling: 200
        dataFormatDigitsDecimal: 0
        measureScale: _
        scale: 1.3
    - kind: Text
      spec:
        dataset: sales_2025_kpi
        value: |
          **Slightly above plan.** Revenue reached ${data.sales_2025_kpi[0].ac_m} mEUR against a plan of ${data.sales_2025_kpi[0].pl_m} mEUR (${data.sales_2025_kpi[0].d_k} kEUR, ${data.sales_2025_kpi[0].dr_pct}%). The variance to plan was ${data.sales_2025_kpi[0].h1_d_k} kEUR in H1 and ${data.sales_2025_kpi[0].h2_d_k} kEUR in H2.

          **East drives the negative deviation.** East was below plan in ${data.sales_2025_kpi[0].east_months_below} of 12 months and closed at ${data.sales_2025_kpi[0].east_d_k} kEUR (${data.sales_2025_kpi[0].east_dr_pct}%) vs. plan. South (${data.sales_2025_kpi[0].south_d_k} kEUR) and Export (${data.sales_2025_kpi[0].export_d_k} kEUR) more than offset it.
  pageCustomTemplate: '"a a a a" "a a a a" "b b b c"'
datasets/sales_2025.yamlyaml
apiVersion: bino.bi/v1alpha1
kind: DataSet
metadata:
  name: sales_2025_monthly
  description: Total revenue per month 2025 in whole kEUR, actual (ac1) vs plan (pl1)
spec:
  dependencies: [sales_2025_csv]
  query: |
    -- kEUR in the data: ChartTime does not apply measureScale to variance bars
    -- rounded because ChartTime has no setting for label digits
    SELECT
      strftime(CAST(month AS DATE), '%Y-%m-%d') AS date,
      ROUND(SUM(revenue_actual_eur) / 1000.0) AS ac1,
      ROUND(SUM(revenue_plan_eur) / 1000.0) AS pl1
    FROM sales_2025_csv
    GROUP BY 1
    ORDER BY 1
---
apiVersion: bino.bi/v1alpha1
kind: DataSet
metadata:
  name: sales_2025_region
  description: Revenue 2025 per region in kEUR, actual (ac1) vs plan (pl1)
spec:
  dependencies: [sales_2025_csv]
  query: |
    SELECT
      region AS category,
      CASE region WHEN 'North' THEN 1 WHEN 'South' THEN 2 WHEN 'East' THEN 3
                  WHEN 'West' THEN 4 WHEN 'Export' THEN 5 END AS categoryIndex,
      SUM(revenue_actual_eur) / 1000.0 AS ac1,
      SUM(revenue_plan_eur) / 1000.0 AS pl1
    FROM sales_2025_csv
    GROUP BY region
    ORDER BY categoryIndex
---
apiVersion: bino.bi/v1alpha1
kind: DataSet
metadata:
  name: sales_2025_kpi
  description: One row of formatted key figures for the page title and commentary
spec:
  dependencies: [sales_2025_csv]
  query: |
    WITH r AS (
      SELECT region,
             SUM(revenue_actual_eur) AS ac,
             SUM(revenue_plan_eur) AS pl,
             SUM(revenue_actual_eur) - SUM(revenue_plan_eur) AS d
      FROM sales_2025_csv
      GROUP BY region
    ),
    t AS (SELECT SUM(ac) AS ac, SUM(pl) AS pl, SUM(d) AS d FROM r),
    h AS (
      SELECT
        SUM(CASE WHEN month(month) <= 6 THEN revenue_actual_eur - revenue_plan_eur END) AS d_h1,
        SUM(CASE WHEN month(month) > 6 THEN revenue_actual_eur - revenue_plan_eur END) AS d_h2
      FROM sales_2025_csv
    ),
    east_months AS (
      SELECT COUNT(*) FILTER (WHERE revenue_actual_eur < revenue_plan_eur) AS below
      FROM sales_2025_csv WHERE region = 'East'
    )
    SELECT
      printf('%.1f', t.ac / 1e6) AS ac_m,
      printf('%.1f', t.pl / 1e6) AS pl_m,
      printf('%+.0f', t.d / 1e3) AS d_k,
      printf('%+.1f', 100.0 * t.d / t.pl) AS dr_pct,
      (SELECT printf('%+.0f', d / 1e3) FROM r WHERE region = 'East') AS east_d_k,
      (SELECT printf('%+.1f', 100.0 * d / pl) FROM r WHERE region = 'East') AS east_dr_pct,
      (SELECT printf('%+.0f', d / 1e3) FROM r WHERE region = 'South') AS south_d_k,
      (SELECT printf('%+.0f', d / 1e3) FROM r WHERE region = 'Export') AS export_d_k,
      printf('%+.0f', h.d_h1 / 1e3) AS h1_d_k,
      printf('%+.0f', h.d_h2 / 1e3) AS h2_d_k,
      east_months.below AS east_months_below
    FROM t, h, east_months

The transcript

Every tool call

In order, as recorded. A red mark means the call failed, or bino’s answer contained an error or a warning.

validate_draft × 17edit_manifest × 14get_layout_state × 12get_rows × 9build × 9outline_kind × 8validate_project × 8write_manifest × 6ReadMcpResourceTool × 3remove_manifest_fields × 3describe_project × 1introspect_source × 1list_kinds × 1describe_kind × 1
Show all 93 calls
#TurnToolArgumentResult
11ReadMcpResourceToolbino://skills/bino-conceptsok
21ReadMcpResourceToolbino://skills/bino-authoringok
31ReadMcpResourceToolbino://skills/bino-ibcsok
41describe_projectok
51introspect_sourcedata/sales_2025.csvok
61outline_kindDataSourceok
71outline_kindDataSetok
81list_kindsok
91validate_draftDataSourceok
101write_manifestdatasources/sales_2025_csv.yamlok
111get_rows$sales_2025_csverror or warning
121validate_projecterror or warning
131edit_manifestdatasources/sales_2025_csv.yamlok
141get_rows$sales_2025_csvok
151validate_draftDataSetok
161write_manifestdatasets/sales_2025.yamlok
171write_manifestdatasets/sales_2025.yamlok
181get_rowssales_2025_monthlyok
191get_rowssales_2025_regionok
201validate_draftDataSetok
211write_manifestdatasets/sales_2025.yamlok
221get_rowssales_2025_kpiok
231outline_kindChartTimeok
241outline_kindTableok
251outline_kindTextok
261outline_kindLayoutPageok
271outline_kindReportArtefactok
281validate_draftLayoutPageerror or warning
291validate_draftLayoutPageok
301write_manifestpages/sales_2025_page.yamlok
311validate_draftReportArtefactok
321write_manifestreports/sales_2025_report.yamlok
331validate_projecterror or warning
341validate_projectok
351buildsales_2025_reportok
361get_layout_statesales_2025_reporterror or warning
371edit_manifestpages/sales_2025_page.yamlok
381validate_draftLayoutPageok
391edit_manifestpages/sales_2025_page.yamlok
401buildsales_2025_reportok
411get_layout_statesales_2025_reporterror or warning
421get_layout_statesales_2025_reporttool error
431validate_draftDataSetok
441edit_manifestdatasets/sales_2025.yamlok
451edit_manifestdatasets/sales_2025.yamlok
461remove_manifest_fieldspages/sales_2025_page.yamlok
471validate_draftLayoutPageok
481edit_manifestpages/sales_2025_page.yamlok
491get_rowssales_2025_regionok
501get_rowssales_2025_monthlyok
511validate_projectok
521buildsales_2025_reportok
531get_layout_statesales_2025_reporterror or warning
541validate_draftLayoutPageok
551edit_manifestpages/sales_2025_page.yamlok
561buildsales_2025_reportok
571get_layout_statesales_2025_reporterror or warning
581get_layout_statesales_2025_reporttool error
591validate_draftLayoutPageok
601edit_manifestpages/sales_2025_page.yamlok
611buildsales_2025_reportok
621get_layout_statesales_2025_reportok
631validate_projectok
642validate_draftLayoutPageok
652edit_manifestpages/sales_2025_page.yamlok
662remove_manifest_fieldspages/sales_2025_page.yamlok
672buildsales_2025_reportok
682get_layout_statesales_2025_reportok
692get_layout_statesales_2025_reporttool error
702validate_draftLayoutPageok
712edit_manifestpages/sales_2025_page.yamlok
722buildsales_2025_reportok
732get_layout_statesales_2025_reportok
742validate_projectok
753outline_kindComponentStyleok
763describe_kindComponentStyleok
773validate_draftComponentStyleok
783validate_draftDataSetok
793edit_manifestdatasets/sales_2025.yamlok
803get_rowssales_2025_monthlyok
813edit_manifestdatasets/sales_2025.yamlok
823validate_projectok
833get_rowssales_2025_monthlyok
843validate_draftLayoutPageok
853edit_manifestpages/sales_2025_page.yamlok
863remove_manifest_fieldspages/sales_2025_page.yamlok
873buildsales_2025_reportok
883get_layout_statesales_2025_reportok
893validate_draftLayoutPageok
903edit_manifestpages/sales_2025_page.yamlok
913buildsales_2025_reportok
923get_layout_statesales_2025_reportok
933validate_projectok

What the agent found about bino

  • With measureScale: k, the time chart scales the bars but not the variance bars.
  • The time chart has no setting for label digits, so values still show one decimal. Tables have dataFormatDigitsDecimal.
  • get_layout_state for a single component failed with an output schema error.
  • get_rows returned old rows right after a query changed, until validate_project ran the queries again.

Run it yourself

All files of the run

The project is plain YAML, SQL and CSV. You need bino 0.100.0 or newer to build it, and Claude Code to rerun the agent. A rerun gives a different transcript, because a model does not answer the same way twice.

terminalbash
cd agent-report
bino lint
bino build          # renders dist/sales_2025_report.pdf

./run.sh            # rerun the agent (turn 1)
./run.sh review 1   # send your review of the PDF (turn 2)