Extending senro

Every seam in senro is a small Go interface you implement in your own module. There is nothing to register, no plugin loader, and nothing under internal/ to import.

The five extension points

Each one lives next to the built-ins it extends, so you can check whether something already does the job before writing anything.

You want toImplementPage
Fan out over a layout no shipped graph readssenro.UnitGraph, senro.UnitAffectorWrite a unit graph
Trigger on an event source senro does not parsetrigger.Provider, trigger.MatcherWrite a trigger source
Send a run’s result somewhere senro has no destination fornotify.Renderer, notify.RequesterWrite a destination
Turn the event stream into traces, metrics or anything elsesenro.SinkWrite a trace exporter
Have a program explain a failed stepsenro.AnalyzerWrite an analyzer

Each page ends with a worked example. The analyzer has two: a provider-free one you can run with no key, and contrib/genkitanalyzer, a shipping package backed by a real model that you install rather than copy.

What they have in common

  • Structural satisfaction. A type with the right methods is the interface. No registry, no build tag, no init.
  • A narrow import. Each one needs github.com/xavidop/senro or one of its public subpackages, and nothing else of senro’s. The test suite checks that mechanically for the worked examples rather than taking it on trust.
  • Your errors are senro’s errors. Every seam has exactly one way to say “I could not answer”, and senro turns it into a message naming your implementation. A panic becomes the same error; none of them can end a run.
  • The built-ins take the same path. trigger.GitHub() is a Provider, notify.Webhook is a To(url, EventJSON(), ...), notify.GitHubChecks is a Requester. There is no private shortcut, so the public path is the tested one.
  • A worked example that compiles. Each page links one under examples/, driven end to end by senro’s own tests.

The smaller seams

These are extension points too, but they are small enough to be documented where they are used:

SeamWhat it isWhere
senro.RegisterFuncA Go function as a step kind, instead of a commandFunction steps
retry.RegisterPredicateA Go function deciding whether a failed attempt is worth retrying, under a name a plan can recordRetries
change.SourceWhere “what changed” comes from, when it is not a triggerAffected sets
senro.DurationHistoryHow long each unit took last time, which is what Partition balances byPartitioning
senro.Flusher, senro.ReporterOptional interfaces a Sink may also implementTrace exporter
notify.ResponseReaderA Requester that needs to read the response it gotNotifier

What is deliberately not a seam

  • The executor. Local, container, Kubernetes and ssh are the four, and senro.ExecutorTarget is closed. See Executors.
  • trigger.Option. Its method is unexported and trigger.Matcher is the way in, because the set of questions a trigger can ask has to stay the set senro can render into a run’s record.
  • api.Remedy. An analyzer’s remedy comes from a closed vocabulary of one, so the most an unsupervised run can do is retry a step. See Failure analyzer.

Where to go next