Workflow loop architecture and control points
Workflow Loop Architecture and Control Points¶
Visual reference for BaseCoat's recurring control loops, escalation paths, and merge pacing.
- Primary source anchors:
.github/workflows/ship-it-intent-dispatch.yml(intent dispatch and remediation).github/workflows/validate-basecoat.yml(verify gate:validate-unix,validate-windows,validate-commit-messages).github/workflows/agent-merge.yml(guardrails, rollback).github/workflows/stale-management.yml(retriage, staleness sweep)docs/operations/merge-queue-enforcement.md(merge queue parameters)docs/agents/multi-agent-workflows.md(fleet merge pacing)
1. Primary Workflow Loop¶
The core delivery cycle spans six phases. Each phase is backed by one or more workflows. Stop conditions and timeout boundaries are marked at every exit that terminates the loop.
flowchart TD
START(["Issue created / PR opened\n/ship-it comment"]) --> TRIAGE
TRIAGE["Triage\nissue-triage.lock.yml\njob: activation"] --> TRIAGE_DEC{Priority + type\nassigned?}
TRIAGE_DEC -->|"priority:critical / high / medium / low\ntype label applied"| IMPLEMENT
TRIAGE_DEC -->|"Duplicate or not-planned"| STOP_TRIAGE(["Stop: Issue closed\nnot_planned"])
IMPLEMENT["Implement\nship-it-intent-dispatch.yml\njobs: resolve-intent → dispatch-intent"] --> VERIFY
VERIFY["Verify\nvalidate-basecoat.yml\njobs: validate-unix\n validate-windows\n validate-commit-messages"] --> VERIFY_DEC{All checks\npass?}
VERIFY_DEC -->|Yes| MERGE
VERIFY_DEC -->|"Failure (retry if budget remains)"| ESCALATE
MERGE["Guardrails validated\nagent-merge.yml / job: guardrails\nOn pass: GitHub merge queue\nperforms squash merge"] --> MERGE_DEC{Merge\nsucceeded?}
MERGE_DEC -->|Yes| MONITOR
MERGE_DEC -->|"Removed from queue"| IMPLEMENT
MONITOR["Monitor\npost-onboarding-drift-loop.yml\nrunner-health-observability.yml"] --> MONITOR_DEC{Drift or\nregression?}
MONITOR_DEC -->|Clean| RETRIAGE
MONITOR_DEC -->|"Regression detected"| IMPLEMENT
RETRIAGE["Retriage\nstale-management.yml\njob: stale-sweep\n(daily at 07:00 UTC)"] --> RETRIAGE_DEC{Activity\nwithin window?}
RETRIAGE_DEC -->|"Active (updated < 30 days)"| TRIAGE
RETRIAGE_DEC -->|"Stale (30 days) → close (+ 14 days)"| STOP_STALE(["Stop: Auto-closed\nstate_reason=not_planned"])
ESCALATE["Escalation Loop\n(see Diagram 2)"] --> ESCALATE_DEC{Recovered?}
ESCALATE_DEC -->|Yes| VERIFY
ESCALATE_DEC -->|"Hard block: gate:needs-check-in"| HUMAN_GATE(["Human Gate\nManual triage required"])
HUMAN_GATE --> TRIAGE
style START fill:#e1f5ff
style STOP_TRIAGE fill:#ffccbc
style STOP_STALE fill:#ffccbc
style HUMAN_GATE fill:#fff9c4
style ESCALATE fill:#f8bbd0
Loop boundaries¶
| Phase | Entry condition | Stop / exit |
|---|---|---|
| Triage | Issue created or reopened | Closed as duplicate / not_planned |
| Implement | Type + priority label applied | — (feeds Verify) |
| Verify | PR ready for review | Hard failure → Escalation Loop |
| Merge | All required checks green | Removal from queue → re-implement |
| Monitor | Squash merge to main |
Regression → re-implement |
| Retriage | Daily schedule | 30-day stale mark → 14-day auto-close |
2. Escalation Loop¶
Captures the failure → retry → fallback → human-gate path wired into
ship-it-intent-dispatch.yml and agent-merge.yml.
flowchart TD
FAIL["Verify or dispatch failure"] --> RETRY_DEC{Operator retry\nbudget remaining?}
RETRY_DEC -->|"Yes: retry_context prepared"| RETRY["Retry dispatch\nships new PR / re-run"]
RETRY --> VERIFY2["Re-enter Verify phase\nvalidate-basecoat.yml"]
VERIFY2 --> VERIFY2_DEC{Checks pass?}
VERIFY2_DEC -->|Yes| RECOVERED(["Recovered\nReturn to primary loop"])
VERIFY2_DEC -->|No| RETRY_DEC
RETRY_DEC -->|"No: budget exhausted"| REMEDIATION["Remediation issue created\nship-it-intent-dispatch.yml\nstep: Create remediation issue\nwhen dispatch fails\nlabels: intent-control-plane, remediation"]
REMEDIATION --> FALLBACK_DEC{"Rollback available?\nagent-merge.yml\ninput: rollback_ref"}
FALLBACK_DEC -->|"Yes: rollback_apply=true"| ROLLBACK["Rollback applied\nagent-merge.yml\njob: rollback-apply\nCreates revert commit\npushes to origin"]
ROLLBACK --> HUMAN(["Human Gate\ngate:needs-check-in applied\nManual review + re-triage"])
FALLBACK_DEC -->|"No rollback ref"| HUMAN
HUMAN --> RESUME{Decision?}
RESUME -->|"Reopen + re-triage"| RECOVER2(["Return to Triage phase"])
RESUME -->|"Close as not_planned"| TERMINAL(["Stop: Closed"])
style FAIL fill:#ffccbc
style REMEDIATION fill:#f8bbd0
style HUMAN fill:#fff9c4
style RECOVERED fill:#c8e6c9
style RECOVER2 fill:#c8e6c9
style TERMINAL fill:#ffccbc
Control points¶
| Signal | Trigger | Action |
|---|---|---|
steps.dispatch.outcome == 'failure' |
ship-it-intent-dispatch.yml |
Create or update remediation issue with intent-control-plane + remediation labels |
rollback_ref input provided |
agent-merge.yml |
Generate rollback patch |
rollback_apply=true |
agent-merge.yml job rollback-apply |
Create revert commit and push to origin |
gate:needs-check-in label |
Any gate decision | Pause loop; require human triage |
gate:no-tests label |
Queue gate | Hold PR; request test coverage before re-queue |
3. Merge / Queue Loop¶
Serialized merge pacing from the merge queue enforcement policy.
Parameters: max_entries_to_merge=1, grouping_strategy=headCommit, merge method squash.
flowchart TD
PR_OPEN["PR opened / pushed"] --> CHECKS["Required checks run\n(1) validate-commit-messages\n(2) validate-unix\n(3) validate-windows\n(validate-basecoat.yml)"]
CHECKS --> CHECKS_DEC{All checks\npass?}
CHECKS_DEC -->|No| FIX["Author fixes + pushes\n(re-enters at PR_OPEN)"]
FIX --> PR_OPEN
CHECKS_DEC -->|Yes| APPROVAL{1 approving\nreview?}
APPROVAL -->|No| WAIT_REVIEW["Await reviewer\n(reviewer-autoassign.yml)"]
WAIT_REVIEW --> APPROVAL
APPROVAL -->|Yes| QUEUE["Enter merge queue\n(grouping: headCommit)"]
QUEUE --> BUILD["Build merge candidate\nPR commits + main HEAD"]
BUILD --> QUEUE_CHECKS["Queue checks run\n(same required checks\nagainst merge candidate)"]
QUEUE_CHECKS --> QUEUE_DEC{Pass within\n60 min timeout?}
QUEUE_DEC -->|Yes| SQUASH["Squash merge to main\n1 PR at a time"]
SQUASH --> MERGED(["Merged\nPost-merge monitor starts"])
QUEUE_DEC -->|"Timeout (> 60 min)"| QUEUE_REMOVE["Removed from queue\nAuthor notified"]
QUEUE_DEC -->|"Checks fail"| QUEUE_REMOVE
QUEUE_REMOVE --> AUTHOR_DEC{Author\naction?}
AUTHOR_DEC -->|"Push fix commits"| PR_OPEN
AUTHOR_DEC -->|"Stale > 30 days"| STALE_CLOSE(["Auto-closed by stale-management.yml"])
QUEUE --> WAIT_SLOT["Wait if queue has\nother entries\n(min_entries_to_merge_wait_minutes=5)"]
WAIT_SLOT --> BUILD
style PR_OPEN fill:#e1f5ff
style MERGED fill:#c8e6c9
style QUEUE_REMOVE fill:#ffccbc
style STALE_CLOSE fill:#ffccbc
Timeout and retry boundaries¶
| Boundary | Value | Source |
|---|---|---|
| Queue check timeout | 60 min | check_response_timeout_minutes: 60 |
| Max concurrent builds | 5 | max_entries_to_build: 5 |
| Merge batch size | 1 | max_entries_to_merge: 1 |
| Batch wait window | 5 min | min_entries_to_merge_wait_minutes: 5 |
| Stale mark threshold | 30 days | stale-management.yml STALE_DAYS |
| Stale close threshold | +14 days | stale-management.yml CLOSE_DAYS |
| Required status check jobs | validate-commit-messages, validate-unix, validate-windows |
validate-basecoat.yml |
Assumptions¶
- Retry attempt counting is operator-managed; no workflow tracks cross-run attempt counts internally. Operators should inspect the remediation issue comment history to determine retry depth before exhausting the budget.
gate:needs-check-inandgate:no-testslabels are applied by queue gate logic documented inagents/basecoat-60-workflow-queue-rebalancer.agent.md.- Stale exemptions apply: items labelled
pinned,security, ordo-not-closeare skipped bystale-management.yml. - The merge queue is scoped to
refs/heads/main;release/*branches are not yet enrolled (seedocs/operations/merge-queue-enforcement.md).
Intended operator usage¶
- Primary loop: Use Diagram 1 to orient yourself when a delivery stalls — identify which phase the work is in, check the corresponding workflow run, and decide whether to retry, escalate, or close.
- Escalation loop: Use Diagram 2 when a remediation issue appears. Confirm the attempt count, check
rollback_refavailability inagent-merge.yml, and applygate:needs-check-into park the loop for human triage. - Merge/queue loop: Use Diagram 3 when PRs are sitting in the queue unexpectedly. Check queue check timeout (60 min), verify required checks are green, and confirm merge queue configuration has
max_entries_to_merge: 1to avoid race conditions. - Stale recovery: If work was auto-closed, reopen the issue with updated context to reset the activity window and re-enter the triage phase.