Inspiration

Every engineer has lived this nightmare: you change one small function, ship it, and three days later a service nobody told you about falls over in production. The post-mortem always ends with the same sentence — "We didn't know that was connected."

We were inspired by how much invisible coupling exists in real codebases. The dependency information exists (in imports, in MR history, in CI pipelines), but it's scattered across systems no human can reconcile before hitting "Merge." Manual dependency hunting takes 30+ minutes and still misses things, because grep can't see who owns the affected code, which open MRs are about to collide with you, or which pipelines are at risk.

When we discovered GitLab Orbit's knowledge graph, the idea clicked: the graph already models files, imported symbols, merge requests, users, and pipelines as connected entities. The answer to "What will this change break?" was sitting in a queryable graph the whole time. GitPulse was born to ask that question for you, in seconds, before you push.


What it does

GitPulse is an AI-powered Blast Radius Analyzer built on the GitLab Duo Agent Platform. You point it at a file or function you are about to change:

node cli.js --file utils/tax.js --function calculateTax --project-id 83678311

In seconds it produces a complete Blast Radius Report:

  • 📁 Direct dependents — every file that imports your target, via Orbit's ImportedSymbol entity
  • 🔗 Transitive dependents — files that depend on those, traced to depth ≥ 2
  • 👥 Ownership — who owns each file, resolved from the User → MergeRequest AUTHORED edge, labeled honestly as mr-authorship, inferred-from-path, or unknown
  • 🔀 Open MR collisions — in-flight MRs already touching the same files
  • ⚙️ Pipeline risk — CI/CD pipelines that will be affected
  • 📊 Deterministic risk score — LOW / MEDIUM / HIGH, computed by code, never decided by the AI
  • Suggested reviewers — the actual people to notify before you merge
  • 📋 Safe-to-merge verdict — a binary answer with enforced guardrails

Live Reports from the Real Orbit Knowledge Graph

All three reports below were generated by the GitPulse agent against the live project (id 83678311) using real Orbit graph data (data_source: orbit-remote). No mock data. No fabrication.


Report 1 — agent.js 🟢 LOW RISK

🟢 Blast Radius Report — agent.js
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Data source: orbit-remote (real graph: yes)
🟢 Risk: LOW (score: 5/100)

📁 Direct Dependents (depth 1) — 1 file
   └── cli.js  →  imports runBlastRadiusAgent from ./agent.js

🔗 Transitive Dependents (depth 2+) — 0 files
   └── cli.js is the top-level entry point; nothing imports it.

👥 Ownership (basis: MR authorship — AUTHORED edge)
   └── @altamish6589 (Altamish Mohammad) — sole author of all changes
   └── Teams affected: 0 (no CODEOWNERS / formal team data in graph)

🔀 Open MRs Touching Related Code — None ✅
   All 4 MRs that touched agent.js are resolved:
   ├── !1  — "fix: wire deterministic scoring engine..."   [merged]
   ├── !9  — "fix: prevent risk-score drift..."            [merged]
   ├── !4  — "feat: maximize hackathon submission"         [closed]
   └── !7  — "feat: add orbit_get_graph_status tool"       [closed]

⚙️  Pipeline Risk
   └── Latest pipelines on main: ✅ passing (iid 48, 49, 51)
   └── No active pipeline risk tied to an agent.js change.

📐 Score Breakdown
   +  5  direct dependents  (1 × 5)
   +  0  transitive         (0 × 2)
   +  0  teams affected     (0 × 10)
   +  0  open MR overlaps   (0 × 15)
   +  0  pipelines          (0 × 5)
   ────
   =  5/100  LOW

✅ Suggested Reviewers
   └── @altamish6589

📋 Safe to merge? ✅ YES
   One direct dependent (cli.js). No transitive fan-out.
   No in-flight MR conflicts. Green pipeline.
   Keep the cli.js ↔ runBlastRadiusAgent import contract intact.

Report 2 — cli.js 🟡 MEDIUM RISK

🟡 Blast Radius Report — cli.js
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Data source: orbit-remote (real graph: yes)
🟡 Risk: MEDIUM (score: 20/100)

📁 Direct Dependents (depth 1) — 0 files
   └── cli.js is the top-level entry point. Nothing imports it.

🔗 Transitive Dependents (depth 2+) — 0 files
   └── No downstream fan-out. Structural changes won't ripple.

👥 Ownership (basis: MR authorship — AUTHORED edge)
   └── @altamish6589 (Altamish Mohammad) — sole author
   └── Teams affected: 0

🔀 Open MRs Touching Related Code ⚠️  — 1 conflict
   └── !11 — "feat: Orbit proof, enforcing risk gate, MR-comment
              integration, and label guard"  [OPEN]
              Pipeline: ✅ success (iid 47)
              Footprint: 20 files including cli.js, report.js,
              gitlab-api.js, mr-comment.js, orbit-client.js, CI config

   Resolved (no conflict risk):
   ├── !5  [merged]
   └── !9  [merged]

⚙️  Pipeline Risk
   └── 1 MR pipeline at risk (tied to open !11)
   └── main pipelines: ✅ green

📐 Score Breakdown
   +  0  direct dependents  (0 × 5)
   +  0  transitive         (0 × 2)
   +  0  teams affected     (0 × 10)
   + 15  open MR overlaps   (1 × 15)
   +  5  pipelines          (1 × 5)
   ────
   = 20/100  LOW band — but safe_to_merge forced ❌
             by open-MR guardrail (overrides score band)

✅ Suggested Reviewers
   └── @altamish6589 (author of cli.js AND of the overlapping !11 —
       holds full context for sequencing both changes)

📋 Safe to merge? 🚫 NO
   Zero downstream dependents — a change here won't ripple.
   But open MR !11 is in-flight and rewrites cli.js.
   Rebase on or sequence after !11, or merge !11 first.

Report 3 — orbit.js 🟡 MEDIUM RISK

🟡 Blast Radius Report — orbit.js
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Data source: orbit-remote (real graph: yes)
🟡 Risk: MEDIUM (score: 37/100)

📁 Direct Dependents (depth 1) — 1 file
   └── agent.js  →  imports orbitQueryDependents, orbitGetOwners,
                    orbitGetGraphStatus from ./orbit.js

🔗 Transitive Dependents (depth 2) — 1 file
   └── cli.js (depth: 2, via: agent.js)
       imports runBlastRadiusAgent from ./agent.js

   Full chain: cli.js → agent.js → orbit.js
   Any change to orbit.js's exported function signatures
   propagates straight to the agent loop and the CLI entry point.

👥 Ownership (basis: MR authorship — AUTHORED edge)
   └── @altamish6589 (Altamish Mohammad) — sole author of orbit.js,
       agent.js, and cli.js
   └── Teams affected: 1 (single-owner project)

🔀 Open MRs Touching Related Code ⚠️  — 1 conflict
   └── !11 — "feat: Orbit proof, enforcing risk gate, MR-comment
              integration, and label guard"  [OPEN]
              Touches cli.js (transitive dependent of orbit.js),
              plus report.js, orbit-client.js, .gitlab-ci.yml

⚙️  Pipeline Risk
   └── Recent MR pipelines: iid 58, 56, 53, 52, 50, 47 ✅ success
   └── iid 44, 45 failed on refs/merge-requests/11/head (now resolved)
   └── Next orbit.js pipeline executes against same CI surface

📐 Score Breakdown
   +  5  direct dependents  (1 × 5)
   +  2  transitive         (1 × 2)
   + 10  teams affected     (1 × 10)
   + 15  open MR overlaps   (1 × 15)
   +  5  pipelines          (1 × 5)
   ────
   = 37/100  MEDIUM

✅ Suggested Reviewers
   └── @altamish6589

📋 Safe to merge? 🚫 NO
   orbit.js drives agent.js → cli.js directly.
   Open MR !11 is in-flight and touches a dependent file (cli.js).
   Coordinate and land/rebase against !11 before merging.

What makes GitPulse different

The "what depends on this code" space is crowded, but each existing tool solves only one slice. The three live reports above demonstrate what none of them can do alone — multi-signal fusion across dependency graph, ownership, open MRs, and pipelines in one deterministic verdict:

Capability Static analyzers CODEOWNERS / blame AI code Q&A GitPulse
Transitive dependency traversal ⚠️ approximate ✅ depth ≥ 2
Owner mapping ✅ from MR history
In-flight open-MR collision
Pipeline risk ⚠️ build targets only
Deterministic risk score ⚠️ static only ✅ formula + guardrails
safe_to_merge verdict
Honest data provenance

GitPulse also runs as a CI/CD gate — posting the report as an idempotent MR comment and failing the pipeline when risk is HIGH, before anything reaches production.

How we built it

We built GitPulse as small, single-responsibility ESM modules in Node.js 18+, depending on nothing but @anthropic-ai/sdk and dotenv:

  • cli.js — entry point, argument parsing, and enforcing CI exit-code gates (--require-orbit, --fail-on, --strict)
  • agent.js — a Claude agentic loop (claude-sonnet-4-6) that decides what to investigate, exposing four tools
  • orbit.js + orbit-client.js — dependency traversal and ownership, querying Orbit over REST (POST /api/v4/orbit/query)
  • report.js — the deterministic scoring engine and report builder
  • mr-comment.js — idempotent MR-note posting via a hidden marker

The pivotal architectural decision was separating the agent from the scoring. The LLM decides what to look at, but the risk formula lives entirely in report.js:

score = (direct_dependents     × 5)
      + (transitive_dependents × 2)
      + (teams_affected        × 10)
      + (open_mr_overlaps      × 15)
      + (pipeline_count        × 5)

LOW < 30  |  MEDIUM 30–60  |  HIGH > 60  (capped at 100)
3+ teams always forces HIGH regardless of score

The same input always yields the same score, whether or not an API key is present. When ANTHROPIC_API_KEY is set, the model drives the tool calls; when absent, GitPulse runs the same four tools deterministically with no LLM. We validated everything against the live graph in CI — an orbit-proof job runs --require-orbit and uploads orbit-report.json as hard proof the real graph answered.


Challenges we ran into

The biggest challenges were all about talking to a live knowledge graph we'd never used before:

  • Schema discovery by trial and error — Orbit rejected queries with HTTP 400 until we learned the exact column names. ImportedSymbol uses identifier_name, not name. MergeRequest queries reject an explicit column allowlist and need default columns. Filters require the { "op": "eq", "value": ... } form. Each rejection was a clue, and we baked those learnings directly into the README.

  • Normalizing inconsistent response shapes — Orbit returns graph-shaped ({ result: { nodes: [...] } }), tabular, and alias-prefixed responses (e.g. imp_file_pathfile_path). We wrote a dedicated extractRows/flattenNode parser to normalize all three shapes defensively.

  • Auth in CICI_JOB_TOKEN can be restricted for the Orbit endpoint, so we had to support a masked api-scoped GITLAB_TOKEN and centralize transport in gitlab-api.js.

  • Honest fallback without lying — we refused to silently emit demo data, so we built a three-tier provenance chain (orbit-remotestatic-analysismock-fallback) where every report declares its own data source.

  • Bounding traversal cost — unbounded transitive traversal explodes, so we cap expansion to the first 5 direct dependents at depth 2.


Accomplishments that we're proud of

  • Deterministic, auditable scoring — the verdict is reproducible and never left to a stochastic model
  • Honest data provenance — GitPulse never passes mock data off as a real graph trace; a loud ⚠️ MOCK DATA banner appears and safe_to_merge is forced to false whenever data isn't real
  • Runs anywhere — no API key required (deterministic mode), no glab binary required (pure REST transport)
  • Real guardrails enforced in code, not prompts — minimum depth 2, no silent file drops, 3+ teams → HIGH, open-MR overlap → never safe
  • End-to-end CI proof — a green orbit-proof artifact confirming data_source: orbit-remote against our own project

What we learned

  • Knowledge graphs beat grep — Orbit gave us cross-cutting context (ownership, open MRs, pipelines) that no import-only static analyzer can reach
  • LLMs should orchestrate, not adjudicate — letting the model choose tools while a deterministic engine computes the verdict gave us both flexibility and reproducibility
  • Provenance is a feature — being explicit about whether data is real, statically derived, or mocked turned a weakness (fallbacks) into a trust signal
  • Fail gracefully, loudly — every external call needed a fallback path, but a fallback should never be mistaken for the real thing

What's next for GitPulse

  • Native Duo Agent Platform skill published to the AI Catalog so any team can enable it from Explore > AI Catalog
  • CODEOWNERS-based ownership as a first-class signal alongside MR-authorship inference
  • Deeper transitive traversal with smarter cost-bounding (priority by import frequency rather than first-5)
  • Historical risk calibration — tuning the scoring weights against real incident data to predict actual breakage probability
  • Richer MR integration — inline diff annotations and per-team notifications instead of a single MR note
  • Language coverage beyond JS/Python import graphs in the static-analysis fallback

Built With

  • anthropic-claude
  • claude-sonnet-4-6
  • dotenv
  • esm-modules
  • gitlab-ci/cd
  • gitlab-duo-agent-platform
  • gitlab-orbit
  • gitlab-rest-api
  • javascript
  • node.js
Share this project:

Updates