PipelineExecutionService¶
Target
This page describes intended behaviour or structure. No implementation is claimed unless a section is explicitly marked Current implementation or Evidence.
| Bounded context | BC-23 Source & Pipelines |
| Platform / plane | DevOps / GitOps / Execution |
| Deployment granularity | module of host · host DeliveryHost |
| Status | target · ConnectSoft.Factory.Delivery.Pipelines |
| Built from | connectsoft-microservice · libraries: connectsoft-extensions-saas-abstractions, connectsoft-extensions-messaging-model, connectsoft-extensions-persistence-model-nhibernate |
| Decisions | ADR-0014 |
| Traceability | FR-07 FUX-05 FUX-06 NFR-08 NFR-12 XL-04 XL-10 |
Purpose and responsibilities¶
PipelineExecutionService keeps two mirrors with provenance. A change proposal is what the propose-change-set tool produced: a change-set artifact pushed as a branch, a pull request opened, reviews, and a merge — each state mirrored from verified webhook deliveries (BC-17) or connector reads, never written by the Factory directly. A pipeline run is the system of record's run with its stages and typed results: build, unit and integration tests, coverage, static analysis, security scan, SBOM, packages, container images and attestations, each a receipt with an artifact reference. Every Factory-authored commit carries trailers naming the run, the change-set hash and the agent release, so a human reading the repository sees exactly which run wrote what.
It absorbs BuildResultService; repository, branch, commit and pull-request "services" of the June-2026 catalog are external canonical rows.
Context and boundaries¶
flowchart LR
S["PipelineExecutionService"]
ToolAdapterService["ToolAdapterService"] -->|grpc| S
StudioBff["StudioBff"] -->|sync-http| S
S -->|grpc| ConnectorRegistryService["ConnectorRegistryService"]
S -->|grpc| ArtifactService["ArtifactService"]
S -. "ChangeProposalOpened" .-> E_ChangeProposalOpened(("event"))
S -. "ChangeProposalStateChanged" .-> E_ChangeProposalStateChanged(("event"))
S -. "ChangeProposalMerged" .-> E_ChangeProposalMerged(("event"))
S -. "PipelineRunStarted" .-> E_PipelineRunStarted(("event"))
S -. "PipelineRunCompleted" .-> E_PipelineRunCompleted(("event"))
Merging is a human or branch-policy decision in the system of record; the Factory never auto-merges to a protected branch without an approval of class shared-write recorded here. A missed webhook is reconciled by polling, never assumed; merge lead time per proposal is the §51 agent-success signal for code changes.
Domain model¶
ChangeProposal — A change set the Factory proposed (through the propose-change-set tool, shared-write class) and its life in the system of record: repository, branch, commits, pull request, reviews and merge — with provenance to the run, attempt, agent release and change-set artifact. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
proposalId |
guid |
yes | |
tenantId |
string |
yes | |
projectId |
string |
yes | |
runId |
guid |
yes | |
attemptId |
guid |
yes | |
agentReleaseId |
string |
yes | |
changeSetRef |
string |
yes | Artifact reference (change-set-v1) |
system |
enum:SystemOfRecord |
yes | |
repository |
string |
yes | |
branch |
string |
yes | |
pullRequestRef |
string |
no | |
commits |
list:string |
yes | |
state |
enum:ProposalState |
yes | |
reviewers |
json |
no | |
mergedCommit |
string |
no | |
updatedAt |
datetime |
yes |
Invariants:
- The system of record is the truth of branches, commits and pull requests; the proposal mirrors it from verified deliveries and connector reads, never the other way round.
- Every commit the Factory authors carries the run id and change-set hash in its trailer; a proposal without provenance is not Factory-authored.
- A merge is a human or policy decision in the system of record; the Factory never auto-merges to a protected branch without an approval of class shared-write recorded here.
stateDiagram-v2
Proposed --> BranchCreated : BranchPushed
BranchCreated --> PullRequestOpen : PullRequestOpened
PullRequestOpen --> ChangesRequested : ReviewRequestedChanges
ChangesRequested --> PullRequestOpen : NewCommits
PullRequestOpen --> Approved : ReviewApproved
Approved --> Merged : Merged
PullRequestOpen --> Abandoned : Closed
Events: ChangeProposalOpened, ChangeProposalStateChanged, ChangeProposalMerged · Tables: ChangeProposals, ProposalCommits · Repository: ChangeProposalRepository
PipelineRun — A mirrored pipeline run with its stages and typed results (build, tests, coverage, static analysis, security scan, SBOM, packages, images, attestations) and the proposal or release that triggered it. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
pipelineRunId |
guid |
yes | |
tenantId |
string |
yes | |
projectId |
string |
yes | |
system |
enum:SystemOfRecord |
yes | |
externalRunRef |
string |
yes | |
definitionRef |
string |
yes | Pipeline definition in the repository (generated by the Factory from the pipeline blueprint). |
proposalId |
guid |
no | |
commit |
string |
yes | |
state |
enum:PipelineRunState |
yes | |
stages |
json |
yes | |
startedAt |
datetime |
yes | |
completedAt |
datetime |
no |
Invariants:
- Results are receipts with artifact references; logs and reports live in the artifact store, not here.
- A run's state comes from verified deliveries (webhooks) or a connector read; a missing final delivery is reconciled by polling, never assumed.
Events: PipelineRunStarted, PipelineRunCompleted · Tables: PipelineRuns, PipelineResults · Repository: PipelineRunRepository
erDiagram
ChangeProposal {
guid proposalId PK
string tenantId
string projectId
guid runId
guid attemptId
string agentReleaseId
string changeSetRef
enum_SystemOfRecord system
string repository
string branch
string pullRequestRef
list_string commits
enum_ProposalState state
json reviewers
string mergedCommit
datetime updatedAt
}
PipelineRun {
guid pipelineRunId PK
string tenantId
string projectId
enum_SystemOfRecord system
string externalRunRef
string definitionRef
guid proposalId
string commit
enum_PipelineRunState state
json stages
datetime startedAt
datetime completedAt
}
PipelineResult {
enum_ResultKind kind
bool passed
json summary
string artifactRef
}
PipelineRun ||--o{ PipelineResult : contains
Entities and value objects¶
PipelineResult (entity, in PipelineRun) — One typed result of a run.
| Field | Type | Required | Description |
|---|---|---|---|
kind |
enum:ResultKind |
yes | |
passed |
bool |
yes | |
summary |
json |
yes | counts, percentages, severities |
artifactRef |
string |
no |
Data model and storage¶
| Store | Role | Engine / profile | Tables |
|---|---|---|---|
| Delivery store | primary | azure-sql / nhibernate-sqlserver | ChangeProposals, ProposalCommits, PipelineRuns, PipelineResults |
| Table | Owner | Columns | Indexes |
|---|---|---|---|
dbo.ChangeProposals |
ChangeProposal | 15 | 2 |
dbo.ProposalCommits |
ChangeProposal | 5 | 0 |
dbo.PipelineRuns |
PipelineRun | 12 | 2 |
dbo.PipelineResults |
PipelineResult | 6 | 0 |
Full DDL: appendix.
APIs¶
| Method | Path | Operation | Scope | Side effects | Idempotency | Gateway | Summary |
|---|---|---|---|---|---|---|---|
GET |
/factory/delivery/proposals |
ListChangeProposals | factory.delivery.read |
read | natural | GW-03 | Change proposals by project, run, repository and state with pull-request references. |
GET |
/factory/delivery/proposals/{proposalId} |
GetChangeProposal | factory.delivery.read |
read | natural | GW-03 | One proposal with commits, reviews, pipeline runs and provenance. |
POST |
/factory/delivery/proposals |
OpenChangeProposal | factory.delivery.propose |
shared-write | key-header | GW-03 | Push a change set as a branch and open a pull request in the system of record (shared-write class; called by the propose-change-set tool with its receipt). |
GET |
/factory/delivery/pipeline-runs |
ListPipelineRuns | factory.delivery.read |
read | natural | GW-03 | Pipeline runs by project, commit, proposal and state. |
GET |
/factory/delivery/pipeline-runs/{pipelineRunId} |
GetPipelineRun | factory.delivery.read |
read | natural | GW-03 | Stages and typed results with artifact references. |
POST |
/factory/delivery/pipeline-runs/{pipelineRunId}/reconcile |
ReconcilePipelineRun | factory.delivery.operate |
shared-write | natural | GW-03 | Re-read the run from the system of record when a delivery was missed (Operator). |
| gRPC service | Method | Streaming | Visibility | Side effects | Summary |
|---|---|---|---|---|---|
Pipelines |
RecordProposal |
none | internal | shared-write | Record a proposal opened by the propose-change-set tool with its receipt. |
Messages¶
| Event | Topic | Scope / v | Aggregate | Consumers | Summary |
|---|---|---|---|---|---|
| ChangeProposalOpened | pipelines.proposals.v1.change-proposal-opened |
integration / 1 | ChangeProposal | RunControlService, StudioBff, AuditService, DashboardService | A Factory change set is a branch and pull request in the system of record. |
| ChangeProposalStateChanged | pipelines.proposals.v1.change-proposal-state-changed |
integration / 1 | ChangeProposal | RunControlService, StudioBff, DashboardService | Review or pull-request state changed. |
| ChangeProposalMerged | pipelines.proposals.v1.change-proposal-merged |
integration / 1 | ChangeProposal | GenerationRecipeService, RunControlService, ReleaseService, RepositoryIntelligenceService, StudioBff, AuditService, FeedbackService | The proposal was merged; the merged commit is known. |
| PipelineRunStarted | pipelines.runs.v1.pipeline-run-started |
integration / 1 | PipelineRun | StudioBff, DashboardService | A pipeline run started in the system of record. |
| PipelineRunCompleted | pipelines.runs.v1.pipeline-run-completed |
integration / 1 | PipelineRun | TemplateRegistryService, ReleaseService, RunControlService, StudioBff, DashboardService, AuditService, QualityScoreService | A pipeline run completed with typed results. |
ChangeProposalOpened payload:
| Field | Type | Required | Description |
|---|---|---|---|
proposalId |
guid |
yes | |
tenantId |
string |
yes | |
projectId |
string |
yes | |
runId |
guid |
yes | |
repository |
string |
yes | |
pullRequestRef |
string |
no |
ChangeProposalStateChanged payload:
| Field | Type | Required | Description |
|---|---|---|---|
proposalId |
guid |
yes | |
tenantId |
string |
yes | |
runId |
guid |
yes | |
fromState |
enum:ProposalState |
yes | |
toState |
enum:ProposalState |
yes |
ChangeProposalMerged payload:
| Field | Type | Required | Description |
|---|---|---|---|
proposalId |
guid |
yes | |
tenantId |
string |
yes | |
projectId |
string |
yes | |
runId |
guid |
yes | |
mergedCommit |
string |
yes |
PipelineRunStarted payload:
| Field | Type | Required | Description |
|---|---|---|---|
pipelineRunId |
guid |
yes | |
tenantId |
string |
yes | |
projectId |
string |
yes | |
commit |
string |
yes | |
proposalId |
guid |
no |
PipelineRunCompleted payload:
| Field | Type | Required | Description |
|---|---|---|---|
pipelineRunId |
guid |
yes | |
tenantId |
string |
yes | |
projectId |
string |
yes | |
commit |
string |
yes | |
state |
enum:PipelineRunState |
yes | |
results |
json |
yes |
| Command | Handled by | Result events | Summary |
|---|---|---|---|
OpenChangeProposal |
ChangeProposal | ChangeProposalOpened | |
ReconcilePipelineRun |
PipelineRun | PipelineRunCompleted |
| Event | Producer | Handler | Purpose |
|---|---|---|---|
| DeliveryReceived | ConnectorRegistryService | ApplyDelivery |
Verified webhooks from Azure DevOps and GitHub: push, pull request, review, pipeline run events. |
| ArtifactValidated | ArtifactService | LinkValidation |
Local validator results of a change set before it is proposed. |
Workers and jobs¶
| Worker | Trigger | Output events | Retry | Idempotency key | Summary |
|---|---|---|---|---|---|
| DeliveryApplier | event | ChangeProposalStateChanged, ChangeProposalMerged, PipelineRunStarted, PipelineRunCompleted | exponential | deliveryId |
Maps verified inbound deliveries (push, pull request, review, pipeline run) to proposal and run state; unknown references are looked up through the connector. |
| RunReconciler | schedule */10 * * * * |
PipelineRunCompleted, ChangeProposalStateChanged | fixed | pipelineRunId + window |
Polls runs still Running past their expected duration and proposals without a terminal state through the connector (read class). |
Actors and grains¶
None modelled
No grains are modelled for this item yet.
Sagas and long-running flows¶
None modelled
No sagas are modelled for this item yet.
sequenceDiagram
participant A as Agent attempt (runtime)
participant T as ToolAdapterService (propose-change-set)
participant P as PipelineExecutionService
participant SOR as Azure DevOps / GitHub (via connector)
participant W as WebhookGatewayService → ConnectorRegistryService
A->>T: propose-change-set (change-set artifact)
T->>SOR: push branch, open pull request (shared-write, receipt)
T->>P: RecordProposal (receipt)
P-)A: ChangeProposalOpened
SOR-)W: webhook: pipeline run started / completed, review, merge
W-)P: DeliveryReceived (verified)
P->>P: mirror state, attach typed results
P-)A: PipelineRunCompleted / ChangeProposalMerged
stateDiagram-v2
[*] --> Proposed
Proposed --> BranchCreated: BranchPushed
BranchCreated --> PullRequestOpen: PullRequestOpened
PullRequestOpen --> ChangesRequested: ReviewRequestedChanges
ChangesRequested --> PullRequestOpen: NewCommits
PullRequestOpen --> Approved: ReviewApproved
Approved --> Merged: Merged
PullRequestOpen --> Abandoned: Closed
Options and configuration¶
| Section | Key | Type | Default | Reloadable | Secret | Summary |
|---|---|---|---|---|---|---|
Delivery:Pipelines |
CommitTrailerKeys |
string[] | [Factory-Run, Factory-ChangeSet, Factory-Agent] |
no | no | Git trailers every Factory-authored commit carries. |
Delivery:Pipelines |
ReconcileAfterMinutes |
int | 60 |
yes | no | Age after which a non-terminal run is polled. |
Metrics and observability¶
| Metric | Instrument | Unit | Dimensions | Summary |
|---|---|---|---|---|
connectsoft.factory.delivery.proposals |
updown | {proposal} | tenant, state | Proposals by state. |
connectsoft.factory.delivery.pipeline_runs |
counter | {run} | tenant, state | Pipeline runs by outcome. |
connectsoft.factory.delivery.pipeline_duration |
histogram | s | tenant | Pipeline run duration. |
connectsoft.factory.delivery.merge_lead_time |
histogram | s | tenant | Time from proposal to merge (the §51 agent-success signal for code changes). |
Security¶
| Authentication | client-credentials |
| Scopes | factory.delivery.read (Read proposals and runs.); factory.delivery.propose (Open proposals (the propose-change-set tool under its receipt).); factory.delivery.operate (Reconcile (Operator).) |
| Roles | Requester, Reviewer, Operator, PlatformAdmin, Observer |
| Tenant isolation | row-filter |
| Data classification | internal |
| Max side-effect class | shared-write |
| Secrets | — |
Dependencies¶
Outbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| ConnectorRegistryService | grpc | Azure DevOps and GitHub connectors (shared-write) and inbound deliveries. | degrade | yes |
| ArtifactService | grpc | Change sets, logs and reports. | fail-fast | yes |
Inbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| ToolAdapterService | grpc | RecordProposal from the propose-change-set tool. | — | yes |
| StudioBff | sync-http | Proposal and pipeline views. | — | yes |
Failure and recovery¶
| Scenario | Detection | Recovery | Data impact | RTO (min) |
|---|---|---|---|---|
| Webhook delivery missed | run non-terminal past threshold | RunReconciler polls the system of record | delayed state | 70 |
| Push refused by branch policy | connector error | proposal stays Proposed with the reason; the agent attempt records a failed shared-write | none | 0 |
NFR targets¶
| Target | Class value (pilot-batch) | Override |
|---|---|---|
| latency_p95_ms | 0 | — |
| availability_pct | 99.5 | — |
| throughput_rps | 10 | — |
| rpo_minutes | 15 | — |
| rto_minutes | 240 | — |
| cost_class | medium | — |
Deployment¶
| Granularity | module of host · host DeliveryHost |
| Topologies | saas-default, hybrid, on-prem, hybrid-compute |
| Runtime | —; replicas —–— |
| Ports | — |
Current implementation¶
ConnectSoft.AzurePipelines defines the pipelines every template repository runs; the Azure DevOps and GitHub MCP tool descriptors exist in the tools catalog. No proposal or run mirror, no typed results, no commit trailers.
Later capabilities¶
Pipeline definitions generated from the pipeline blueprint and validated against the registry; test-impact selection fed by repository intelligence; flaky-test tracking as a typed result.
Decisions and traceability¶
| Family | Ids |
|---|---|
| FR | FR-07 |
| FUX | FUX-05, FUX-06 |
| NFR | NFR-08, NFR-12 |
| XL | XL-04, XL-10 |