Skip to content

Docs audit — observability (requests page, analytics) — 2026-08-10 #54

Closed

Description

@SebConejo

Audited: the observability surface of mnfst/manifest (analytics dashboard, request drawer, spend limits/alerts, usage API) across merged PRs incl. #2374, #2456, #2485, #2502, #2508, #2535, #2609, #2642, #2666 over ~45 days, against the docs at 692a3fd. Scope: observability.mdx only.

Findings

1 · [WRONG] The self-hosted email setup tells you to configure a provider on the Limits page, but that form does not exist. Environment variables are the only way.

Page: https://manifest.build/docs/observability (Spend alerts, Email delivery, Self-hosted view)

What actually happens:

  1. On a self-hosted install you want alert emails.
  2. You open the harness Limits page. It shows one read-only line: the address alerts will go to. There is no provider picker, no API key, domain, or address field.
  3. The only way to wire up email is environment variables: EMAIL_PROVIDER (resend, mailgun, or sendgrid), EMAIL_API_KEY, EMAIL_FROM, and EMAIL_DOMAIN for Mailgun.
  4. With no provider set, alerts are skipped. Blocking rules still work.

Options:

  1. Rewrite the block so the EMAIL_* env vars are the only path, and say the Limits page just shows where alerts go. Recommended.
  2. Same, and state outright that there is no in-app email form, to kill the expectation.

Suggested wording (option 1), replacing the self-hosted block at observability.mdx:96-102:

<div data-deploy="selfhosted">

Set an email provider with the [`EMAIL_*` environment variables](/reference/environment-variables): `EMAIL_PROVIDER` (`resend`, `mailgun`, or `sendgrid`), `EMAIL_API_KEY`, `EMAIL_FROM`, and `EMAIL_DOMAIN` for Mailgun. The **Limits** page shows the address alerts go to.

Without an email provider, alerts are skipped. Blocking rules still work, since they don't need to send anything.

</div>
Evidence (for the agent)
  • observability.mdx:98 — "Configure a provider on the Limits page: Resend, Mailgun, or SendGrid. Enter the API key, domain, and notification address, then save. You can set the same thing with the EMAIL_* environment variables."
  • Code: EmailProviderSetup (the root email-config component) has zero JSX usages anywhere in packages/frontend/src; EmailProviderModal is referenced only from inside that never-mounted EmailProviderSetup (EmailProviderSetup.tsx:48), so nothing renders either. Last touched by the revert "per-user email provider configuration for notifications (#821)". pages/Limits.tsx:213 renders only the read-only <CloudEmailInfo> ("Alerts will be sent to "), for every deployment.
  • Code: notifications/services/email-providers/send-email.ts:30-59 reads EMAIL_PROVIDER/EMAIL_API_KEY/EMAIL_DOMAIN/EMAIL_FROM (legacy MAILGUN_* still honored); with no provider it logs "skipping email send" and returns.
  • Reference already carries the rows: reference/environment-variables.mdx:101-104 (EMAIL_PROVIDER, EMAIL_API_KEY, EMAIL_DOMAIN, EMAIL_FROM), so the fix keeps coherence.
  • A self-hosted operator hunts the Limits page for a picker that is not there and never gets alert emails.

2 · [WRONG] The limit-rule "Action" row reads "Email you, block further requests, or both", as if block-only is selectable. It is not: every rule emails, and blocking is an optional toggle.

Page: https://manifest.build/docs/observability (Spend alerts, the rule-fields table)

What actually happens:

  1. You open the Limits page and create a rule.
  2. You set the metric, threshold, and period.
  3. Every rule emails you when the threshold is crossed.
  4. A single toggle, "Block requests when exceeded", optionally also blocks. There is no silent-block (block without email) choice.

Options:

  1. Reword: a rule always emails, and blocking is an optional add-on toggle. Recommended.
  2. Leave as-is: every combination described exists in some form; only silent-block is unreachable.

Suggested wording (option 1). Action row at observability.mdx:67:

| **Action** | Every rule emails you. Turn on blocking to also stop requests over the threshold. |

The "Configure it" step at observability.mdx:79:

    Pick the metric, threshold, and period. Every rule emails you; turn on **Block requests when exceeded** to also stop requests over the threshold.
Evidence (for the agent)
  • observability.mdx:67 — "| Action | Email you, block further requests, or both |" (echoed at :79 "whether it should notify, block, or both").
  • Code: components/LimitRuleModal.tsx:25 actionValue = () => (blockEnabled() ? 'both' : 'notify'); :150 the single toggle "Block requests when exceeded"; :98 copy "You'll receive an email alert when usage exceeds the threshold." The block-only enum value exists in the DTO but the UI never emits it.
  • A user who wants a silent hard-block expects to select "block further requests" alone; the UI always emails.

3 · [MISSING-CASE] On the Free cloud plan the dashboard only shows 7 days of history; the 30, 90, and 365-day ranges are Pro, and the page never says so.

Page: https://manifest.build/docs/observability (The dashboard, Overview)

What actually happens:

  1. A Free cloud user opens the Overview.
  2. The 24h and 7d ranges work.
  3. The 30-day, 90-day, and 365-day ranges are disabled.
  4. Nothing on the page says this is a plan limit, so it reads like a bug.

Options:

  1. Add one cloud-only line: the Free plan covers up to 7 days; longer ranges need Pro. Recommended.
  2. Leave as-is: the page never promises specific ranges, so there is no direct contradiction.

Suggested wording (option 1), inserted right after the </CardGroup> at observability.mdx:44:

<div data-deploy="cloud">

On the Free plan, the Overview covers up to 7 days of history; the 30-, 90-, and 365-day ranges need Pro.

</div>
Evidence (for the agent)
  • observability.mdx:32-34 — Overview card: "Spend, tokens, and request volume over time, across all harnesses or one." No range mention anywhere on the page.
  • Code: pages/Overview.tsx:50 PRO_RANGES = new Set(['30d','90d','365d']); :131 usePlanRangeLock(...); :193 clamp isFreePlan() && PRO_RANGES.has(range()) back to 7d; services/plan-range-lock.ts:27-28. Same lock on the Requests log (pages/MessageLog.tsx:87,267). 24h and 7d are free.
  • A Free cloud user sees greyed ranges with no explanation.

4 · [IMAGE] The "The dashboard" section names four surfaces in prose but shows no screenshot of the dashboard.

Page: https://manifest.build/docs/observability (The dashboard)

Place one screenshot directly under the four-card grid in the "The dashboard" section: the harness Overview tab, showing the KPI tiles (tokens, cost, reliability) and the spend-over-time chart, so a new reader recognizes the landing surface the four cards describe.

Evidence (for the agent)
  • observability.mdx:29-44 — the ## The dashboard CardGroup (Overview / Requests / Providers / Limits) is prose only.
  • Code: pages/Overview.tsx — the real rendered Overview (KPI cards + the unified chart with requests/selfheal/cost/tokens tabs + CostByModelTable + MessageTable).

Reply with the finding number and the chosen option
(e.g. "3 → option 2", or "3: other idea, let's discuss").
PRs are then made manually with the agent, never by the harness.
Rejected findings: reply "N → reject" and they will never be re-raised.

Activity

  1. added
    docs-syncFiled by the docs-sync harness
    wrongDocs say it incorrectly
    missing-caseA user situation is not covered
    imageAn illustration would help
    on Aug 10, 2026
  2. SebConejo commented on Aug 15, 2026

    @SebConejo
    MemberAuthor

    Traitement en lot (2026-08-15) : findings 1, 2, 3 → option 1 chacun, appliqués dans la PR #63. Finding 4 (screenshot) reste ouvert en attendant une capture du dashboard.

  3. added 2 commits that reference this issue on Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    docs-syncFiled by the docs-sync harnessimageAn illustration would helpmissing-caseA user situation is not coveredwrongDocs say it incorrectly

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions