Elyra
Elyra The coding agent e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Start here
Concepts
Elyra

Reports

Five reports over one filter, and a way to keep a filter you use often.


The shape of it

Every report accepts the same filter: period, projects, types, statuses, priorities, specific people and agents, and whether to count humans, agents or both. Reports differ in what they aggregate, never in what they can be narrowed by.

That is deliberate. Five reports with five filter implementations is five places for "last quarter" to come to mean something slightly different.

What each one answers

Report The question
Status report Where are we, what moved, what is stuck, what is due
Time report Where the hours went, and whose they were
Throughput Are we keeping up, and how long does work take
Workload Who is carrying what, right now
Agent performance Which agents are working, and which are failing

Status report

Built for somebody who is not in the tool. Overdue and on-hold come first, because a status report is read for its problems.

Closings are read from the activity log rather than from updated_at: an issue somebody comments on next week would otherwise slide out of the period it was actually finished in.

Time report

Human time and machine time in the same table, split but never separated. An agent's twenty minutes is twenty minutes the team did not spend, and a report that hides it understates both the cost and the help.

Estimates are only compared where an estimate exists. An absent estimate is not a promise, and counting it as zero would invent an overrun nobody agreed to.

Throughput

Opened against closed, week by week. Whether the backlog is growing is a fact no board will ever show you, because a board only knows the present.

Cycle time is a median. One issue that sat in the backlog for a year should not define the team's reputation.

Workload

Deliberately not filtered by period — what somebody had on their plate last March is not a useful question.

Issues without an estimate are counted and shown rather than averaged away. A plate with three unestimated issues on it is not a light plate.

Agent performance

Success rate counts finished runs only. A run still in flight is not yet a success or a failure, and counting it as either is a lie that flatters.


Time has to be attributable

issues.spent_minutes is a cached total. Underneath it sits one entry per piece of work, each with an actor, a date and a source:

  • A person logs time from the issue's properties panel
  • An agent writes an entry when its run completes

The date used everywhere is when the work happened, not when it was entered. An hour worked on Monday and logged on Friday belongs to Monday.

Agent entries cannot be deleted. They are a record of something that happened, not a claim somebody typed, and removing one would make the run history disagree with the time report.

Saved views

Narrow a report, then Save this view. It appears on the reports index and in the picker on that report.

Saved views are private by default. A saved filter is a working note until its author decides it is a team artefact, so sharing is a deliberate act rather than the fallback. A shared view can be opened by anyone in the workspace and changed only by whoever made it.

A view that has been deleted or unshared stops applying rather than breaking the page somebody bookmarked.

How they are computed

Aggregation happens in the database. The first version fetched every row in the period and grouped in PHP, which is fine for a fortnight and is a memory limit for All time — the one option in the picker nobody thinks twice about choosing.

Three things deliberately stay in PHP:

  • Medians, which have no portable SQL. Only the two timestamp columns are read, never whole models.
  • Weekly buckets, because grouping by week means YEARWEEK on MySQL and strftime on SQLite. Days are grouped in SQL and bucketed in PHP: a year is at most 365 rows.
  • Name lookups, resolved in two queries rather than one per row.

The lists on a status report are capped at 200 rows, since the page shows twenty-five. The export builds its own and is not capped.

Every aggregate is exercised against both MySQL and SQLite in the test suite, because conditional sums, DATE() and JSON paths are exactly where the two engines diverge.

Export

Every report exports as CSV, honouring the filters on screen.

CSV rather than PDF because a spreadsheet is what people actually do with a report: re-sort it, total a column, paste it into something else. A PDF looks finished and can only be read.

The time export is one row per entry rather than a summary, so whoever opens it can group it whichever way they came for. It is streamed lazily — each row is written and forgotten, so a year of entries is never resident at once.

Exports lead with a byte-order mark. Without one, Excel reads UTF-8 as Latin-1 and turns Félagi into Félagi on the first row of every file.


Not there yet

  • No scheduled reports. Nothing arrives in an inbox on a Monday morning.
  • Cost is only as complete as your CLIs. Claude Code reports it; Elyra does not yet, and a run that reported nothing shows rather than $0.00 — a total says how many runs it covers, so a figure from three of forty cannot be multiplied out by mistake.
  • No currency other than USD, and no conversion. An agent hour is time rather than money.
  • No per-project time budget, and no billable flag.