Skip to content

Repository files navigation

Jack Ryan

Your codebase has secrets. Jack Ryan finds them before they find you.

Read the full write-up on Medium: Building a Background Agent That Monitors Codebases for Risk Indicators

Jack Ryan is a silent background agent that monitors your engineering team's GitHub and Jira activity around the clock. It spots the risk patterns that humans miss or notice too late — and delivers targeted alerts directly to Slack, at the moment they matter.

No dashboards. No queries. No waiting to be asked.

It just watches. And when something is wrong, it tells you.


The problem

Incidents don't come out of nowhere. The signals are always there:

  • A PR quietly touching a file that's caused three outages
  • A 600-line change with no ticket, no review trail, no rollback plan
  • Two engineers unknowingly editing the same critical module
  • A ticket marked Done that was never actually code-reviewed
  • Half the sprint converging on the same service with nobody coordinating

By the time anyone notices, it's too late. Jack Ryan notices first.


What it detects

Signal 1 — PR touches a historically incident-linked file

Some files break repeatedly. Jack Ryan tracks which files have appeared in past incidents and flags any new PR that touches them — before it merges.

[Risk] PR #42 "Update settlement logic" touches 2 file(s) linked to past incidents:
settlement_engine.py (3 incidents), position_calculator.py (1 incident).
Review carefully before merge.

Signal 2 — Large change with no linked ticket

A PR with 300+ lines changed and no Jira ticket has no review trail, no sprint visibility, and no rollback context. High blast radius, zero traceability.

[Risk] PR #67 "Refactor auth flow" has 847 lines changed but no linked Jira ticket.
Large untracked changes carry rollback risk.

Signal 3 — Multiple PRs modifying the same file

Merge conflicts are annoying. Conflicting logic that merges cleanly is dangerous. Jack Ryan catches simultaneous edits before they hit main — one alert per file, all PRs listed.

[Risk] 4 open PRs are all modifying src/order_router.py:
PR #2 "Add retry logic", PR #3 "Fix timeout handling",
PR #7 "Fix settlement routing", PR #8 "Add metrics".
Coordinate before merging.

Signal 4 — Ticket marked Done with no pull request

Work that's marked done without a PR either wasn't reviewed, was committed directly, or was never implemented. All three are worth knowing about.

[Risk] JRT-204 "Migrate rate limiter config" was marked Done but has no linked pull request.
Was this change code-reviewed?

Signal 5 — Sprint overloaded on the same service

Parallel work on the same service from different contributors is a coordination risk — especially across team boundaries. Small changes can interact in ways nobody anticipated.

[Risk] 4 tickets in the current sprint all touch the payments service:
JRT-188, JRT-192, JRT-199, JRT-203. Confirm the team is coordinating.

When a risk clears — a PR merges, a ticket gets a PR link, the overlap resolves — Jack Ryan posts a [Resolved] message and moves on. It never re-alerts on the same condition while it's still active.


How it works

Every 15 minutes:

  GitHub ──────────────┐
                       ├──▶ 5 signal detectors ──▶ compare vs DB ──▶ Slack
  Jira ────────────────┘
  1. Fetch all open PRs and their file diffs from GitHub
  2. Fetch active sprint tickets, recently Done tickets, and incident tickets from Jira
  3. Run all 5 signal detectors against the data
  4. Compare against the active signals database
    • New condition → post alert, record in DB
    • Condition cleared → post [Resolved], remove from DB
    • Condition unchanged → stay silent
  5. Repeat

State is stored locally in SQLite. No external services. No dependencies beyond the APIs it already monitors.


Requirements

  • Python 3.10+
  • GitHub personal access token (read-only)
  • Jira Cloud account with a Scrum board (Kanban boards have no sprints — Signal 5 won't work)
  • Slack workspace with a bot app

Installation

git clone https://github.com/parambhatia2004/jack-ryan
cd jack-ryan
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Setup

Run the interactive setup script — it walks through each credential, explains where to find it, and tests all three connections before writing .env:

python setup.py

The script will:

  1. Prompt for GitHub token, org, and repo
  2. Prompt for Jira credentials and board ID
  3. Prompt for Slack bot token and channel
  4. Test all three connections live
  5. Write .env if everything passes

Credentials (manual alternative)

If you prefer to configure manually, copy .env.example to .env and fill in each value:

cp .env.example .env

GitHub token

  1. Go to github.com → Settings → Developer settings → Fine-grained tokens
  2. Generate new token, select your target repo
  3. Set permissions: Pull requests: Read-only, Contents: Read-only
  4. Paste into GITHUB_TOKEN

Jira API token

  1. Go to id.atlassian.com/manage-profile/security/api-tokens
  2. Create API token, paste into JIRA_API_TOKEN
  3. Set JIRA_EMAIL, JIRA_BASE_URL, JIRA_PROJECT_KEY
  4. Set JIRA_BOARD_ID — find it in your board URL: .../boards/3434

Note: Jack Ryan uses the Jira Agile API, which requires a board ID. If you get 410 errors, your project is likely team-managed — setting JIRA_BOARD_ID fixes this.

Slack bot

  1. Go to api.slack.com/apps → Create New App → From scratch
  2. Add the chat:write scope under OAuth & Permissions
  3. Install to workspace, paste the xoxb-... token into SLACK_BOT_TOKEN
  4. Create a channel, invite the bot: /invite @Jack Ryan
  5. Set SLACK_CHANNEL

Configuration

# GitHub
GITHUB_TOKEN=
GITHUB_ORG=
GITHUB_REPO=

# Jira
JIRA_BASE_URL=https://yourorg.atlassian.net
JIRA_EMAIL=
JIRA_API_TOKEN=
JIRA_PROJECT_KEY=
JIRA_BOARD_ID=

# Slack
SLACK_BOT_TOKEN=
SLACK_CHANNEL=#jack-ryan

# Thresholds (defaults shown)
POLL_INTERVAL_MINUTES=15
LARGE_DIFF_THRESHOLD=300
FILE_RISK_SCORE_THRESHOLD=2
SPRINT_SERVICE_OVERLAP_THRESHOLD=3
SIGNAL_4_GRACE_PERIOD_HOURS=2

# Signal 3: files to ignore in simultaneous edit detection
EXCLUDED_FILE_PATTERNS=*.lock,*.md,*.json,*.yaml,*.yml,*.env,*.ambr,tests/*

# Signal 5: service names to match against sprint ticket titles
KNOWN_SERVICES=payments,auth,orders,data-pipeline

# Signal 1: Jira issue types treated as incidents
JIRA_INCIDENT_ISSUE_TYPES=Bug,Incident

Running

source .venv/bin/activate
python agent.py

Jack Ryan validates all three API connections on startup and logs the result before the first cycle runs. One cycle runs immediately, then every POLL_INTERVAL_MINUTES. Logs go to stdout. Alerts go to Slack.

To stop:

pkill -f "agent.py"

Always kill the existing process before restarting. Multiple instances will post duplicate alerts. Verify with pgrep -f "agent.py".


Docker

No Python or venv required — just Docker.

First time:

git clone https://github.com/parambhatia2004/jack-ryan
cd jack-ryan
cp .env.example .env
# fill in .env with your credentials
docker compose up -d

View logs:

docker compose logs -f

Update to latest version:

git pull && docker compose up --build -d

Stop:

docker compose down

State persists in a data/ directory on the host via a volume mount — survives container restarts and rebuilds.


Signal details

Signal 1 — Incident-linked files

Jack Ryan builds a file risk table automatically: when it sees a Jira Bug or Incident ticket linked to a GitHub PR, it records every file that PR touched. Over time this becomes a map of your most dangerous files.

On day one the table is empty. Seed it with files you already know are risky:

source .venv/bin/activate
python3 -c "
import db; db.setup()
db.increment_file_risk_score('src/payments.py')
db.increment_file_risk_score('src/payments.py')  # twice = score of 2, hits default threshold
"

Signal 2 — Large untracked change

Checks PR title, description, and branch name for a ticket pattern (PROJ-123). A PR named feature/JRT-10-refactor or with JRT-10 anywhere in its description is correctly excluded.

Signal 3 — Simultaneous edits

One alert per file, listing all PRs touching it. Files matching EXCLUDED_FILE_PATTERNS are skipped — add your own patterns for generated files, fixtures, or version files specific to your codebase. Defaults exclude lock files, config files, test files, and snapshot files.

Signal 4 — Done ticket, no PR

PR links are detected via Jira's remote links API. If your team uses the GitHub for Jira integration, these are created automatically when a PR references the ticket key. Without the integration, a developer can paste the PR URL into the ticket.

The 2-hour grace period accounts for the natural delay between merging a PR and marking the ticket Done. Resolution only fires when a PR link is genuinely found — not when the ticket simply ages out of the query window.

Signal 5 — Sprint coordination risk

Matches KNOWN_SERVICES keywords against ticket titles (case-insensitive substring match). This signal is disabled if KNOWN_SERVICES is not set, and requires an active Scrum sprint.


Deduplication

State is stored in jack_ryan.db. A signal fires once when a condition appears and resolves when it clears. To force everything to re-fire:

python3 -c "
import sqlite3
with sqlite3.connect('jack_ryan.db') as conn:
    conn.execute('DELETE FROM active_signals')
"

Production deployment

Docker (recommended):

docker compose up -d

systemd:

[Unit]
Description=Jack Ryan
After=network.target

[Service]
WorkingDirectory=/path/to/jack-ryan
ExecStart=/path/to/jack-ryan/.venv/bin/python3 agent.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Troubleshooting

Problem Fix
No Slack messages Invite the bot to the channel: /invite @Jack Ryan
Jira 410 errors Set JIRA_BOARD_ID — find in board URL .../boards/3434
Signal 1 never fires Seed the risk table manually (see above)
Signal 5 never fires Set KNOWN_SERVICES and ensure a sprint is active
Duplicate alerts Kill all instances with pkill -f "agent.py" before restarting
Signals re-fire after restart Expected if jack_ryan.db was deleted — all conditions fire once on next cycle
Too many Signal 3 alerts Add directory patterns to EXCLUDED_FILE_PATTERNS, e.g. tests/*,*/migrations/*

About

A background agent that monitors GitHub and Jira for engineering risk signals and delivers alerts to Slack before they become incidents.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages