Principles

Principle — pipeline tooling itself must be block-composition, not monolithic Go

**Pipeline tooling itself must be block-composition pipelines with declarative logic-routing. Imperative Go orchestration is not the pipeline's runtime — it is at most the executor's implementation detail behind a…

Principle — pipeline tooling itself must be block-composition, not monolithic Go

Status: ratified — warp#698 Filed under: ADR-113 (principles tier above initiative) Decision document: ADR-124 (pipeline tooling is block-composition) Enforced at PR review by: principle-aware reviewers per ADR-115; 4-layer enforcement gate stack per warp#705

Statement

Pipeline tooling itself must be block-composition pipelines with declarative logic-routing. Imperative Go orchestration is not the pipeline's runtime — it is at most the executor's implementation detail behind a runner.Executor port.

The shape of a pipeline-step contribution:

  • Block names are abstract primitives. read-json, validate-schema, transform, write-json, http-probe, assert-equal. They name a generic capability and are reusable across pipelines.
  • Pipeline names are domain-specific. discovery-brief-enhancement, api-contract-extract, provision-and-deploy-warp. They name the business concern and compose blocks into it.
  • Logic routing lives in the pipeline file, not in the block. if, foreach, cache, retry, timeout are routing primitives expressed declaratively in YAML/JSON between block invocations. A block with branching switch statements over its inputs is a smell — that is pipeline-shape leaking into the block.
  • Single concern per block. A block does one thing. If it needs to know about two upstream concerns to decide what to do, it is two blocks plus routing.
  • Typed I/O ports per ADR-085. Block declarations carry inputs: and outputs: with named, typed ports. The validator catches reference typos and type mismatches at design time, before the runtime ever loads the pipeline.

Why this rule

Today's empirical evidence: ai-research/cmd/server/pipeline/ contains 367 .go files; exactly one (library_bridge.go) imports gyrum-pipelines/pkg/block. The bridge file's own header comment names the shape as Phase 1 of a multi-PR migration that has not graduated. Without a citable principle, reviewers seeing a new monolithic RunContext-shaped step had no canonical rule to push back with — the shape kept reproducing.

The 600s sub-agent watchdog incident on warp#622 (memory: feedback_subagent_watchdog_600s.md) was the same monolith failure mode: a single agent shipping a single large change because the substrate offered no smaller atomic unit. ADR-123 named the same failure one layer up — Build phase was a single opus-driven monolith generating 80% of every project from scratch — and the resolution was the same shape: deterministic templates plus declarative composition plus scoped AI gap-fill.

Recursive application

This principle is the recursive application of the generic-primitives principle (warp#724, re-mint of cancelled warp#693) to pipeline tooling itself:

  • Library packages (sister principle) — package symbols are generic verbs/nouns; domain shape lives in product code.
  • Pipeline blocks (this principle) — block names are abstract verbs; pipeline names are domain. ADR-124 cites the generic-primitives principle as the foundation.
  • Module guidelines (ADR-117) — each module-type's guideline says "this module-type's symbols must be generic for that module shape, not domain-specific." pipeline-step@v1 rests on this principle directly.

How to apply

  1. At write time — when adding a pipeline step, ask: "is the name an abstract primitive (good — file as a block) or a domain-specific flow (good — file as a pipeline composing blocks)?" A RunContext-shaped imperative step is the smell — split it into typed-port blocks plus pipeline routing.
  2. At PR time — principle-aware reviewers per ADR-115 cite this principle when blocking PRs that introduce a new monolithic step. The 4-layer gate stack (warp#705) rejects new monolithic steps at author / PR / persona / drift time.
  3. At graduation time — the existing 366 imperative cmd/server/pipeline/ files are tracked as legacy graduation backlog (warp#707), prioritised by churn. New pipeline tooling MUST be authored in the block shape; legacy migrates incrementally, not in a big-bang rewrite.
  4. At catalog time — block names are a fleet-wide artefact. Domain leakage into a block name is a bug; ADR-117 module guidelines treat pipeline-step as a module type with block-name = generic primitive as a forbidden-pattern check.

Applies to

cmd/server/pipeline/**, pkg/pipeline/**, pkg/block/**, and any new directory whose go.mod imports gyrum-pipelines/pkg/block.

Cross-link

  • warp#698 — this principle's live ticket
  • ADR-124 — the decision document ratifying this principle
  • generic-primitives — the sister principle this one recursively applies (warp#724)
  • warp#705 — 4-layer enforcement gate stack (author / PR / persona / drift)
  • warp#707 — graduation epic: convert 366 imperative cmd/server/pipeline/ files to block-composition
  • ADR-085 — typed I/O ports (the contract block-composition rests on)
  • ADR-090 — AI as scaffold, pipelines as runtime (this principle refines for pipeline tooling)
  • ADR-113 — principles tier (the schema this is filed under)
  • ADR-115 — principle-aware reviewers (how the gate cites this principle)
  • ADR-117 — module guidelines (pipeline-step@v1 rests on this principle)
  • ADR-123 — templates as generator (the same shape one layer up)
  • Memory feedback_modular_pipelines_default.md — operator preference, 2026-04-29 (motivates this principle)
  • Memory feedback_subagent_watchdog_600s.md — the failure mode this principle structurally prevents