Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"guides/github-linear",
"guides/mcp-setup",
"guides/dashboard",
"guides/worklogs",
"guides/querying-data"
]
},
Expand Down
157 changes: 157 additions & 0 deletions guides/worklogs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
title: "Review and Approve Worklogs Before They Post to Jira"
sidebarTitle: "Worklogs"
description: "Use the Worklogs view in the Meridian dashboard to review, edit, and approve drafted worklogs — the only path that posts time to your tracker."
---

Meridian's PM-worklog stage synthesises one worklog per ticket per hour from your classified sessions. **Nothing reaches Jira automatically.** The daemon only ever *drafts* worklogs; you review, edit, and approve each one in the dashboard's **Worklogs** view, and a separate post sweep writes the approved entries to Jira within about a minute.

Use this guide when you want to keep an automated record of your time without giving up the final say over what lands on a ticket.

## When to use this

Turn to the Worklogs view if you want to:

- Capture per-ticket time logs from your real activity without writing them by hand.
- Keep a human in the loop — every comment posted to Jira is something you explicitly approved.
- Edit auto-generated comments before they go out, or dismiss drafts that don't represent useful work.

The flow assumes Jira is already connected. See [Connect Meridian to Jira Cloud](/guides/jira) if you haven't set up the connector yet.

## The drafted → approved → posted state machine

Every worklog moves through a small set of states:

```
drafted ──(UI edit)──▶ drafted ──(UI approve)──▶ approved ──(post sweep)──▶ posted
terminal (empty / < 60s) └──▶ failed
```

| State | What it means |
|---|---|
| `drafted` | The hourly driver synthesised a worklog from your sessions. It is **not** in Jira. Edit it, approve it, or dismiss it. |
| `approved` | You approved the draft in the Worklogs view. It is queued for the next post sweep but has **not** been posted yet. You can still un-approve it to hold it back. |
| `posted` | The post sweep wrote the comment to Jira. The state is terminal; Meridian records the Jira worklog ID alongside the row. |
| `skipped` | You dismissed the draft. It will not be posted and the hour is considered handled. |
| `failed` | The post sweep tried to write to Jira and got an error (auth, network, validation). The error is shown on the card; fix the cause and re-approve to retry. |

Approval is the **only** way a worklog reaches your tracker. A driver re-run can never clobber an `approved` or `posted` row, so a human decision is never silently overwritten.

<Note>
`time_spent` always comes from your actual idle-discounted activity for the hour (capped at one hour). The language model writes the comment, not the duration.
</Note>

## The Worklogs view

Open the dashboard at [http://localhost:3000](http://localhost:3000) and select **Worklogs** in the sidebar (keyboard shortcut `4`).

The header shows the day you're reviewing and a running count of drafts, approved, and posted worklogs. Use the `←` / `→` arrows to step through previous days; you can't step past today.

Each worklog is rendered as a card containing:

- The ticket key, the hour the worklog covers, and the time spent in that hour.
- A confidence ring and any risk flags raised during synthesis (e.g. `low_confidence`, `thin_evidence`).
- The editable comment that will be posted to Jira.
- Optional supporting detail — the underlying bullets and suggested next steps — collapsed by default behind **show supporting detail**.
- The current state badge (`Draft`, `Approved`, `Posted`, `Dismissed`, `Failed`).

The view polls every 30 seconds, so when the post sweep flips an `approved` row to `posted` (or `failed`) you'll see it update without reloading.

## Approving, editing, and dismissing drafts

Each card on a non-posted worklog has three actions:

<CardGroup cols={3}>
<Card title="Approve → post" icon="check">
Marks the worklog as `approved`. The post sweep picks it up within about 60 seconds and writes it to Jira.
</Card>
<Card title="Edit" icon="pen">
Opens the comment in an inline textarea. Saving re-drafts the worklog (you'll need to approve it again before it posts). The `edited` badge appears on cards you've touched.
</Card>
<Card title="Dismiss" icon="xmark">
Marks the worklog as `skipped` after asking *where the time should have gone* — see [Dismissing a draft and correcting attribution](#dismissing-a-draft-and-correcting-attribution).
</Card>
</CardGroup>

If a card is already `approved` but hasn't posted yet, the primary button becomes **Hold (un-approve)** so you can pull it back before the next sweep.

When every draft on the page looks good, use **Approve all N drafts** at the top of the view to approve them in one click. Empty drafts (no synthesised comment) are skipped automatically.

## Dismissing a draft and correcting attribution

When you click **Dismiss** on a draft, the card expands into an attribution picker asking *"Where should this time have gone?"*. You're not just rejecting the worklog — you're labelling what the classifier got wrong. Pick one of:

- **Another open ticket** — the work belonged to a different task. Meridian records the corrected `task_key` as the ground truth for that hour.
- **Untracked / personal** — the activity wasn't billable work at all. Meridian records that the hour should not have produced any worklog.
- **Just dismiss — not sure** — you don't have a clear answer; the worklog is dismissed with no attribution label.

After choosing, click **Dismiss worklog** to confirm. The worklog moves to `skipped` and the correction is stored alongside the existing edit history as immutable review feedback.

Every approve, reject, and un-approve action is recorded the same way — not just edits — so Meridian retains a complete history of how each draft was reviewed even though only the latest state is shown on the card. This feedback feeds the classifier evaluation pipeline; over time, drafts you reject with corrections help Meridian get attribution right on similar sessions.

<Note>
No external system is told about the correction — it's stored locally in your Meridian database for evaluation only.
</Note>

## The post sweep

A background sweep in the daemon runs approximately every 60 seconds and posts every `approved` worklog it finds to Jira, marking each one `posted` on success or `failed` (with the error message) otherwise. This sweep is the **sole** path that writes to your tracker.

If you can't wait for the next sweep — for example, you just approved a batch at the end of the day and want them all posted now — run:

```bash
meridian worklog-post-approved
```

The command runs exactly the same sweep on demand and exits when it's done.

## Inspecting the day from the CLI

For a no-SQL summary of a given day, including hours done, pending, and stuck, plus rows grouped by state and a flagged list of low-confidence or risk-flagged drafts to inspect first, use:

```bash
meridian worklog-status --day 2026-05-30
```

Omit `--day` to see today.

## Configuration

The PM-worklog stage runs inside the Rust daemon and uses the MLX server's synthesis endpoint to write each draft comment. Beyond connecting Jira, you typically don't need to set anything. A few optional tunables in `~/.meridian/.env`:

| Variable | Default | Purpose |
|---|---|---|
| `PM_WORKLOG_INTERVAL_HOURS` | `1.0` | How often the driver runs a drafting pass. Clamped to a 60-second floor. |
| `PM_WORKLOG_MIN_CONFIDENCE` | `0.65` | Below this, drafts are flagged `low_confidence` for extra scrutiny. |
| `PM_WORKLOG_READINESS_AGING_MIN` | `90` | Maximum minutes the driver will wait for an hour to settle before drafting anyway. |

<Note>
There is no environment variable that enables automatic posting. The previous `PM_WORKLOG_POST_ENABLED` switch has been removed — approval in the Worklogs view is the only gate.
</Note>

## Troubleshooting

<AccordionGroup>
<Accordion title="No worklogs appear in the view">
Drafts are produced once an hour by the daemon and only for tickets you have classified sessions against. Confirm with:

```bash
meridian worklog-status
```

If `hours done` is `0`, check that `meridian status` shows the daemon and MLX server running, and that Jira is connected (`meridian doctor`).
</Accordion>
<Accordion title="A worklog stays in 'Approved' and never becomes 'Posted'">
The post sweep runs roughly every 60 seconds. If a row hasn't posted after a minute or two, run the sweep manually to surface any error:

```bash
meridian worklog-post-approved
```

If the row flips to `Failed`, the card will display the underlying Jira error. Fix the cause (commonly a stale API token or a closed ticket) and approve again to retry.
</Accordion>
<Accordion title="A draft is empty">
If synthesis couldn't produce a useful comment — for example, the hour had too little signal — the draft renders as `(empty — nothing to post; edit to add a comment)`. Approve is disabled for empty drafts; either edit the comment yourself or dismiss the card.
</Accordion>
</AccordionGroup>
43 changes: 43 additions & 0 deletions reference/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,49 @@ Without Screen Recording permission, screenpipe cannot capture frames and Meridi

</Accordion>

<Accordion title="meridian pm-worklog [--day YYYY-MM-DD]">

```bash
meridian pm-worklog [--day YYYY-MM-DD]
```

Runs one pass of the PM-worklog drafter on the given day (defaults to today). For each ticket with classified sessions in a settled hour, Meridian collects the activity, calls the MLX server's synthesis endpoint, grounds the result against the evidence, and writes a `drafted` row to the database. **This command never posts to Jira** — every draft awaits approval in the dashboard's Worklogs view.

The hourly daemon driver runs the same logic automatically. Run this command manually when you want to backfill drafts for an earlier day or replay drafting after fixing classification.

See [Review and Approve Worklogs](/guides/worklogs) for the full approval flow.

</Accordion>

<Accordion title="meridian worklog-post-approved">

```bash
meridian worklog-post-approved
```

Runs the post sweep on demand: finds every worklog you have marked `approved` in the dashboard's Worklogs view and writes it to Jira, flipping the row to `posted` (or `failed` with the underlying error). This is the same sweep the daemon runs automatically every ~60 seconds — use this command when you want to flush approvals immediately rather than wait for the next tick.

This is the **only** path Meridian uses to write worklogs to Jira.

</Accordion>

<Accordion title="meridian worklog-status [--day YYYY-MM-DD]">

```bash
meridian worklog-status [--day YYYY-MM-DD]
```

Prints a human-readable summary of the PM-worklog stage for the given day (defaults to today), without touching SQL:

- Hours done, pending, and stuck for the day.
- Counts of worklogs by state (`drafted`, `approved`, `posted`, `skipped`, `failed`).
- A per-ticket table with the synthesised Jira comment.
- A **flagged** list of low-confidence or risk-flagged drafts to inspect before approving.

Run this command to triage the day before opening the Worklogs view.

</Accordion>

<Accordion title="meridian uninstall">

```bash
Expand Down