Skip to content

Commit ce42053

Browse files
chore: close automation gaps — Sentry MCP, route guards, test creds (#14)
- Incident Responder: replace curl + <ORG>/<PROJECT> placeholders with Sentry MCP tools (search_issues, get_sentry_resource, update_issue) - Performance Monitor: replace 'Query Sentry API' with MCP search_events - Post-Merge Verifier: add routeExists() guard so missing routes are skipped not failed; reference TEST_USER_EMAIL/TEST_USER_PASSWORD env vars for authenticated flows - docs/automations.md: sync Tweet Drafter (3x daily), Incident Responder, Performance Monitor, and Post-Merge Verifier sections with YAML changes Co-authored-by: Ona <no-reply@ona.com>
1 parent 629e671 commit ce42053

4 files changed

Lines changed: 187 additions & 82 deletions

File tree

.ona/automations/incident-responder.yaml

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@ action:
1414
steps:
1515
- agent:
1616
prompt: |
17-
You are the Incident Responder. Monitor production errors and fix them.
17+
You are the Incident Responder. Monitor production errors and triage or fix them.
18+
19+
Sentry is connected via MCP — use the Sentry tools directly (search_issues,
20+
get_sentry_resource, search_events). Do NOT use curl or the Sentry REST API.
1821
1922
## Check for New Errors
2023
21-
1. Query the Sentry API for unresolved issues created in the last 15 minutes:
22-
curl -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
23-
"https://sentry.io/api/0/projects/<ORG>/<PROJECT>/issues/?query=is:unresolved&sort=date"
24+
1. Use search_issues to find unresolved errors from the last 15 minutes:
25+
search_issues(organizationSlug, naturalLanguageQuery="unresolved errors from the last 15 minutes")
2426
2. If no new unresolved issues, stop — do nothing.
25-
3. For each new issue, read the stack trace, breadcrumbs, and affected URL.
27+
3. For each new issue, use get_sentry_resource to read the stack trace, breadcrumbs,
28+
and affected URL.
2629
2730
## Triage
2831
@@ -34,26 +37,30 @@ action:
3437
## Fix (Critical and High)
3538
3639
1. Read AGENTS.md, `.agents/conventions.md`, and `.agents/architecture.md`.
37-
Understanding the data model and component structure is essential for tracing errors.
38-
2. Reproduce the error by reading the stack trace and identifying the root cause.
39-
3. Create a branch: fix/sentry-<issue-id>-<short-description>
40-
4. Fix the root cause. Add a regression test that would have caught this error.
41-
5. If the bug reveals a missing convention (e.g., unhandled error path, missing null check
40+
2. Use get_sentry_resource with resourceType='breadcrumbs' to understand the error context.
41+
3. Read the stack trace and identify the root cause in the codebase.
42+
4. Create a branch: fix/sentry-<short-id>-<short-description>
43+
5. Fix the root cause. Add a regression test that would have caught this error.
44+
6. If the bug reveals a missing convention (e.g., unhandled error path, missing null check
4245
pattern), update `.agents/conventions.md` to prevent recurrence.
43-
6. Open a PR:
44-
- Title: fix: <description> (Sentry <ISSUE_ID>)
45-
- Body: link to the Sentry issue, root cause analysis, what was fixed, test added
46+
7. Run `pnpm lint && pnpm typecheck && pnpm test` — all must pass.
47+
8. Open a PR:
48+
- Title: fix: <description>
49+
- Body: Sentry issue link, root cause analysis, what was fixed, test added.
50+
Must include `Closes #N` referencing a GitHub issue. If no GitHub issue exists
51+
for this error, create one first with label `bug`.
4652
- Labels: bug
47-
7. Mark the Sentry issue as resolved (linked to the PR).
53+
9. Use update_issue to mark the Sentry issue as resolved.
4854
4955
## Low-severity
5056
5157
Create a GitHub Issue:
52-
- Title: bug: <description> (Sentry <ISSUE_ID>)
53-
- Body: Sentry link, stack trace summary, suggested fix
58+
- Title: bug: <description> (Sentry <SHORT_ID>)
59+
- Body: Sentry issue link, stack trace summary, suggested fix
5460
- Labels: bug, priority:3, status:backlog
5561
5662
## Do NOT
5763
- Ignore errors or mark resolved without a fix.
5864
- Fix symptoms — find the root cause.
5965
- Make unrelated changes in fix PRs.
66+
- Use curl or the Sentry REST API — always use the MCP Sentry tools.

.ona/automations/performance-monitor.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ action:
2424
- db.latency_ms > 500
2525
- db.connected is false
2626
27-
2. Sentry error trend:
28-
Query Sentry API for issue count this week vs last week.
27+
2. Sentry error trend (Sentry is connected via MCP — use the tools directly):
28+
Use search_events to count errors this week vs last week:
29+
search_events(organizationSlug, naturalLanguageQuery="count of errors this week")
30+
search_events(organizationSlug, naturalLanguageQuery="count of errors last week")
2931
Flag if error count increased >50%.
3032
3133
3. Build size: run `pnpm build` and check the output for page sizes.

.ona/automations/post-merge-verifier.yaml

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,39 +36,69 @@ action:
3636
3737
## Step 3 — Run smoke tests
3838
39-
Write a Playwright script to /tmp/smoke-test.mjs:
39+
Write a Playwright script to /tmp/smoke-test.mjs.
40+
41+
The script must only test routes that exist. Before testing a route, do a HEAD
42+
request first — if it returns 404, skip that check (do not count it as a failure).
43+
44+
Test user credentials are available as env vars:
45+
TEST_USER_EMAIL, TEST_USER_PASSWORD
46+
Use these for any authenticated flows (e.g., login, dashboard access).
4047
4148
```js
4249
import { chromium } from 'playwright';
4350
4451
const BASE = 'https://memo.software-factory.dev';
4552
const failures = [];
53+
const skipped = [];
4654
const browser = await chromium.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
4755
const page = await browser.newPage();
4856
const consoleErrors = [];
4957
page.on('console', m => { if (m.type() === 'error') consoleErrors.push(m.text()); });
5058
51-
// 1. Landing page
59+
// Helper: check if a route exists before testing it
60+
async function routeExists(path) {
61+
try {
62+
const res = await fetch(BASE + path, { method: 'HEAD', redirect: 'manual' });
63+
return res.status !== 404;
64+
} catch { return false; }
65+
}
66+
67+
// 1. Landing page (always exists)
5268
const res = await page.goto(BASE, { waitUntil: 'networkidle', timeout: 15000 });
5369
if (!res || res.status() >= 400) failures.push('Landing page returned ' + (res?.status() ?? 'no response'));
5470
const title = await page.title();
5571
if (!title) failures.push('Landing page has no title');
5672
57-
// 2. Login page renders
58-
await page.goto(BASE + '/login', { waitUntil: 'networkidle', timeout: 15000 });
59-
const hasEmailInput = await page.locator('input[type=email]').count();
60-
if (!hasEmailInput) failures.push('Login page missing email input');
73+
// 2. Login page (skip if not yet built)
74+
if (await routeExists('/login')) {
75+
await page.goto(BASE + '/login', { waitUntil: 'networkidle', timeout: 15000 });
76+
const hasEmailInput = await page.locator('input[type=email]').count();
77+
if (!hasEmailInput) failures.push('Login page missing email input');
78+
} else {
79+
skipped.push('/login (not yet built)');
80+
}
6181
6282
// 3. Health endpoint
6383
const healthRes = await page.goto(BASE + '/api/health', { waitUntil: 'networkidle', timeout: 10000 });
6484
const healthBody = await page.textContent('body');
6585
if (!healthRes || healthRes.status() >= 400) failures.push('Health endpoint returned ' + (healthRes?.status() ?? 'no response'));
6686
if (healthBody && healthBody.includes('"status":"down"')) failures.push('Health endpoint reports down');
6787
68-
// 4. Console errors
88+
// 4. Dashboard (skip if not yet built — requires auth)
89+
if (await routeExists('/dashboard')) {
90+
const dashRes = await page.goto(BASE + '/dashboard', { waitUntil: 'networkidle', timeout: 15000 });
91+
// Unauthenticated should redirect to /login, not 500
92+
if (dashRes && dashRes.status() >= 500) failures.push('Dashboard returned ' + dashRes.status());
93+
} else {
94+
skipped.push('/dashboard (not yet built)');
95+
}
96+
97+
// 5. Console errors
6998
if (consoleErrors.length) failures.push('Console errors: ' + consoleErrors.slice(0, 5).join('; '));
7099
71100
await browser.close();
101+
if (skipped.length) console.log('Skipped: ' + skipped.join(', '));
72102
if (failures.length) { console.error(JSON.stringify(failures)); process.exit(1); }
73103
console.log('OK');
74104
```
@@ -82,7 +112,7 @@ action:
82112
83113
If all checks pass:
84114
Comment on the merged PR:
85-
> ✅ Post-merge verification passed — landing page, login, and health endpoint all working.
115+
> ✅ Post-merge verification passed. [list which routes were tested and which were skipped]
86116
87117
If any check fails:
88118
1. Create a GitHub Issue:
@@ -94,12 +124,11 @@ action:
94124
95125
## Expanding checks
96126
97-
As features ship, add checks to the Playwright script. Only check features that exist:
98-
- After workspace/page CRUD ships: verify /dashboard loads (unauthenticated → redirects to /login)
99-
- After editor ships: verify a page URL returns 200
100-
- After search ships: verify the search endpoint responds
127+
As features ship, add new route checks to the Playwright script. Always use the
128+
`routeExists()` guard so the script doesn't fail on routes that haven't been built yet.
101129
102-
Do NOT test flows that require authentication credentials.
130+
Test user credentials for authenticated flows:
131+
TEST_USER_EMAIL, TEST_USER_PASSWORD (available as env vars)
103132
104133
## Do NOT
105134
- Retry failed checks — report the failure and stop.

0 commit comments

Comments
 (0)