> ## Documentation Index
> Fetch the complete documentation index at: https://graph.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Reporting

> Status reports and scheduled digests, annotated

Plans that turn workspace data into prose on a schedule or on demand. Both examples ship as working plans (`~/.config/graph/plans/`) against a Linear workspace; the lessons transfer to any MCP source.

## `project_status` — solver report, resilient references

A status report for a Linear project: metadata → milestones → 14 days of issues → latest human status updates → synthesized report.

```yaml theme={null}
steps:
  - id: E0
    tool_name: linear__list_projects
    input: { query: "{{input.project}}", limit: 5 }
  - id: E1
    tool_name: linear__list_milestones
    input: { project: "{{E0.projects.0.name}}" }
  - id: E2
    tool_name: linear__list_issues
    input: { project: "{{E0.projects.0.name}}", updatedAt: "-P14D", limit: 250 }
  - id: E3
    tool_name: linear__get_status_updates
    input: { project: "{{E0.projects.0.name}}", type: project, limit: 3 }
```

**Lessons:**

* Later steps pass the project *by name* (`{{E0.projects.0.name}}`) — one canonical deep reference instead of chained ids. A bad project name produces exactly one clean `EmptyData` outcome.
* The solver template conditionally includes a Milestones section only when milestones exist:
  ```
  {{#E1.milestones}}{{#@first}} …section instructions… {{/@first}}{{/E1.milestones}}
  ```
* Real-world constraint encoded as a comment in the plan: Linear's API rejects `includeMilestones` on project listing (query complexity), so milestones are their own step.

## `sprint_analysis` — strict output via a grammar

Ported from graph's predecessor. Its solver template pins the report *structure* with a context-free grammar, which buys week-over-week consistency:

```
<document> ::= <sprint-header> <rationale-section> <project-snapshot>
<sprint-header> ::= "# Sprint " NAME ": " <status-emoji> " " <status-text> …
<status-text> ::= "AT RISK" | "ON TRACK" | "NEEDS ATTENTION"
```

**Lessons:**

* A grammar (or any rigid spec) in `query_to_answer` is the tool for reports that get diffed or posted on a schedule — prose specs drift more.
* The plan handles "team has no active cycle" honestly via an inverted section that switches the instructions:
  ```
  {{^E1}}NOTE: this team has NO active cycle. Say so plainly…{{/E1}}
  ```
* Aggregation (ticket counts, story points) is done *by the solver from raw data* — no computation in templates, by design.

## Recipe: scheduled report to Slack

A solver plan on cron — stdout is the deliverable, so delivery is a pipe:

```bash theme={null}
graph plan run project_status '{"project":"New Relic"}' > report.md
curl -X POST "$SLACK_WEBHOOK" --data-urlencode "payload={\"text\": $(jq -Rs . < report.md)}"
```

With the [`slack` pack](/tools/builtins#the-slack-pack) enabled, delivery can live *in* the plan instead of the pipe: a final `builtin__slack_post_message` step posts the report and returns the message `ts`, so a follow-up step can thread details under it. Prefer the tool when the plan owns delivery and a failed post should fail the run; prefer the webhook pipe when stdout is the deliverable and the shell owns delivery (a webhook needs no app scopes or channel invite).

Run it with `GRAPH_STORAGE=memory` in the cron environment when it should leave no state behind ([scripting contract](/reference/scripting-contract)).

## Patterns to reuse

| Pattern                  | How                                                     |
| ------------------------ | ------------------------------------------------------- |
| Optional data section    | `{{#Ex.field}}…{{/Ex.field}}` around instructions       |
| "Nothing found" handling | `{{^Ex.values}}say so{{/Ex.values}}` (solver mode)      |
| Fan-in for the solver    | multiple `data:` keys, one per step result              |
| Strict formats           | pin the structure with a grammar in `query_to_answer`   |
| Time windows             | ISO-8601 durations in tool params: `updatedAt: "-P14D"` |


## Related topics

- [Finish modes](/plans/finish-modes.md)
- [What is a plan](/plans/overview.md)
- [CLI reference](/reference/cli.md)
