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:
- On a self-hosted install you want alert emails.
- 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.
- 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.
- With no provider set, alerts are skipped. Blocking rules still work.
Options:
- Rewrite the block so the
EMAIL_* env vars are the only path, and say the Limits page just shows where alerts go. Recommended.
- 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:
- You open the Limits page and create a rule.
- You set the metric, threshold, and period.
- Every rule emails you when the threshold is crossed.
- A single toggle, "Block requests when exceeded", optionally also blocks. There is no silent-block (block without email) choice.
Options:
- Reword: a rule always emails, and blocking is an optional add-on toggle. Recommended.
- 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:
- A Free cloud user opens the Overview.
- The 24h and 7d ranges work.
- The 30-day, 90-day, and 365-day ranges are disabled.
- Nothing on the page says this is a plan limit, so it reads like a bug.
Options:
- Add one cloud-only line: the Free plan covers up to 7 days; longer ranges need Pro. Recommended.
- 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.
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.mdxonly.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:
EMAIL_PROVIDER(resend,mailgun, orsendgrid),EMAIL_API_KEY,EMAIL_FROM, andEMAIL_DOMAINfor Mailgun.Options:
EMAIL_*env vars are the only path, and say the Limits page just shows where alerts go. Recommended.Suggested wording (option 1), replacing the self-hosted block at
observability.mdx:96-102: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 theEMAIL_*environment variables."EmailProviderSetup(the root email-config component) has zero JSX usages anywhere inpackages/frontend/src;EmailProviderModalis referenced only from inside that never-mountedEmailProviderSetup(EmailProviderSetup.tsx:48), so nothing renders either. Last touched by the revert "per-user email provider configuration for notifications (#821)".pages/Limits.tsx:213renders only the read-only<CloudEmailInfo>("Alerts will be sent to "), for every deployment.notifications/services/email-providers/send-email.ts:30-59readsEMAIL_PROVIDER/EMAIL_API_KEY/EMAIL_DOMAIN/EMAIL_FROM(legacyMAILGUN_*still honored); with no provider it logs "skipping email send" and returns.reference/environment-variables.mdx:101-104(EMAIL_PROVIDER,EMAIL_API_KEY,EMAIL_DOMAIN,EMAIL_FROM), so the fix keeps coherence.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:
Options:
Suggested wording (option 1). Action row at
observability.mdx:67:The "Configure it" step at
observability.mdx:79: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").components/LimitRuleModal.tsx:25actionValue = () => (blockEnabled() ? 'both' : 'notify');:150the single toggle "Block requests when exceeded";:98copy "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.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:
Options:
Suggested wording (option 1), inserted right after the
</CardGroup>atobservability.mdx:44: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.pages/Overview.tsx:50PRO_RANGES = new Set(['30d','90d','365d']);:131usePlanRangeLock(...);:193clampisFreePlan() && 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.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 dashboardCardGroup(Overview / Requests / Providers / Limits) is prose only.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.