21 — Forgeyard SCM Provider Integrations System Architecture
Document type: Core Source-Control Hosting Provider Integration System Architecture
Project: Forgeyard CI/CD
Subsystem: SCM hosting providers, repository bindings, provider app/OAuth integrations, webhook ingestion, exact revision resolution, Change Proposal mapping, check/status publication, comments/reviews, merge/integration operations, installation lifecycle, rate limits, drift reconciliation, and provider-specific capability adapters
Architecture style: Provider-neutral core, VCS/provider separation, exact immutable source identity, verified webhook ingress, at-least-once provider synchronization, provider-specific adapters behind capability traits, and reconciliation for missed/ambiguous external state
Status: Target production architecture for the new Forgeyard repository
Relationship to prior work: Builds on the VCS-neutral architecture, Change Proposal system, API/Axum, Events/Reconciliation, Identity/Authz/Policy, Secrets/Trust, Run/Job, Release, and Supply-Chain systems. It deliberately separates source-control hosting providers from source-control VCS implementations.
1. Purpose
Forgeyard must integrate cleanly with SCM hosting services such as:
GitHub
GitLab
Bitbucket
Gitea/Forgejo
self-hosted enterprise forges
future provider APIs
These providers expose capabilities such as:
repository discovery
webhooks
pull/merge requests
checks/statuses
comments
reviews
labels
branch protection
installation/authentication
release/tag views
But Forgeyard must not let provider semantics become its internal domain model.
The central rule is:
GitHub PR, GitLab MR, Bitbucket PR, and similar concepts map into Forgeyard
ChangeProposal; provider-specific names never become core business types.
A second rule is:
A provider repository/ref/revision is always resolved to an exact immutable
SourceSnapshotIdbefore Forgeyard schedules trusted work. Mutable branch names are navigation metadata only.
A third rule is:
Webhook delivery is an optimization/trigger. Reconciliation against provider state repairs missed, duplicated, delayed, or ambiguous webhook effects.
2. Architectural Position
SCM Hosting Provider
│
┌──────────┼──────────┐
▼ ▼ ▼
Webhook REST App/OAuth
│ │ │
└──────────┼──────────┘
▼
Provider Adapter
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Repository Change Checks
Binding Proposal /Status
│ │ │
└─────────────┼─────────────┘
▼
Forgeyard Core
│
┌─────────────┼──────────────┐
▼ ▼ ▼
VCS SourceSnapshot Policy/Run
Adapter Id
3. Goals
The subsystem MUST:
- separate VCS from provider;
- support provider installations/accounts;
- support repository bindings;
- support provider-native webhook verification;
- deduplicate webhook deliveries;
- normalize provider events;
- resolve exact revisions;
- create/update Change Proposals;
- publish checks/statuses;
- publish comments;
- support review/approval mappings where provider supports;
- support provider labels/metadata;
- support integration/merge submission;
- verify resulting integrated revision;
- support provider rate limits;
- support retries/backoff;
- support missed-webhook reconciliation;
- support ambiguous-submit reconciliation;
- support provider credential rotation;
- support enterprise/self-hosted endpoints;
- support installation revocation;
- support multi-tenant separation;
- support provider capability discovery;
- support SCM deep links;
- support provider-specific diagnostics;
- keep provider SDKs adapter-local;
- never trust mutable refs for build identity;
- never treat provider UI approval as universally authoritative without normalization;
- preserve native IDs for navigation/audit;
- remain extensible to new providers.
4. Non-Goals
This subsystem does not:
implement Git/Mercurial itself
replace Forgeyard Change Proposal
replace SourceSnapshot identity
replace policy engine
replace provider's full UI
5. Workspace Structure
crates/scm/
├── forgeyard-scm/
├── forgeyard-scm-model/
├── forgeyard-scm-provider/
├── forgeyard-scm-binding/
├── forgeyard-scm-webhook/
├── forgeyard-scm-change/
├── forgeyard-scm-check/
├── forgeyard-scm-comment/
├── forgeyard-scm-review/
├── forgeyard-scm-label/
├── forgeyard-scm-integration/
├── forgeyard-scm-reconcile/
├── forgeyard-scm-health/
├── forgeyard-scm-testkit/
│
├── forgeyard-scm-github/
├── forgeyard-scm-gitlab/
├── forgeyard-scm-bitbucket/
├── forgeyard-scm-gitea/
└── forgeyard-scm-forgejo/
VCS remains separate:
crates/vcs/
├── forgeyard-vcs/
├── forgeyard-vcs-git/
├── forgeyard-vcs-mercurial/
└── ...
6. ProviderId
#![allow(unused)]
fn main() {
pub struct ScmProviderId(Ulid);
}
Represents configured provider instance.
7. Provider Kind
#![allow(unused)]
fn main() {
pub enum ScmProviderKind {
GitHub,
GitLab,
Bitbucket,
Gitea,
Forgejo,
Custom(ScmProviderKindId),
}
}
8. Provider Instance
Example:
github.com
gitlab.com
git.company.internal
forgejo.internal
9. Provider Config
#![allow(unused)]
fn main() {
pub struct ScmProviderConfig {
pub id: ScmProviderId,
pub kind: ScmProviderKind,
pub base_url: Url,
pub api_url: Url,
pub auth: ScmProviderAuthRef,
}
}
10. Public Cloud vs Self-Hosted
Provider adapter must not hardcode only public endpoint.
11. Installation Identity
#![allow(unused)]
fn main() {
pub struct ProviderInstallationId(Ulid);
}
Forgeyard semantic installation identity.
12. External Installation ID
Provider-native installation/app/account ID retained.
13. Installation Record
#![allow(unused)]
fn main() {
pub struct ProviderInstallation {
pub id: ProviderInstallationId,
pub provider: ScmProviderId,
pub external_id: BoundedString,
pub tenant: TenantId,
pub state: ProviderInstallationState,
}
}
14. Installation State
#![allow(unused)]
fn main() {
pub enum ProviderInstallationState {
Active,
Suspended,
Revoked,
Unavailable,
}
}
15. Auth Types
Potential:
#![allow(unused)]
fn main() {
pub enum ScmProviderAuthKind {
AppInstallation,
OAuthApp,
ServiceAccount,
PersonalToken,
}
}
16. Preferred Authentication
Provider app/installation token where available.
17. Avoid Human PAT
Use PAT only fallback.
18. Credential Storage
SecretRef.
19. Short-Lived Token
Generate/refresh installation token when provider supports.
20. Provider Credential Scope
Only repositories/installations needed.
21. RepositoryBindingId
#![allow(unused)]
fn main() {
pub struct RepositoryBindingId(Ulid);
}
22. Repository Binding
#![allow(unused)]
fn main() {
pub struct RepositoryBinding {
pub id: RepositoryBindingId,
pub project: ProjectId,
pub repository: RepositoryId,
pub provider: ScmProviderId,
pub installation: ProviderInstallationId,
pub external_repository: ExternalRepositoryRef,
}
}
23. RepositoryId
Forgeyard VCS-neutral repository identity.
24. ExternalRepositoryRef
#![allow(unused)]
fn main() {
pub struct ExternalRepositoryRef {
pub provider_repository_id: BoundedString,
pub owner: BoundedString,
pub name: BoundedString,
}
}
25. Provider Repo ID vs Name
Native repository ID is more stable than owner/name path.
Retain both.
26. Repository Rename
Binding remains through native ID.
27. Repository Transfer
Reconcile owner/namespace change.
28. Binding Authority
Only authorized admin/provider installation can bind repo.
29. VCS Type
Provider binding includes underlying VCS:
Git
Mercurial
if provider supports multiple.
30. Provider vs VCS
Example:
GitHub = hosting provider
Git = VCS
31. Provider Trait
#![allow(unused)]
fn main() {
#[async_trait]
pub trait ScmProvider {
fn capabilities(&self) -> ScmCapabilities;
async fn repository(
&self,
repo: &ExternalRepositoryRef,
) -> Result<ScmRepository, ScmError>;
async fn change(
&self,
request: ExternalChangeRef,
) -> Result<ScmChange, ScmError>;
async fn publish_check(
&self,
request: PublishCheckRequest,
) -> Result<PublishCheckResult, ScmError>;
async fn submit_integration(
&self,
request: IntegrationSubmissionRequest,
) -> Result<IntegrationSubmissionResult, ScmError>;
}
}
32. Provider Capabilities
#![allow(unused)]
fn main() {
pub struct ScmCapabilities {
pub webhooks: bool,
pub checks: bool,
pub commit_statuses: bool,
pub reviews: bool,
pub comments: bool,
pub merge_queue: bool,
pub branch_protection: bool,
pub app_installations: bool,
}
}
33. Capability Honesty
Provider features vary by plan/version/self-hosted release.
Do not fake unsupported capability.
34. Native Change Ref
#![allow(unused)]
fn main() {
pub struct ExternalChangeRef {
pub repository: ExternalRepositoryRef,
pub number: BoundedString,
}
}
35. Forgeyard ChangeProposalId
Independent internal ID.
36. Provider Change Mapping
GitHub Pull Request
GitLab Merge Request
Bitbucket Pull Request
↓
ChangeProposal
37. ChangeProposal External Binding
#![allow(unused)]
fn main() {
pub struct ChangeProposalExternalBinding {
pub proposal: ChangeProposalId,
pub provider: ScmProviderId,
pub repository: RepositoryBindingId,
pub external_change: ExternalChangeRef,
}
}
38. Provider State Normalization
Normalize:
open
closed
merged/integrated
draft
to internal lifecycle facts.
39. Do Not Mirror Provider State Blindly
Forgeyard has richer separate states:
review
checks
policy
mergeability
integration
40. Proposal Revision
Each external source update creates:
new ProposalRevisionId
41. Exact Source Revision
Provider webhook/API supplies exact source revision ID.
42. Resolve Source
provider revision
↓
VCS adapter fetch/materialize
↓
canonical tree
↓
SourceSnapshotId
43. Mutable Branch
Only contextual metadata.
44. Webhook Ingress
Path:
/webhooks/{provider}/{binding-or-installation}
45. Webhook Flow
raw request
↓
body size bound
↓
signature verification
↓
delivery ID dedup
↓
persist accepted delivery
↓
normalize
↓
process asynchronously
46. Webhook Signature
Provider-specific adapter.
47. Raw Body
Preserve exact bytes for signature verification where required.
48. DeliveryId
#![allow(unused)]
fn main() {
pub struct ProviderDeliveryId(BoundedString);
}
49. Dedup Key
provider + installation + delivery ID
50. Webhook Event Types
Examples:
repository changed
change opened
change updated
change closed
change merged
review submitted
push
installation revoked
51. Normalized Provider Event
#![allow(unused)]
fn main() {
pub enum ScmNormalizedEvent {
RepositoryChanged(...),
ChangeCreated(...),
ChangeUpdated(...),
ChangeClosed(...),
ChangeIntegrated(...),
ReviewChanged(...),
RefUpdated(...),
InstallationChanged(...),
}
}
52. Provider Event Is Not Domain Event Yet
Normalize/resolve then domain service decides transition.
53. Webhook Fast Ack
Return accepted after durable persistence/verification.
54. Processing Failure
Retry internally.
Provider need not wait.
55. Duplicate Provider Delivery
Safe.
56. Missed Webhook
Reconciliation.
57. Ref Update Event
Never schedule release build from branch name alone.
Resolve exact revision.
58. Push Event
Can trigger pipeline according to policy.
59. SourceSnapshot Binding
Triggered run stores exact SourceSnapshotId.
60. Change Event Processing
fetch provider change
↓
read exact head/base revisions
↓
resolve VCS snapshots
↓
append ProposalRevision
↓
evaluate policy/checks
61. Provider Diff
May use provider API for navigation/metadata.
Forgeyard can compute canonical diff from snapshots where needed.
62. Review Mapping
Provider reviews normalize to Forgeyard ReviewVerdict.
63. Review Verdict
Use existing:
Comment
Approve
RequestChanges
64. Native Provider Review ID
Stored for linkage.
65. Review Identity Mapping
Provider actor -> external actor.
If linked to Forgeyard principal:
verified mapping
66. No Username Equality Trust
GitHub username != Forgeyard Principal automatically.
67. Unlinked External Review
May be preserved but policy decides whether it counts.
68. Provider Approval Counting
Do not assume every provider “approved” means Forgeyard policy approval.
Normalize then evaluate.
69. Comments
Forgeyard can publish:
check summary
failure explanation
policy block
70. Comment Idempotency
Use marker/provider external comment ID.
71. Avoid Comment Spam
Update existing Forgeyard summary comment where provider supports.
72. Check Publishing
Forgeyard maps Run/Check result to provider check/status.
73. Internal Check Identity
#![allow(unused)]
fn main() {
pub struct CheckRunId(Ulid);
}
or existing check identity.
74. Provider Check Binding
#![allow(unused)]
fn main() {
pub struct ProviderCheckBinding {
pub check: CheckRunId,
pub provider: ScmProviderId,
pub external_id: BoundedString,
}
}
75. Check Status
Normalize provider concepts.
76. Check Publication State
#![allow(unused)]
fn main() {
pub enum CheckPublicationState {
Pending,
InProgress,
Succeeded,
Failed,
Unknown,
}
}
77. Unknown Publication
Reconcile.
78. Check Idempotency
Same internal check/source snapshot updates same provider check where possible.
79. Commit Status Fallback
If provider lacks rich checks API.
80. Check Context Name
Stable Forgeyard-defined name.
81. Check Details URL
Deep link to Forgeyard run/check.
82. Status Security
Never publish secret/log raw data.
83. Check Summary
Sanitized.
84. Annotation
Provider-specific limit.
Bound count/size.
85. Provider Rate Limits
First-class.
86. Rate Limit Model
#![allow(unused)]
fn main() {
pub struct ProviderRateLimit {
pub remaining: Option<u64>,
pub reset_at: Option<Timestamp>,
pub retry_after: Option<Duration>,
}
}
87. Rate-Limit Backoff
Respect provider hints.
88. Priority
Critical operations:
integration verification
status finalization
webhook reconcile
before cosmetic comments.
89. Provider Request Budget
Per installation/provider.
90. Caching
Cache provider metadata with ETag/Last-Modified where supported.
91. Do Not Cache Mutable Critical State Too Long
Integration target state must be fresh.
92. Reconciliation
Periodic targeted sync.
93. Repository Reconcile
Checks:
binding still valid
repo renamed/transferred
default branch changed
installation access revoked
94. Change Reconcile
Checks:
provider head revision
base revision
open/closed state
reviews
mergeability
95. Check Reconcile
Checks:
provider check missing/stale
status publication Unknown
96. Installation Reconcile
Checks permissions/access.
97. Ref Reconcile
For configured watched refs.
98. Missed Webhook Recovery
Reconcile identifies provider state newer than last known.
99. Reconcile Cursor
Per repository/provider.
100. Updated-Since
Use provider API if reliable.
101. Fallback Full Window
Bounded recent change scan.
102. No Unbounded Full Provider Crawl
Keyset/provider pagination.
103. Integration Submission
Change Proposal integration may be executed through provider.
104. Integration Request
#![allow(unused)]
fn main() {
pub struct IntegrationSubmissionRequest {
pub proposal: ChangeProposalId,
pub proposal_revision: ProposalRevisionId,
pub expected_target_revision: RevisionId,
pub candidate_snapshot: SourceSnapshotId,
pub strategy: IntegrationStrategy,
}
}
105. Precondition
Provider target revision must still equal expected base.
106. Provider Merge Strategy
Map internal:
FastForward
Merge
Rebase
Squash
BackendNative
to provider-supported operation.
107. Unsupported Strategy
Fail before submit.
108. Ambiguous Submission
Example:
HTTP timeout after merge request accepted
state:
Unknown
109. Unknown Integration Reconcile
inspect target revision
inspect proposal state
materialize resulting revision
compare SourceSnapshotId
110. Never Blind Merge Retry
Critical.
111. Post-Integration Verification
After provider reports merged:
resolve resulting revision
↓
materialize snapshot
↓
verify equals approved integration candidate
112. Mismatch
Critical integrity issue.
113. Provider Native Merge Queue
Optional adapter.
114. Forgeyard Queue
Can remain authoritative integration queue.
115. Hybrid
Forgeyard may submit to provider queue then reconcile result.
116. Branch Protection
Provider branch protection can be inspected/configured if API supports.
117. Provider Enforcement vs Forgeyard Enforcement
Expose distinction.
118. Protected Target Policy
Forgeyard policy remains authority for Forgeyard actions.
119. Provider Direct Push
If provider allows bypass outside Forgeyard, Forgeyard cannot guarantee block unless branch protection/provider permissions enforce it.
120. Honesty
UI/documentation must state protection coverage.
121. Repository Write Permission
Forgeyard integration app should have minimum needed.
122. Read-Only Mode
For CI-only integration:
read repo
write checks
without merge permission.
123. Merge Permission
Separate optional scope.
124. Comment Permission
Separate if provider allows.
125. Installation Scope UI
Show requested provider permissions.
126. Least Privilege Installation
Recommended.
127. Webhook Secret Rotation
Dual-secret validation window if needed.
128. App Private Key
Secret/KMS managed.
129. OAuth Refresh Token
SecretRef/encrypted store.
130. Provider Token Caching
Short-lived in memory.
131. Credential Revocation
Provider 401/403 triggers installation health degradation/reconcile.
132. Installation Removed
Mark Suspended/Revoked.
133. Existing Runs
Can continue from already-materialized SourceSnapshot according to policy.
134. New Provider Actions
Blocked.
135. Provider Health
#![allow(unused)]
fn main() {
pub struct ScmProviderHealth {
pub api: HealthStatus,
pub auth: HealthStatus,
pub webhook: HealthStatus,
pub rate_limit: ProviderRateLimitHealth,
}
}
136. Repository Binding Health
Healthy
Degraded
Revoked
Unknown
137. Doctor
forgeyard scm doctor
138. Doctor Checks
provider reachability
auth
repository access
webhook registration
check publication
rate-limit state
139. Safe Probe
Do not create spam comments/status unless explicitly testing.
140. Webhook Registration
Provider adapter can create/update webhook/app subscription where supported.
141. Webhook Ownership Marker
Identify Forgeyard-managed hook.
142. Duplicate Webhooks
Detect/avoid multiple equivalent hooks.
143. Self-Hosted TLS
Support custom trusted CA bundle via Trust subsystem.
144. Insecure TLS
Forbidden by default.
145. Proxy
Provider HTTP client may support configured proxy.
146. HTTP Client
Central hardened provider HTTP client.
147. Timeouts
Bound connect/request/read.
148. Retry
Only safe/idempotent provider operations automatically.
149. POST Retry
Only with provider idempotency/semantic reconciliation.
150. User Agent
Identify Forgeyard/version.
151. API Version Headers
Provider adapter-specific.
152. GitHub Adapter
Potential capabilities:
GitHub App
installations
webhooks
pull requests
checks
statuses
reviews
comments
merge
branch protection
153. GitLab Adapter
Potential:
OAuth/token/app model
webhooks
merge requests
pipelines/status
notes
approvals
merge
protected branches
154. Bitbucket Adapter
Normalize available capabilities.
155. Gitea/Forgejo Adapter
Useful self-hosted integration.
156. Provider API Drift
Self-hosted versions vary.
Capability discovery/version gates.
157. Adapter Version Support
Document/test supported provider versions.
158. Provider-Specific DTOs
Stay inside adapter.
159. Core Scm DTOs
Normalized stable internal model.
160. ScmRepository
#![allow(unused)]
fn main() {
pub struct ScmRepository {
pub external: ExternalRepositoryRef,
pub default_ref: Option<RefName>,
pub visibility: RepositoryVisibility,
pub archived: bool,
}
}
161. ScmChange
#![allow(unused)]
fn main() {
pub struct ScmChange {
pub external: ExternalChangeRef,
pub title: BoundedString,
pub state: ScmChangeState,
pub source_revision: RevisionId,
pub target_revision: RevisionId,
pub source_ref: Option<RefName>,
pub target_ref: Option<RefName>,
}
}
162. RevisionId
VCS-native immutable revision ID.
163. SourceSnapshotId
Resolved canonical tree identity.
164. Provider Change Number
Navigation only.
165. Change Title/Body
Untrusted text.
Sanitize UI rendering.
166. Provider Labels
Normalize strings.
167. Labels Are Not Trust
Never grant privileged capability solely from external label unless explicit policy mapping.
168. Label Mapping
Policy can map trusted provider labels into workflow facts only if configured.
169. Review Comments
Untrusted.
170. Suggested Changes
Provider suggestion mapping can create new source revision only when user/provider actually applies.
171. Outdated Comments
Provider/native anchor state can supplement Forgeyard anchor logic.
172. Forgeyard Native Review Mode
Provider may be external navigation only.
173. External Review Mode
Provider review can be imported.
174. Hybrid Review Mode
Both Forgeyard and provider evidence.
175. Review Conflict
Keep source/origin.
Do not merge distinct review identities incorrectly.
176. Provider Checks Import
Forgeyard may ingest external checks.
177. External Check Trust
Policy decides which provider check contexts count.
178. Check Origin
#![allow(unused)]
fn main() {
pub enum CheckOrigin {
Forgeyard,
ProviderExternal,
ThirdParty,
}
}
179. Required Check
Policy binds by trusted check identity, not display string alone.
180. Provider Context Spoofing
Prevent external user from creating similarly named status that satisfies Forgeyard requirement.
181. Trusted Check Binding
Map:
provider app/integration identity + context
182. Webhook Event Security
Verify:
signature
installation/repo binding
delivery dedup
event repository ID
183. Cross-Tenant Spoof
Webhook route cannot specify arbitrary tenant without verified binding.
184. Provider URL Validation
Avoid SSRF from provider-supplied URLs.
185. API URLs
Construct from configured trusted provider base, not arbitrary event links.
186. Clone URL
Validate allowed schemes/hosts.
187. Provider Redirect
Bound/validate.
188. Archive Download
If provider provides source tarball, still canonicalize into SourceSnapshotId.
189. Prefer VCS Fetch
Use VCS adapter where possible.
190. Provider Archive Semantics
Not assumed equivalent unless verified tree.
191. Submodules/Subrepos
Handled by VCS architecture.
Provider metadata may help authentication.
192. Private Submodule Credentials
Secret/provider token scoped.
193. Fork Proposal
Source repository may differ from target repository.
194. Fork Trust
SourceTrust::Fork.
195. Token Security for Fork
Never expose target repo write token to fork job.
196. Proposal Build Credentials
Read-only source fetch as needed.
197. Provider Check Token
Control plane publishes checks; job does not get provider token.
198. Security Boundary
Untrusted build cannot call provider API with Forgeyard installation credential.
199. Provider Actions
Only daemon/restricted integration worker.
200. SCM Worker
Optional dedicated worker for provider calls.
201. Daemon Initially
Provider calls can run in daemon service with bounded async.
202. Scale-Out Later
Separate integration worker queue.
203. Provider Operation Record
#![allow(unused)]
fn main() {
pub struct ScmOperation {
pub id: ScmOperationId,
pub provider: ScmProviderId,
pub kind: ScmOperationKind,
pub state: ScmOperationState,
}
}
204. Operation State
Pending
InProgress
Succeeded
Failed
Unknown
205. Operation Types
PublishCheck
PublishComment
SubmitReview
SubmitIntegration
RegisterWebhook
UpdateLabel
206. State-Changing Provider Calls
Persist desired operation before call when correctness matters.
207. Comment Publication
Lower criticality; can still use outbox.
208. Check Finalization
Important for provider UI consistency.
209. Integration Submission
Highest criticality.
210. Operation Idempotency
Adapter supplies semantic key.
211. Provider Retry Class
#![allow(unused)]
fn main() {
pub enum ScmRetryClass {
SafeRetry,
RetryAfterInspect,
DoNotRetry,
}
}
212. 429
Safe after delay.
213. 5xx GET
Retry.
214. Merge Timeout
Inspect first.
215. Merge Conflict
No retry without state change.
216. Provider Pagination
Adapter consumes provider-specific pagination.
217. Core Pagination
Normalized cursor internal.
218. Repository Discovery
Installation can list accessible repositories.
219. Bind Flow
select provider installation
↓
list accessible repos
↓
choose repo
↓
verify VCS access
↓
create RepositoryBinding
↓
register webhook
↓
initial reconcile
220. Initial Reconcile
Fetch:
repo metadata
default branch
open changes
current protection/status config
bounded.
221. Import Existing Changes
Policy-controlled.
222. Backfill Limit
Do not ingest entire historical PR universe by default.
223. Unbind
Disable provider sync.
224. Unbind Does Not Delete Forgeyard History
Existing source/run/change history retained.
225. Rebind
Can restore same external repo to same internal RepositoryId if verified.
226. Repository Clone Auth
Provider credential helper/token generation.
227. VCS Adapter Interface
SCM provides authenticated fetch material; VCS performs fetch/tree resolution.
228. Separation Example
GitHub App creates short-lived token
↓
Git adapter fetches revision
↓
Forgeyard canonicalizes SourceSnapshot
229. No GitHub SDK in VCS Core
Critical.
230. No Git Logic in GitHub Adapter Core Model
Provider adapter may expose clone URLs/revisions, but tree semantics remain VCS adapter.
231. Check Run Lifecycle
Queued
InProgress
Completed
provider mapping.
232. Forgeyard Job State Mapping
Not one-to-one.
Aggregate into provider-safe summary.
233. Multiple Forgeyard Jobs
Can map to:
one overall check
+
individual checks
configuration.
234. Recommended
One check per required workflow plus aggregate summary.
235. Check Annotation Limits
Truncate with link to Forgeyard detail.
236. Provider UI Is Secondary
Full logs/evidence remain Forgeyard.
237. Commit Status Race
Check published for exact revision.
238. Never Publish to Moving Branch Ref
Always revision SHA/ID.
239. Release Tag Integration
SCM provider may create tag/release view.
Release subsystem remains authority.
240. Tag Creation
Exact integrated revision.
241. Tag Signing
Supply-chain/release policy.
242. Provider Release View
Mirror Forgeyard release.
243. Provider Release Deletion
Does not delete Forgeyard release history.
244. Provider Webhook Event Retention
Raw accepted webhook can be retained short term for debugging.
245. Sensitive Raw Payload
Protected retention.
246. Normalized Event Retention
Longer if domain history requires.
247. Webhook Replay
Operator can replay accepted raw webhook through normalization after bug fix.
248. Replay Safety
Dedup/domain idempotency.
249. API Request Logging
No tokens.
250. Provider Error Logging
Sanitize bodies.
251. Provider Request Trace
Include:
provider kind
operation
status
rate-limit info
not token.
252. Metrics
scm_api_requests
scm_api_latency
scm_rate_limited
scm_webhook_received
scm_webhook_invalid
scm_webhook_duplicate
scm_reconcile_lag
scm_check_publish_failures
scm_integration_unknown
253. Metric Labels
Low-cardinality:
provider_kind
operation
result_class
254. No RepositoryId Metric Label
Use traces/logs.
255. Tracing
scm.webhook.verify
scm.webhook.normalize
scm.change.fetch
scm.revision.resolve
scm.check.publish
scm.integration.submit
scm.reconcile
256. Health
Provider health contributes to project integration health.
257. Degraded Mode
Provider unavailable:
already-running jobs continue
new source triggers delayed
provider status sync delayed
258. Source Fetch Availability
If exact SourceSnapshot already in CAS, some reruns can continue without provider.
259. New Change Runs
May block until exact source fetched.
260. API Rate Limit Exhausted
Reconciliation/status delayed.
261. Prioritization
Protect:
source resolution
integration correctness
final checks
262. UI
SCM provider pages:
Providers
Installations
Repositories
Webhooks
Health
Rate Limits
263. Repository Integration Page
Shows:
provider
external repo
VCS
binding health
webhook status
last reconcile
264. Change Proposal UI
Provider deep link shown.
265. Check Publication UI
Status + provider link.
266. Integration UI
Shows provider capability/strategy.
267. Provider Limitation
Example:
provider cannot perform requested merge strategy
clear.
268. Installation Permission UI
Show granted/missing permissions.
269. Webhook Doctor UI
Last delivery/verification/reconcile.
270. Rate Limit UI
Admin diagnostics.
271. API Endpoints
Potential:
GET /v1/scm/providers
POST /v1/scm/providers
GET /v1/scm/installations
POST /v1/scm/bindings
GET /v1/scm/bindings/{id}
POST /v1/scm/bindings/{id}/reconcile
GET /v1/scm/bindings/{id}/health
272. Authorization Permissions
scm.read
scm.bind
scm.admin
scm.reconcile
scm.integrate
273. Provider Installation Admin
Tenant/org scoped.
274. Repository Binding Admin
Project/org scoped.
275. Integration Permission
Change integration still requires central Change/Policy permission.
276. Provider Permission Is Additional
Provider token must also have right.
277. Two-Layer Authorization
Forgeyard authz
+
provider API permission
278. Provider Denial
Surface as external authorization failure.
279. Local Standalone
Can use local Git without provider integration.
280. Provider Optional
Forgeyard does not require GitHub/GitLab.
281. Offline Mode
Local VCS-only workflows continue.
282. SCM Testkit
forgeyard-scm-testkit/src/
├── lib.rs
├── provider.rs
├── webhook.rs
├── repository.rs
├── change.rs
├── check.rs
├── integration.rs
├── rate_limit.rs
└── assertions.rs
283. Adapter Conformance Tests
Every provider adapter tests:
- repository fetch;
- change fetch;
- webhook verification;
- check publication;
- pagination;
- rate limit mapping;
- integration capability if supported.
284. Webhook Tests
invalid signature
duplicate delivery
unknown event
oversized body
repo binding mismatch
285. Source Identity Test
Provider branch update resolves exact revision + SourceSnapshotId.
286. Mutable Ref Test
Branch moves after webhook; run still uses webhook exact revision.
287. Fork Test
Source fork identity/source trust correct.
288. Review Mapping Test
Provider approval maps to normalized review with exact revision.
289. Actor Mapping Test
Same username without verified binding does not become internal PrincipalId.
290. Check Spoof Test
Untrusted third-party status cannot satisfy trusted required check.
291. Check Idempotency Test
Retry updates same semantic provider check.
292. Rate Limit Test
429 honors reset/retry.
293. Installation Revocation Test
New provider operations fail; existing Forgeyard history remains.
294. Missed Webhook Test
Drop provider event; reconcile catches updated proposal revision.
295. Integration Ambiguity Test
Provider merges then response times out; reconcile verifies resulting revision.
296. Integration Mismatch Test
Provider result snapshot differs from candidate -> critical failure.
297. Repository Rename Test
Native repository ID preserves binding.
298. Self-Hosted Version Test
Capability differences handled.
299. Credential Rotation Test
New provider credential works without binding recreation.
300. Security Test
Build workload cannot obtain provider installation token.
301. SSRF Test
Provider payload URL cannot force arbitrary outbound fetch.
302. Fuzzing
Fuzz:
webhook parser
normalized event conversion
provider pagination cursor
review/comment text handling
303. Failure Injection
provider outage
DNS failure
TLS failure
429
500
timeout
credential revocation
304. Scale Tests
thousands of repositories
many webhook events
large number of open changes
provider rate-limit pressure
305. Reconcile Scale
Use incremental cursors and bounded pages.
306. Implementation Phase 1 — Provider-Neutral Model
Implement:
ScmProviderId
RepositoryBinding
ScmCapabilities
normalized change/check models
307. Phase 2 — Webhook Framework
Signature, dedup, persistence, normalization.
308. Phase 3 — GitHub
First Tier-0 provider adapter.
309. Phase 4 — Change Proposal Mapping
Exact revision/source snapshot binding.
310. Phase 5 — Check/Status Publication
Run/check deep links.
311. Phase 6 — Provider Reconciliation
Missed webhook/status drift.
312. Phase 7 — Integration Submission
Precondition + post-merge verification.
313. Phase 8 — GitLab
Second major adapter.
314. Phase 9 — Gitea/Forgejo/Bitbucket
Additional adapters.
315. Phase 10 — Review/Comment/Label Enrichment
Optional features.
316. Phase 11 — Enterprise/Self-Hosted Hardening
Custom CA, proxy, provider versions.
317. Phase 12 — Scale/Fuzz/Failure Testing
Production hardening.
318. Acceptance Tests
- VCS and SCM provider are separate abstractions.
- GitHub PR maps to ChangeProposal, not core GitHubPR type.
- GitLab MR maps to same ChangeProposal model.
- Repository binding uses stable provider repository ID where available.
- Repo rename does not break binding.
- Webhook signature is verified before semantic processing.
- Duplicate webhook delivery is idempotent.
- Webhook repo identity must match binding.
- Mutable branch names never become build identity.
- Exact provider revision is resolved before run creation.
- SourceSnapshotId is canonical authority.
- Fork proposals are marked untrusted/fork.
- Provider token is never exposed to build workload.
- Provider approval is normalized before policy evaluation.
- Unlinked external actor does not automatically become Forgeyard principal.
- Trusted required checks cannot be spoofed by same display name.
- Check publication binds exact revision.
- Check retries are idempotent.
- Provider rate limits are honored.
- Missed webhook is repaired by reconciliation.
- Installation revocation blocks new provider actions.
- Existing Forgeyard history remains after provider revocation.
- Integration submit checks expected target revision.
- Ambiguous merge result is inspected before retry.
- Resulting provider revision is materialized and verified against approved candidate snapshot.
- Mismatch after provider integration is critical.
- Provider-specific SDKs remain adapter-local.
- Self-hosted provider base URLs/custom trust are supported.
- Provider feature limitations are exposed honestly.
- Local VCS workflows work without SCM provider.
- SCM outages do not corrupt already-authoritative Forgeyard state.
- External release/tag views do not replace Forgeyard release authority.
- Provider webhooks/events never carry secret values into domain events.
- Standalone/distributed share normalized SCM semantics.
- Forgeyard’s own repository integration dogfoods this subsystem.
319. Production Readiness Gates
Do not call SCM integrations production-ready until:
provider/VCS separation enforced
webhook verification/dedup stable
exact revision resolution proven
Change Proposal mapping stable
check publication idempotent
rate limit handling tested
provider reconciliation catches missed events
integration ambiguity handling proven
post-integration snapshot verification works
credential isolation tested
Additional providers can graduate independently after adapter conformance.
320. Architectural Invariants
- SCM provider != VCS;
- provider names never become core business types;
- ChangeProposal is provider-neutral;
- SourceSnapshotId is source authority;
- mutable refs are navigation only;
- webhooks are verified and deduplicated;
- webhooks are triggers, not sole correctness mechanism;
- reconciliation repairs missed provider events;
- provider actors require verified identity mapping;
- provider approvals are normalized before policy;
- trusted checks bind trusted origin, not display text only;
- check publication binds exact revision;
- provider credentials never reach untrusted build workloads;
- provider SDKs remain adapter-local;
- provider rate limits are first-class;
- state-changing provider calls are idempotent/reconciled;
- ambiguous integration is inspected before retry;
- integration checks expected target revision;
- resulting revision is re-materialized and snapshot-verified;
- provider UI is secondary to Forgeyard authority;
- repo rename/transfer is reconciled;
- installation revocation does not erase history;
- self-hosted endpoints are supported;
- insecure TLS is forbidden by default;
- provider-supplied URLs do not bypass outbound security;
- fork trust is explicit;
- local VCS mode does not require provider integration;
- provider capability limitations are honest;
- standalone/distributed share SCM semantics;
- Forgeyard dogfoods its SCM integration system.
321. Final Target Architecture
SCM Provider
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Webhook API App/OAuth
│ │ │
└─────────────┼─────────────┘
▼
Provider Adapter
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Repository Change Check/Review
Binding Normalizer Publisher
│ │ │
└─────────────┼─────────────┘
▼
Forgeyard Domain
│
┌───────────┼───────────┐
▼ ▼ ▼
ChangeProposal VCS SourceSnapshotId
│
▼
Run / Policy
322. Final Architectural Position
Inbound change flow:
verified webhook/provider poll
↓
external repository/change ID
↓
exact VCS source revision
↓
VCS adapter materialization
↓
SourceSnapshotId
↓
ProposalRevision
↓
checks/policy/run
Outbound check flow:
Forgeyard check result
↓
exact source revision
↓
provider check/status adapter
↓
idempotent publication
↓
reconciliation
Integration flow:
approved ProposalRevision
+
expected target revision
+
approved integration candidate snapshot
↓
provider submit
↓
success / failure / unknown
↓
inspect resulting revision
↓
materialize
↓
verify SourceSnapshotId
The key guarantee is:
Forgeyard can integrate deeply with GitHub, GitLab, Bitbucket, Forgejo, and future hosting providers without becoming structurally dependent on any of them. Provider APIs are synchronization edges; Forgeyard’s own VCS-neutral source snapshots, Change Proposals, policies, runs, and release records remain the internal source of truth.
323. New-Repository Sequence
01 Core Domain & Foundation
02 Storage & Metadata
03 CAS & Artifact Data Plane
04 Pipeline IR / Parsing / Planning
05 Run / Job State Machine
06 Scheduler
07 Runner / Agent
08 Sandbox & Executor
09 Transport / QUIC / Protocol
10 Events / Reconciliation
11 Policy / Authorization / Identity
12 Secrets & Trust
13 Supply Chain / SBOM / Provenance / Signing
14 Packaging
15 Release
16 Deployment
17 Observability / Health / Doctor
18 API / Axum
19 Dioxus UI
20 Device Lab
21 SCM Provider Integrations
22 HA / Coordination / Raft
23 RBE Interop
24 Plugin / Extension Architecture
25 Operations / Backup / Upgrade / DR
26 Self-Hosting / Bootstrap / Release of Forgeyard