Content: this PR. Answers can now carry structured, clickable blocks built from metadata, and the model is told how to present.
present_results tool
Always offered. It reads no data itself.
Input:
summary;
metrics;
table: columns, rows, percentOfColumn (the server computes the shares), and rowFilter + rowKeys for per-row filtered-list links;
chart (bar, pie or line), built from the table's rows;
filterLinks, recordLinks and actions.
Checks: every table and field is validated against metadata and against what the user can see through the UI API (GET /metaData with their session). Items the user cannot see are dropped, and the tool result says so.
Links: built server-side only: /app/<table>?filter=<QQQ filter JSON> (Next's deserializeFilter accepts it), /app/<table>/<id>, or /app/<table>. Model-written URLs never become links.
Formatting: comes from the field displayFormat; percents show as 80.2%.
Output: a presentation message part, persisted and streamed as its own SSE event, plus a server-built multi-line GFM markdown fallback for clients without blocks (CLI/TUI). The contract is in docs/answer-presentation.md.
Prompting
The static system prompt gets a Presentation section, the built-in default skill; profile skills extend it.
Answer first, with no restating ("101 add up to 101").
A table for 3+ items, comparisons or breakdowns, and a chart when shares or trends help.
Metrics for a single big number.
Any list the answer offers to open goes in as a filterLinks item.
Values only from tool results, zero-count categories omitted, and never URLs.
The prefix stays cache-stable.
Golden eval: the owner's example
"How many images do I have by type?" against the fixture media table (81 image/jpeg, 20 image/png) must produce:
a one-line summary;
a 2-row table with counts and 80.2% / 19.8%;
a 2-point pie chart;
two filter links that decode to media.mimeType EQUALS image/jpeg and image/png;
a markdown fallback whose rows are on separate lines.
Negative cases:
unknown table or field;
a table the user cannot see;
a URL in a label;
a percent of a non-numeric column;
more than 12 chart points.
A live-tagged version of the eval exists for real providers.
Tests
270 in total, all green in ./mvnw verify with the JaCoCo gates (core: 194).
## Answer presentation (owner priority 2026-10-13)
The owner saw an answer whose table rendered on one line, ending with "You can open the lists filtered by JPEG or PNG" and no links. Two parts fix it:
- **Rendering:** the root cause was the Next renderer, which lacked GFM. That is fixed in QRun-IO/qqq-frontend-next#111.
- **Content:** this PR. Answers can now carry structured, clickable blocks built from metadata, and the model is told how to present.
### `present_results` tool
- Always offered. It reads no data itself.
- **Input:**
- `summary`;
- `metrics`;
- `table`: columns, rows, `percentOfColumn` (the server computes the shares), and `rowFilter` + `rowKeys` for per-row filtered-list links;
- `chart` (bar, pie or line), built from the table's rows;
- `filterLinks`, `recordLinks` and `actions`.
- **Checks:** every table and field is validated against metadata **and** against what the user can see through the UI API (`GET /metaData` with their session). Items the user cannot see are dropped, and the tool result says so.
- **Links:** built server-side only: `/app/<table>?filter=<QQQ filter JSON>` (Next's `deserializeFilter` accepts it), `/app/<table>/<id>`, or `/app/<table>`. Model-written URLs never become links.
- **Formatting:** comes from the field `displayFormat`; percents show as `80.2%`.
- **Output:** a `presentation` message part, persisted and streamed as its own SSE event, plus a server-built multi-line **GFM markdown fallback** for clients without blocks (CLI/TUI). The contract is in `docs/answer-presentation.md`.
### Prompting
The static system prompt gets a **Presentation** section, the built-in default skill; profile skills extend it.
- Answer first, with no restating ("101 add up to 101").
- A table for 3+ items, comparisons or breakdowns, and a chart when shares or trends help.
- Metrics for a single big number.
- Any list the answer offers to open goes in as a `filterLinks` item.
- Values only from tool results, zero-count categories omitted, and never URLs.
The prefix stays cache-stable.
### Golden eval: the owner's example
"How many images do I have by type?" against the fixture media table (81 `image/jpeg`, 20 `image/png`) must produce:
- a one-line summary;
- a **2-row table** with counts and **80.2% / 19.8%**;
- a **2-point pie** chart;
- **two filter links** that decode to `media.mimeType EQUALS image/jpeg` and `image/png`;
- a markdown fallback whose rows are on separate lines.
**Negative cases:**
- unknown table or field;
- a table the user cannot see;
- a URL in a label;
- a percent of a non-numeric column;
- more than 12 chart points.
A `live`-tagged version of the eval exists for real providers.
### Tests
270 in total, all green in `./mvnw verify` with the JaCoCo gates (core: 194).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Answer presentation (owner priority 2026-10-13)
The owner saw an answer whose table rendered on one line, ending with "You can open the lists filtered by JPEG or PNG" and no links. Two parts fix it:
present_resultstoolsummary;metrics;table: columns, rows,percentOfColumn(the server computes the shares), androwFilter+rowKeysfor per-row filtered-list links;chart(bar, pie or line), built from the table's rows;filterLinks,recordLinksandactions.GET /metaDatawith their session). Items the user cannot see are dropped, and the tool result says so./app/<table>?filter=<QQQ filter JSON>(Next'sdeserializeFilteraccepts it),/app/<table>/<id>, or/app/<table>. Model-written URLs never become links.displayFormat; percents show as80.2%.presentationmessage part, persisted and streamed as its own SSE event, plus a server-built multi-line GFM markdown fallback for clients without blocks (CLI/TUI). The contract is indocs/answer-presentation.md.Prompting
The static system prompt gets a Presentation section, the built-in default skill; profile skills extend it.
filterLinksitem.The prefix stays cache-stable.
Golden eval: the owner's example
"How many images do I have by type?" against the fixture media table (81
image/jpeg, 20image/png) must produce:media.mimeType EQUALS image/jpegandimage/png;Negative cases:
A
live-tagged version of the eval exists for real providers.Tests
270 in total, all green in
./mvnw verifywith the JaCoCo gates (core: 194).🤖 Generated with Claude Code