Skip to content

Concepts

Baron’s whole job is to let you (or an agent) speak one abstract language and have it work correctly against very different providers. Six ideas carry that.

Work is split into independent ports, each bound to a provider on its own:

  • issues — work items / tickets.
  • scm — branches and pull requests.
  • ci — pipelines and runs (Azure Pipelines, GitHub Actions).
  • deploy — environments and deployments (Azure Environments, GitHub Environments).
  • notify — messages (Slack).
  • docs — planned (v2); binding policy.providers.docs throws DOCS_UNSUPPORTED for now.

A single install can mix providers: Linear issues + GitHub scm + Slack notify. Nothing assumes one vendor spans everything. You bind ports in policy.providers. The ci and deploy ports reuse the same provider credentials and coordinates as issues/scm — no extra env keys and no baron init step, because their status maps are vendor-fixed adapter knowledge, not something a human confirms.

Providers model status too differently for a universal schema, so Baron never speaks native states. It speaks abstract roles, and a per-provider map translates them:

  • Workflow roles: backlog → ready → in_progress → in_review → done. Blocking is NOT one of them: it is an orthogonal flag (issue.block / issue.unblock) that coexists with whatever role an item holds, so unblocking returns it to where the work actually was.
  • Type roles: initiative, epic, story, task, subtask.
  • Link types: relates, blocks, blocked_by, duplicates.

baron init introspects the provider’s real vocabulary and proposes the map; you confirm it once. A recipe that says “move to in_review” works whether that means Azure state Test + board column Test, or a GitHub in-review label.

The ci and deploy ports speak the same way, with their own normalized vocabularies parallel to the workflow roles:

  • CI RunStatus: queued | running | succeeded | failed | canceled | skipped | waiting | unknown — collapsed from each provider’s native phase + result (so an Azure Pipelines inProgress and a GitHub Actions in_progress both read as running).
  • Deploy DeployStatus: pending | running | succeeded | failed | canceled | skipped | unknown.

Unlike issue roles, these maps aren’t confirmed during init — provider CI/deploy enums are fixed and well-known, so Baron ships the translation as adapter knowledge.

Providers differ in what they can do (GitHub issues have no native hierarchy or arbitrary states; Azure Boards do). When an operation needs a capability the provider lacks, the configured gap policy decides — explicitly:

  • error — fail loudly with an actionable message (the strict default).
  • emulate:<strategy> — synthesize it (e.g. GitHub hierarchy/links via labels).
  • degrade — skip it, but always log a warning.

A gap that is neither errored nor logged is a bug. The same machinery governs every port (issue hierarchy, arbitrary states, issue links; PR drafts and threads).

  • The core exposes deterministic primitives only: issue.create, issue.transition, scm.pr.create, learning.append, … No opinion about when to use them.
  • Recipes are declarative YAML that compose primitives into a workflow (“start a task”, “open a PR and move it to review”). All workflow opinion lives here, editable, outside the code.

This keeps the provider abstraction testable and stable while teams customize process freely.

Beyond one-shot actions, Baron persists learning and followup records across runs (decision #11) via a pluggable store (local markdown by default, under .baron/knowledge). An agent can append a learning, query past ones, and track follow-ups — durable, human-readable, committable knowledge.

The normalized ports cover the common cases; when you need something a port doesn’t expose yet, baron_native_request (decision #18) makes a raw authenticated REST call straight to a provider. It is the deliberate last resort — clearly labeled and non-portable, because you’re now speaking a vendor’s native API instead of an abstract role. It can only reach providers the policy already binds. Prefer the normalized tools every time they fit; reach for the hatch only when they don’t.

recipe / MCP tool call
│ (abstract: role, type role, link type)
▼
BaseIssuesAdapter / BaseScmAdapter ← all role↔native translation + gap negotiation
│ (native: state, board column, label, ref name)
▼
provider transport (octokit / azure-devops-node-api) ← provider I/O only, no translation

Adapters contribute only a capability manifest + a transport; everything abstract↔native happens in the shared core. That’s why a new provider is a thin adapter, not a fork.

Baron is open source under Apache-2.0, a zanaat project.