Skip to content

feat: add Linear + Sentry connectors, operational procedures, scheduler, and Hermes MCP server#26

Open
haasonsaas wants to merge 13 commits into
mainfrom
feat/linear-sentry-operations-procedures
Open

feat: add Linear + Sentry connectors, operational procedures, scheduler, and Hermes MCP server#26
haasonsaas wants to merge 13 commits into
mainfrom
feat/linear-sentry-operations-procedures

Conversation

@haasonsaas

Copy link
Copy Markdown
Contributor

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

  • Linear connector — GraphQL API: list/read/write issues, teams, comments, stale detection
  • Sentry connector — issues, error counts, event search, tag distributions, project listing

Operational procedures (YAML)

  • weekly_progress_review — Calendar → Sentry → Linear → GitHub sweep → Slack digest
  • dependabot_triage — scan open Dependabot PRs, auto-merge green security bumps, report skips
  • agent_pr_security_scan — scan recent agent PRs for leaked secrets, unsafe patterns, missing authz
  • deploy_readiness — cross-reference platform PRs against deploy repo for monitoring/alerting gaps

Scheduling

  • In-process cron scheduler (5-field, minute granularity)
  • Schedule config for all 4 procedures

Hermes MCP server

  • Stdio JSON-RPC MCP server: 5 tools exposing agent-pm to Hermes/other agents

Settings + wiring

  • New env vars: LINEAR_API_KEY, LINEAR_TEAM_IDS, SENTRY_AUTH_TOKEN, SENTRY_ORG_SLUG, SENTRY_BASE_URL, GITHUB_REPOSITORIES
  • Updated connectors/init, .env.example

Tests

  • 13 new tests + full suite: 102 tests pass

…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
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@cursor

cursor Bot commented Jun 14, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Scheduled procedures and MCP tools can call external APIs (GitHub merges implied in YAML, Slack, Jira, Linear writes) and run LLM steps on live PR diffs and Sentry data; misconfiguration or missing dry_run could cause unintended production actions.

Overview
Adds operations automation on top of agent-pm: new Linear and Sentry connectors, a YAML procedure runner that chains calendar/Sentry/Linear/GitHub scans, model steps, Slack digests, and Jira/calendar side effects, plus an in-process cron scheduler wired into FastAPI lifespan.

Ships four operational procedures (weekly_progress_review, dependabot_triage, agent_pr_security_scan, deploy_readiness) and config/procedure_schedule.yaml to run them on a schedule. A stdio MCP server exposes procedure runs and direct scans as tools for Hermes and other agents.

Settings gain LINEAR_*, SENTRY_*, and GITHUB_REPOSITORIES, plus a task-local dry_run override so MCP/procedure dry_run does not race global config. Planning is refactored via generate_plan_for_idea (used from app.py and step-less procedures), and plans now include a plan_id. Procedure execution returns full step context, not only metadata.

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.

Comment thread agent_pm/mcp_server.py Outdated
Comment thread agent_pm/connectors/linear.py Outdated
Comment thread agent_pm/scheduler.py Outdated
Comment thread agent_pm/scheduler.py
Comment thread agent_pm/scheduler.py
Comment thread agent_pm/scheduler.py Outdated
Comment thread agent_pm/mcp_server.py Outdated
Comment thread agent_pm/mcp_server.py Outdated
Comment thread agent_pm/settings.py
Comment thread agent_pm/procedure_runner.py Outdated
Comment thread agent_pm/procedure_runner.py Outdated
Comment thread agent_pm/procedure_runner.py Outdated
Comment thread procedures/weekly_progress_review.yaml

@haasonsaas haasonsaas left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 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.

Comment thread agent_pm/procedure_runner.py
Comment thread agent_pm/procedure_runner.py
Comment thread agent_pm/scheduler.py Outdated
- 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 haasonsaas left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three remaining Cursor Bugbot issues addressed in b0b3810:

  1. 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).

  2. Calendar dry_run inflates event count — now explicitly checks for in the calendar payload and returns empty events.

  3. 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 haasonsaas left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three remaining Cursor Bugbot issues addressed in b0b3810:

  1. 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.

  2. Calendar dry_run inflates event count — _run_calendar_scan now explicitly checks for dry_run in the calendar payload and returns empty events list.

  3. 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.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_id and state into the Linear issue query so team-scoped scans stay within the requested scope.
  • ✅ 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.

Comment thread agent_pm/procedure_runner.py
Comment thread agent_pm/procedure_runner.py
Comment thread agent_pm/procedure_runner.py
Comment thread agent_pm/mcp_server.py Outdated
Comment thread agent_pm/mcp_server.py Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread agent_pm/procedure_runner.py
Comment thread agent_pm/mcp_server.py
Comment thread agent_pm/mcp_server.py Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 /pulls results instead of stopping after the first page.
  • ✅ Fixed: LINEAR_TEAM_IDS ignored in scans
    • Linear scan helpers now scope unqualified scans across configured LINEAR_TEAM_IDS instead of querying the whole workspace.
  • ✅ 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.

Comment thread agent_pm/procedure_runner.py Outdated
Comment thread agent_pm/procedure_runner.py
Comment thread agent_pm/procedure_runner.py
Comment thread agent_pm/mcp_server.py Outdated
Comment thread agent_pm/mcp_server.py Outdated
Comment thread agent_pm/procedure_runner.py
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"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread agent_pm/scheduler.py
Comment thread agent_pm/mcp_server.py
Comment thread agent_pm/mcp_server.py
Comment thread agent_pm/mcp_server.py
Comment thread agent_pm/procedure_runner.py Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_procedure now 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.

Comment thread agent_pm/procedure_runner.py Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

There are 5 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 6560a6f. Configure here.

Comment thread app.py
try:
await scheduler.start()
except Exception: # pragma: no cover - defensive startup logging
logger.exception("Failed to start procedure scheduler")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6560a6f. Configure here.

Comment thread agent_pm/mcp_server.py
sys.stdout.write(json.dumps(response) + "\n")
sys.stdout.flush()
except json.JSONDecodeError:
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6560a6f. Configure here.

Comment thread agent_pm/scheduler.py
if last and (now - last).total_seconds() < 120:
continue
self._last_runs[name] = now
await self._run_procedure(name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6560a6f. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants