Reporting and analysis
Use this guide for summaries, comparisons, trends and charts. Follow Talking to the user for tone and general reporting rules.
Retrieve data with the commands in Records and Filters and pipeline queries, then calculate and present the answer. The CLI returns records and counts; don't assume it provides grouped statistics or charts.
Choose the presentation
Lead with the finding, not a dump of the records. Use the simplest format that answers the question:
| Question | Useful result | Presentation |
|---|---|---|
| How many deals match? | The returned count and its scope | A sentence |
| What needs attention? | Records with the relevant issue, such as no owner or no recent activity | A short, prioritised table |
| How is the pipeline distributed? | Deal count and known value by stage or owner | A comparison table or bar chart |
| What changed over time? | Comparable measurements for each period | A table, line chart or column chart |
| Which records explain the result? | The relevant records and their IDs | A detail table |
Make a chart
Use a chart when it makes a comparison or trend clearer than a table. Generate it with code from the same retrieved data as your figures, never by drawing values by hand. A hand-written SVG needs no libraries. If you use a plotting library, check that it's installed first.
- Use Sofatofu's colours unless the user asks for others. Lilac
#EEEBFFfor the background, Cream#FFFDF8for the chart area, Ink#2D2C4Efor titles and labels, Muted#595A78for supporting text, and Coral#FF806Bfor the main series. Add Purple#372986and Mint#BEE8D6for more series. Don't rely on colour alone: label each series. - Label everything. Give the chart a title, label the axes, periods and units, and show the value on each bar or point when space allows. Say in the chart when the data is partial or illustrative.
- Check the result. If you can render or view the file, look at it before presenting it. Fix cropped, overlapping or unreadable labels.
- Give the user the file. Save it with a descriptive name, and give its absolute path, such as
/tmp/open-deals-by-stage.svg, so they can open it. If the interface shows images, display it as well. If the user can't reach the files on your machine, say so and use a table.
Always include the key figures in text or a table next to the chart.
Keep reports short. Include enough detail to inspect the finding, not every available field. Suggest next actions when useful, but don't change CRM data unless the request covers those actions.
Calculate from retrieved data
Use returned counts directly. For derived totals, averages, percentages, grouping or comparisons, run code against the retrieved JSON. Don't calculate mentally, reconstruct records from memory, or estimate missing values. Use an available runtime such as Python or JavaScript; don't assume a particular runtime or library is installed.
- Keep the source intact. Pass the actual command output into the calculation, rather than retyping selected values. Check that each command succeeded before using its output.
- Handle money precisely. Deal values arrive as numbers. Round only for presentation. Read the workspace currency with
workspace get; custom currency fields have their own currency. Don't add amounts in different currencies without an explicit conversion basis. - Separate missing from zero. A missing value is unknown, not zero. Report how many records lack values. For an average over known values, use only those records in the denominator and say so.
- Define rates. State the numerator, denominator and period. A zero denominator means the rate isn't available, not that it is zero.
- Check the output. Verify row counts and group totals against the source. Deduplicate records by type and ID when combining overlapping searches. Preserve the query scope and calculation method so you can explain the result.
If code execution isn't available, report the verified records and returned counts, and explain which calculations you couldn't perform. Don't replace computation with a guess.
Define the scope and check completeness
State the workspace or relevant filters, time period, units and currency. For time-based reports, name the date field used and the timezone where it affects the boundaries. Deals expected to close in a month are not the same set as deals actually won in that month.
Use metric definitions from the request or established workspace context. If an unresolved definition would materially change the answer, ask. Don't silently invent which custom stages count as open, won or lost.
Calculate over the full matching set, not just the first page:
- Check
totaland follownext.offsetwhile more results remain.items.lengthcounts only the returned page. - If only a count is needed, use
record search --count. Counts don't provide the values needed for sums or averages. - Separate reads return current data, not a shared snapshot. If records change during collection, reconcile the results or disclose the inconsistency. Deduplication alone doesn't prove that no records were missed.
- If you can't retrieve the complete set, label the analysis as partial and state what is missing. Don't extrapolate a partial sum, average or rate to the whole dataset.
Respect what the data can establish
Distinguish observations from interpretations and estimates. A useful chart doesn't make an unsupported metric valid.
- Pipeline snapshots aren't conversion funnels. Current deal counts by stage show today's distribution, not how many deals progressed between stages.
- Trends and durations need history. Stage conversion, time in stage, historical pipeline value and period-specific win rates require the relevant events or historical observations. Current records alone aren't enough. Change history has limited retention; see Change history and reversal. Don't assume it covers the requested period.
- Expected close dates aren't actual outcomes. Label totals based on
expected_close_dateas scheduled or expected pipeline value, not revenue earned or a probability-weighted forecast. Don't invent stage probabilities. - Activity measures need a defined link.
last_activitycounts only active activities linked directly to the deal. It doesn't include activity linked only to a company or person, and it doesn't prove that no contact happened outside the CRM.
When the available data can't answer the question, say what it can show and what additional evidence the requested analysis would need.
