feat: add Linear + Sentry connectors, operational procedures, scheduler, and Hermes MCP server#26
feat: add Linear + Sentry connectors, operational procedures, scheduler, and Hermes MCP server#26haasonsaas wants to merge 13 commits into
Conversation
…er, and Hermes MCP server - Linear connector: GraphQL API for issues, teams, comments (read/write) - Sentry connector: issues, events, error counts for ops awareness - 4 operational procedures: weekly_progress_review, dependabot_triage, agent_pr_security_scan, deploy_readiness - ProcedureScheduler: in-process cron (5-field, minute granularity) - Hermes MCP server: stdio JSON-RPC server exposing agent-pm as MCP tools - Updated settings, connectors __init__, .env.example - Added tests for new connectors, scheduler, MCP server, procedures - 102 tests pass
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
PR SummaryHigh Risk Overview Ships four operational procedures ( Settings gain Extensive tests cover connectors, scheduler, MCP, and procedure-runner behavior (pagination, stale Linear rules, PR diffs, token/dry-run guards). Reviewed by Cursor Bugbot for commit 6560a6f. Bugbot is set up for automated code reviews on this repo. Configure here. |
haasonsaas
left a comment
There was a problem hiding this comment.
🔒 Hermes automated security scan flagged this PR.
🟠 Unsafe patterns (review):
procedures/agent_pr_security_scan.yaml— eval():- Unsafe code: eval(), exec(), subprocess with shell=True, os.system()
Automated gitleaks + pattern scan. Dismiss this review if it's a false positive.
- Fix dry_run not propagating to worker threads: check settings.dry_run before calling asyncio.to_thread in _run_model_step and _generate_plan_result - Fix calendar dry_run inflating event count to 1 instead of 0 - Fix scheduler drift: align sleep to minute boundary instead of fixed 60-second delay after each tick - Update test_mcp_run_procedure to reflect dry_run short-circuit behavior
haasonsaas
left a comment
There was a problem hiding this comment.
All three remaining Cursor Bugbot issues addressed in b0b3810:
-
dry_run ignored in threads — and now check before calling , short-circuiting with a stub. Updated test to cover both dry_run=True (skips thread) and dry_run=False (calls create_plan in thread).
-
Calendar dry_run inflates event count — now explicitly checks for in the calendar payload and returns empty events.
-
Scheduler drifts past cron minute — Replaced fixed 60-second delay with sleep-to-next-minute-boundary, keeping tick alignment regardless of procedure duration.
112 tests pass.
haasonsaas
left a comment
There was a problem hiding this comment.
All three remaining Cursor Bugbot issues addressed in b0b3810:
-
dry_run ignored in threads — _run_model_step and _generate_plan_result now check settings.dry_run before calling asyncio.to_thread, short-circuiting with a stub. Updated test to cover both dry_run=True and dry_run=False paths.
-
Calendar dry_run inflates event count — _run_calendar_scan now explicitly checks for dry_run in the calendar payload and returns empty events list.
-
Scheduler drifts past cron minute — Replaced fixed 60-second delay with sleep-to-next-minute-boundary, keeping tick alignment regardless of procedure duration.
112 tests pass.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 5 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for all 5 issues found in the latest run.
- ✅ Fixed: Linear stale scan wrong slice
- The Linear procedure scan now scopes by assignee email server-side, fetches the full matching issue set for stale scans, and sorts by oldest updates before evaluating stale work.
- ✅ Fixed: Assigned-to-me needs email config
- Assigned-to-me Linear scans now return an explicit configuration error with no results instead of silently scanning unscoped workspace issues.
- ✅ Fixed: Calendar drops earlier today
- This-week calendar scans now start at the beginning of the current day so earlier same-day and in-progress events remain in the weekly window.
- ✅ Fixed: MCP stale sweep ignores team
- The MCP stale sweep now forwards
team_idandstateinto the Linear issue query so team-scoped scans stay within the requested scope.
- The MCP stale sweep now forwards
- ✅ Fixed: MCP author search org scoped
- The MCP GitHub author search now builds repo-qualified search queries from configured repositories so PRs in repos outside the default org are included.
You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for all 3 issues found in the latest run.
- ✅ Fixed: Web diff URL breaks auth
- PR diff fetches now fall back to the authenticated API pull URL whenever GitHub's web diff URL would break token-based access.
- ✅ Fixed: MCP stale sweep misses issues
- The MCP stale sweep now paginates through all matching issues and sorts them oldest-first before evaluating staleness.
- ✅ Fixed: MCP scan hides repo failures
- The multi-repo MCP GitHub scan now raises on per-repo HTTP failures instead of silently returning partial results.
You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 4 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for all 4 issues found in the latest run.
- ✅ Fixed: Partial repo match drops defaults
- GitHub PR scans now merge explicitly mentioned repos with configured/default repo sets when the instruction asks for evalops or default repos.
- ✅ Fixed: GitHub PR scan single page
- Both procedure and MCP open-PR scans now paginate through
/pullsresults instead of stopping after the first page.
- Both procedure and MCP open-PR scans now paginate through
- ✅ Fixed: LINEAR_TEAM_IDS ignored in scans
- Linear scan helpers now scope unqualified scans across configured
LINEAR_TEAM_IDSinstead of querying the whole workspace.
- Linear scan helpers now scope unqualified scans across configured
- ✅ Fixed: MCP stale sweep incomplete rules
- MCP stale sweeps now reuse the procedure-runner stale logic, including recent-comment checks and parsed stale-day thresholds from an optional instruction.
You can send follow-ups to the cloud agent here.
| return await _run_model_step(_render_text(step.get("input", ""), context), context) | ||
| if run_name == "publish_status_digest": | ||
| params = _render_value(step.get("with", {}), context) | ||
| return await slack_client.post_digest(str(params.get("body_md", "")), params.get("channel")) |
There was a problem hiding this comment.
Slack deliver needs default channel
Medium Severity
New procedure publish_status_digest steps pass #engineering into post_digest, but SlackClient.enabled still requires SLACK_STATUS_CHANNEL. With only a bot token set, posts are treated as dry-run and never sent despite DRY_RUN=false.
Reviewed by Cursor Bugbot for commit 051e4a2. Configure here.
- MCP _github_pr_scan with author filter now paginates through search results instead of returning only the first page - _list_linear_issues_for_scan no longer shares a global remaining counter across teams; each team gets its full limit
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Scheduler skips scheduled minute
- The scheduler now evaluates the current minute before sleeping and aligns subsequent sleeps to the next minute boundary.
- ✅ Fixed: MCP GitHub scan missing token guard
- The MCP GitHub PR scan now short-circuits without network calls when no GitHub token is configured and returns an empty dry-run payload.
You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Procedure run drops step outputs
execute_procedurenow returns the accumulated procedure context so callers receive each step's outputs, and a regression test covers the behavior.
You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 4 potential issues.
There are 5 total unresolved issues (including 1 from previous review).
Reviewed by Cursor Bugbot for commit 6560a6f. Configure here.
| try: | ||
| await scheduler.start() | ||
| except Exception: # pragma: no cover - defensive startup logging | ||
| logger.exception("Failed to start procedure scheduler") |
There was a problem hiding this comment.
Scheduler runs every replica
High Severity
The procedure scheduler starts inside the FastAPI lifespan on every process that runs app. With multiple pods, uvicorn workers, or scaled containers, each instance runs the same cron jobs independently, so scheduled procedures such as Slack digests and security scans can execute repeatedly for one schedule tick.
Reviewed by Cursor Bugbot for commit 6560a6f. Configure here.
| payload["repository"] = repo | ||
| if fetch_diffs: | ||
| payload["diff"] = await _fetch_pull_request_diff(client, repo, pr, headers) | ||
| pulls.append(payload) |
There was a problem hiding this comment.
Diff fetch aborts PR scan
Medium Severity
When github_pr_scan is asked to fetch PR diffs, a failed diff HTTP request propagates out of the loop and aborts the whole scan. Other matching PRs are dropped instead of being returned with partial diff data or per-PR errors, which breaks procedures like agent_pr_security_scan on a single bad PR.
Reviewed by Cursor Bugbot for commit 6560a6f. Configure here.
| sys.stdout.write(json.dumps(response) + "\n") | ||
| sys.stdout.flush() | ||
| except json.JSONDecodeError: | ||
| continue |
There was a problem hiding this comment.
MCP parse errors stay silent
Medium Severity
The stdio MCP loop catches json.JSONDecodeError and continues without writing a JSON-RPC error response. Clients that sent a request with an id receive no reply and can hang waiting for a message that never arrives.
Reviewed by Cursor Bugbot for commit 6560a6f. Configure here.
| if last and (now - last).total_seconds() < 120: | ||
| continue | ||
| self._last_runs[name] = now | ||
| await self._run_procedure(name) |
There was a problem hiding this comment.
Restart reruns same cron minute
Medium Severity
Scheduled run deduplication uses an in-memory _last_runs map cleared on process restart. If the app restarts during a matching cron minute (for example 09:00–09:59 for 0 9 * * 1), the procedure can run again even though it already completed earlier in that minute.
Reviewed by Cursor Bugbot for commit 6560a6f. Configure here.


Summary
Pivots agent-pm from Jira-based PRD ideation to operations automation. Based on analysis of actual Hermes↔Jonathan interaction patterns: Linear (not Jira), Sentry (not blind spots), real procedures, scheduling, and MCP integration.
Changes
New connectors
Operational procedures (YAML)
weekly_progress_review— Calendar → Sentry → Linear → GitHub sweep → Slack digestdependabot_triage— scan open Dependabot PRs, auto-merge green security bumps, report skipsagent_pr_security_scan— scan recent agent PRs for leaked secrets, unsafe patterns, missing authzdeploy_readiness— cross-reference platform PRs against deploy repo for monitoring/alerting gapsScheduling
Hermes MCP server
Settings + wiring
Tests