Skip to content

nabaz/pulsar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pulsar

Phoenix-aware engineering-intelligence layer: call-graph + AST analysis.

Credo, Dialyzer, and mix xref cover the linting / type / module-graph layer for Elixir, but nobody stitches their signals into a higher-level engineering view. Pulsar builds a function-level call graph (via a compile tracer) and joins it with source-AST structural metrics, then runs analyzers that surface things a file-by-file linter structurally can't:

  • Fat contexts — domain modules grown past size/public-API thresholds, reported with their fan-in (blast radius) from the call graph.
  • Boundary violations — web-layer modules (controllers/LiveViews) calling *.Repo or another context's internals directly, instead of through a context. Rules are configurable.
  • Dead public functions — public functions with zero callers anywhere in the project, excluding framework entrypoints (router-dispatched controller actions, LiveView/GenServer/Oban callbacks, Mix tasks).

Each run also produces a 0–100 health score per module and for the project, and a stable, versioned JSON document designed as the ingest feed for a future hosted "Observatory".

Installation

Add Pulsar as a dev/test dependency of your Phoenix app — the tracer must run during your app's own mix compile:

def deps do
  [
    {:pulsar, "~> 0.1.0", only: [:dev, :test], runtime: false}
  ]
end

Usage

mix pulsar                   # grouped, colorized terminal report
mix pulsar --format json     # stable JSON schema (Observatory feed)
mix pulsar --only boundary   # run a single analyzer (fat_context|boundary|dead_code)
mix pulsar --max-findings 0  # exit non-zero past a budget — a CI gate

Configuration

Drop a .pulsar.exs at your project root (see .pulsar.exs.example):

[
  fat_context_public_fns: 25,
  fat_context_loc: 1500,
  boundary_rules: [
    [from: "Web", to: "Repo", message: "web layer should go through a context, not Repo directly"]
  ],
  ignore_modules: [],
  max_findings_budget: :infinity
]

A boundary rule fires on a call edge when some segment of the caller module name contains from and some segment of the callee module name contains to.

Notes & limitations

  • The call graph captures statically resolvable calls from .ex sources. Functions reached only via runtime dispatch (apply/3, framework-invoked behaviour callbacks) or from .heex templates (Phoenix function components like <.table>) have no traceable caller, so dead-code analysis is strongest on an application's internal/context modules — callers that live only in test/, in templates, or behind dynamic dispatch are not edges. Framework entrypoints (controller actions, LiveView/OTP/Oban callbacks, Mix tasks, use-target helper modules) are all-listed to avoid false positives.
  • The JSON schema is versioned (schema_version); treat it as the stable contract.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages