37 — Forgeyard Static Analysis, Code Quality, Security Scanning & Findings Management System Architecture
Document type: Core Static Analysis, Code Quality, Security Scanning, Finding Lifecycle & Remediation System Architecture
Project: Forgeyard CI/CD
Subsystem: SAST, linters, type checking, code-quality analyzers, secret scanning, dependency/source security analysis, DAST integration hooks, normalized findings, deduplication, suppression, baselines, severity/confidence, remediation state, ownership, SCM annotations, quality/security policy gates, evidence retention, and scanner-provider integration
Architecture style: Analyzer-neutral, evidence-first, immutable observations, normalized finding identity, explicit source/artifact binding, policy-controlled gating, auditable suppressions, versioned scanner semantics, and no scanner becoming authorization or release authority
Status: Target production architecture extending the Forgeyard series beyond the original 01–26 plan
Relationship to prior work: Builds on Pipeline IR, Run/Job/Attempt, Test Intelligence, Dependency Governance, Supply Chain, Change Proposal, Search/Analytics, Audit, Notifications, Policy/Authz, Monorepo Intelligence, and Release/Deployment. It centralizes the static/security analysis concepts referenced elsewhere into one coherent subsystem.
1. Purpose
Forgeyard already builds, tests, packages, signs, and releases software.
Production CI/CD also needs to answer:
did the compiler/type checker find issues?
did a linter find unsafe or suspicious code?
did static analysis identify a vulnerability?
was a secret committed?
did code quality regress?
is this finding new or pre-existing?
is it a duplicate?
was it suppressed?
who approved that suppression?
is the issue fixed in this exact revision?
does this finding block merge/release?
Without a normalized findings subsystem, every scanner becomes its own silo:
cargo clippy output
Semgrep JSON
CodeQL SARIF
Trivy findings
secret scanner output
custom analyzer logs
compiler warnings
The central rule is:
Analyzers produce evidence. Forgeyard normalizes, stores, correlates, and evaluates that evidence; analyzers themselves never decide merge, release, deployment, or authorization.
A second rule is:
A finding is always bound to exact analyzed subject identity—source snapshot, artifact, package, image, or deployment target—not merely a branch name.
A third rule is:
Suppressions never delete or rewrite findings. They create auditable policy/context overlays with scope, reason, owner, and expiry.
2. Architectural Position
Analysis Job
│
▼
Analyzer Output
│
┌────────────┼────────────┐
▼ ▼ ▼
SAST Lint/Type Secrets
│ │ │
└────────────┼────────────┘
▼
Finding Normalizer
│
┌───────────┼───────────┐
▼ ▼ ▼
Deduplicate Correlate Classify
│ │ │
└───────────┼───────────┘
▼
Finding Store
│
┌────────────┼────────────┐
▼ ▼ ▼
Baseline Suppression Ownership
│
▼
Policy Facts
│
▼
Merge / Release Gate
3. Goals
The subsystem MUST:
- define analyzer identity;
- define scanner versions;
- normalize findings;
- support SAST;
- support linters;
- support compiler/type-check findings;
- support secret scanning;
- support dependency/code security findings;
- support artifact/image findings;
- support DAST evidence integration;
- preserve raw reports;
- define finding identity;
- deduplicate findings;
- correlate recurring findings;
- distinguish new/existing findings;
- support severity;
- support confidence;
- support ownership;
- support suppression;
- support suppression expiry;
- support baseline comparisons;
- support remediation lifecycle;
- support SCM annotations;
- support policy gates;
- support release evidence;
- support notifications;
- support audit;
- support search/analytics;
- support multi-tenancy;
- remain rebuildable/reprocessable.
4. Non-Goals
This subsystem does not:
replace rustc/clippy/Semgrep/CodeQL/etc.
replace vulnerability databases
replace test execution
replace policy engine
replace incident response
guarantee every scanner is correct
5. Workspace Structure
crates/analysis/
├── forgeyard-analysis/
├── forgeyard-analysis-model/
├── forgeyard-analysis-ingest/
├── forgeyard-analysis-normalize/
├── forgeyard-analysis-finding/
├── forgeyard-analysis-dedup/
├── forgeyard-analysis-baseline/
├── forgeyard-analysis-suppression/
├── forgeyard-analysis-ownership/
├── forgeyard-analysis-quality/
├── forgeyard-analysis-security/
├── forgeyard-analysis-health/
└── forgeyard-analysis-testkit/
Scanner adapters:
crates/analysis-adapters/
├── forgeyard-analysis-sarif/
├── forgeyard-analysis-clippy/
├── forgeyard-analysis-rustc/
├── forgeyard-analysis-semgrep/
├── forgeyard-analysis-codeql/
├── forgeyard-analysis-secret-scan/
├── forgeyard-analysis-trivy/
├── forgeyard-analysis-custom/
└── ...
Use modules first; split only when parser/provider/tool dependencies justify.
6. AnalyzerId
#![allow(unused)]
fn main() {
pub struct AnalyzerId(Digest);
}
Logical analyzer identity.
7. AnalyzerVersion
#![allow(unused)]
fn main() {
pub struct AnalyzerVersion(BoundedString);
}
8. Analyzer Descriptor
#![allow(unused)]
fn main() {
pub struct AnalyzerDescriptor {
pub id: AnalyzerId,
pub name: AnalyzerName,
pub version: AnalyzerVersion,
pub kind: AnalyzerKind,
pub ruleset: AnalyzerRulesetId,
}
}
9. Analyzer Kind
#![allow(unused)]
fn main() {
pub enum AnalyzerKind {
Compiler,
TypeChecker,
Linter,
StaticSecurity,
SecretScanner,
DependencySecurity,
ArtifactSecurity,
DynamicSecurity,
License,
CodeQuality,
Custom(AnalyzerKindId),
}
}
10. Ruleset Identity
#![allow(unused)]
fn main() {
pub struct AnalyzerRulesetId(Digest);
}
Includes:
rule configuration
rule versions
enabled/disabled rules
severity overrides
11. Why Ruleset Identity Matters
Same scanner version with different rules is different evidence.
12. AnalysisSubject
#![allow(unused)]
fn main() {
pub enum AnalysisSubject {
SourceSnapshot(SourceSnapshotId),
Artifact(ArtifactId),
Package(PackageId),
OciImage(CasObjectRef),
ReleaseCandidate(ReleaseCandidateId),
DeploymentRevision(DeploymentRevisionId),
}
}
13. Exact Subject Binding
Critical.
14. AnalysisRunId
#![allow(unused)]
fn main() {
pub struct AnalysisRunId(Ulid);
}
One logical analyzer execution.
15. Analysis Run
#![allow(unused)]
fn main() {
pub struct AnalysisRun {
pub id: AnalysisRunId,
pub analyzer: AnalyzerDescriptor,
pub subject: AnalysisSubject,
pub job_attempt: JobAttemptId,
pub raw_report: CasObjectRef,
pub state: AnalysisRunState,
}
}
16. Analysis Run State
#![allow(unused)]
fn main() {
pub enum AnalysisRunState {
Pending,
Processing,
Complete,
InvalidEvidence,
Failed,
}
}
17. Raw Report Preservation
Raw scanner output stored in CAS according to retention.
18. Normalizer Version
#![allow(unused)]
fn main() {
pub struct AnalysisNormalizerVersion(u16);
}
19. Reprocessing
Scanner report may be re-normalized later without rewriting original evidence.
20. FindingObservationId
#![allow(unused)]
fn main() {
pub struct FindingObservationId(Ulid);
}
21. FindingLogicalId
#![allow(unused)]
fn main() {
pub struct FindingLogicalId(Digest);
}
Represents a recurring logical issue across revisions where correlation is sufficiently stable.
22. Finding Observation
#![allow(unused)]
fn main() {
pub struct FindingObservation {
pub id: FindingObservationId,
pub logical_id: FindingLogicalId,
pub analysis_run: AnalysisRunId,
pub subject: AnalysisSubject,
pub rule: FindingRuleRef,
pub location: Option<FindingLocation>,
pub severity: FindingSeverity,
pub confidence: FindingConfidence,
pub message: SanitizedFindingMessage,
pub fingerprint: FindingFingerprint,
pub status: FindingObservationStatus,
}
}
23. Observation Immutability
Once normalized, do not mutate.
24. Finding Observation Status
#![allow(unused)]
fn main() {
pub enum FindingObservationStatus {
Present,
ResolvedInSubject,
Unknown,
}
}
25. Logical Finding Lifecycle
Separate from observation.
26. FindingLifecycleState
#![allow(unused)]
fn main() {
pub enum FindingLifecycleState {
Open,
Acknowledged,
Suppressed,
Remediated,
Reopened,
}
}
27. Why Separate
Observation is evidence.
Lifecycle is human/system workflow.
28. Finding Rule
#![allow(unused)]
fn main() {
pub struct FindingRuleRef {
pub analyzer: AnalyzerId,
pub rule_id: BoundedString,
pub rule_version: Option<BoundedString>,
}
}
29. Stable Rule ID
Use analyzer-native ID when stable.
30. SARIF
Natural interchange format for many analyzers.
31. SARIF Adapter
Can ingest:
tool
rules
results
locations
fingerprints
code flows
32. SARIF Is External Format
JSON is appropriate.
33. Core Model
Not tied to SARIF.
34. Finding Severity
#![allow(unused)]
fn main() {
pub enum FindingSeverity {
Informational,
Low,
Medium,
High,
Critical,
}
}
35. Native Severity
Preserve analyzer-native severity separately.
36. Severity Mapping
Versioned per adapter/ruleset.
37. Confidence
#![allow(unused)]
fn main() {
pub enum FindingConfidence {
Low,
Medium,
High,
Confirmed,
Unknown,
}
}
38. Severity != Confidence
Critical distinction.
39. Example
High severity + Low confidence.
40. Finding Category
#![allow(unused)]
fn main() {
pub enum FindingCategory {
Correctness,
Reliability,
Maintainability,
Security,
SecretExposure,
License,
Performance,
Style,
Custom(FindingCategoryId),
}
}
41. Security Taxonomy
Optional mappings:
CWE
OWASP category
CVE where applicable
42. Taxonomy Is Metadata
Not finding identity alone.
43. Finding Location
#![allow(unused)]
fn main() {
pub struct FindingLocation {
pub path: RepoRelativePath,
pub region: Option<SourceRegion>,
pub symbol: Option<SymbolRef>,
}
}
44. Artifact Finding
May have no source path.
45. Binary Location
Can use artifact component/path offset metadata.
46. Path Validation
Repo-relative only for source.
47. Finding Message
Sanitized.
48. Secret Scanner
Never store actual discovered secret value.
49. Secret Finding
Store:
secret type
location
fingerprint/hash
confidence
50. Secret Fingerprint
Keyed/hash representation where needed.
51. No Plaintext Secret
Critical.
52. Scanner Log
Must redact detected secret.
53. Secret Remediation
Rotate/revoke actual credential via Part 12 workflows.
54. Finding Fingerprint
#![allow(unused)]
fn main() {
pub struct FindingFingerprint(Digest);
}
55. Fingerprint Inputs
Potential:
rule
normalized path
symbol
context hash
analyzer-native fingerprint
56. Deduplication
Same logical issue reported by repeated runs should correlate.
57. Cross-Analyzer Dedup
Harder.
58. Baseline
Do not aggressively merge different analyzers.
59. Correlation Confidence
#![allow(unused)]
fn main() {
pub enum FindingCorrelationConfidence {
Exact,
Strong,
Heuristic,
Unknown,
}
}
60. Exact
Analyzer stable fingerprint.
61. Strong
Rule+symbol+context.
62. Heuristic
Presentation only unless policy allows.
63. No Silent Merge of Heuristic Findings
Critical.
64. New Finding
A logical finding absent in baseline subject.
65. Existing Finding
Present in baseline and candidate.
66. Resolved Finding
Present in baseline but absent in candidate, assuming comparable analysis.
67. Baseline Comparability
Must match:
analyzer version/ruleset compatibility
subject type
scope
configuration
68. Baseline Subject
Exact immutable identity.
69. Baseline Selection
#![allow(unused)]
fn main() {
pub enum FindingBaselineSelection {
Explicit(AnalysisSubject),
TargetBranchLatestAccepted,
LastStableRelease,
}
}
70. Mutable Ref Resolution
Resolve to exact subject before comparison.
71. FindingDiff
#![allow(unused)]
fn main() {
pub struct FindingDiff {
pub new: Vec<FindingLogicalId>,
pub existing: Vec<FindingLogicalId>,
pub resolved: Vec<FindingLogicalId>,
pub unknown: Vec<FindingObservationId>,
}
}
72. Unknown
Used when analysis not comparable/incomplete.
73. Do Not Call Missing Finding Resolved If Scanner Failed
Critical.
74. Analysis Completeness
#![allow(unused)]
fn main() {
pub enum AnalysisCompleteness {
Complete,
Partial,
Failed,
Unknown,
}
}
75. Partial
Some modules/files skipped.
76. Protected Gate
May require Complete.
77. Incremental Analysis
Part 34 may scan affected subset.
78. Scope Must Be Explicit
#![allow(unused)]
fn main() {
pub enum AnalysisScope {
Full,
AffectedOnly(AffectedSetId),
Paths(Vec<RepoRelativePath>),
ArtifactSubset(Vec<ArtifactComponentRef>),
}
}
79. Full Release Scan
Recommended for Stable.
80. PR Scan
Affected-only possible when policy accepts.
81. Scope Included in AnalysisRun Identity
Critical.
82. Static Analysis Execution
Runs as normal Forgeyard Job.
83. Analyzer Container/Toolchain
Pinned.
84. Network
Deny by default for source analyzers unless scanner requires remote service.
85. Remote SaaS Analyzer
Possible adapter.
86. Data Egress Policy
Explicit.
87. High-Assurance Mode
Can forbid source upload to external scanner.
88. Analyzer Trust Class
#![allow(unused)]
fn main() {
pub enum AnalyzerExecutionMode {
LocalTrustedTool,
SandboxedTool,
RemoteService,
Plugin,
}
}
89. Analyzer Credentials
SecretRef.
90. Source Upload
Exact, policy-authorized.
91. Minimal Data
Upload only required content.
92. Remote Result
Still normalized and independently policy-evaluated.
93. Compiler Diagnostics
Can be normalized as findings.
94. rustc
Warnings/errors.
95. Errors
Usually job failure itself.
96. Warnings
Can become quality findings.
97. Clippy
First-class Rust linter adapter.
98. Clippy Rule ID
Use lint name.
99. Deny Warnings
Policy/build config controls.
100. Formatting
cargo fmt --check is usually check outcome, not finding-heavy.
Can still report file-level findings.
101. Type Checkers
Examples:
mypy
pyright
tsc
102. Code Quality
Examples:
complexity
dead code
unsafe usage
duplication
API lint
architecture rules
103. Architecture Findings
Part 34 architecture checks can emit normalized findings.
104. Unsafe Rust
Potential custom quality/security rule.
105. Secret Scanning
Scan:
source snapshot
diff
commits/history optionally
artifacts optionally
106. Diff Secret Scan
Fast PR gate.
107. Full Secret Scan
Release/nightly/security baseline.
108. History Scan
High-cost optional.
109. Secret Revocation Workflow
Finding -> notification -> secret owner -> rotation.
110. Dependency Security
Part 36/13 findings can map into common Finding model where useful.
111. Avoid Duplicate Vulnerability Authority
Canonical vulnerability evidence remains Part 13.
112. Findings UI Can Project Both
Unified experience.
113. Artifact Security
Binary/image scanner.
114. DAST
Dynamic Application Security Testing.
115. DAST Target
Exact DeploymentRevisionId or preview environment.
116. DAST Safety
Only authorized Forgeyard-managed targets.
117. No Arbitrary Internet Scanning
Critical.
118. DAST Permission
analysis.dast.run
119. Production DAST
Separate high-risk permission/policy.
120. DAST Finding
Bind exact deployment/release target.
121. DAST Tool
Normal job/load-like sandbox.
122. Scan Rate Limits
Protect target.
123. Suppression
Never edit finding.
124. FindingSuppressionId
#![allow(unused)]
fn main() {
pub struct FindingSuppressionId(Ulid);
}
125. Suppression Record
#![allow(unused)]
fn main() {
pub struct FindingSuppression {
pub id: FindingSuppressionId,
pub finding: FindingLogicalId,
pub scope: SuppressionScope,
pub reason: BoundedString,
pub created_by: PrincipalId,
pub created_at: Timestamp,
pub expires_at: Timestamp,
pub tracking_ref: Option<ExternalTrackingRef>,
}
}
126. Suppression Scope
#![allow(unused)]
fn main() {
pub enum SuppressionScope {
ExactFinding,
RuleForPath,
RuleForProject,
AnalyzerRule,
}
}
127. Broad Suppression
Higher permission.
128. Expiry
Required/recommended.
129. Permanent Suppression
Strongly discouraged.
130. Suppression Reason
Mandatory.
131. Suppression Audit
Mandatory.
132. Suppressed Finding
Still visible.
133. UI Label
Suppressed until ...
134. Suppression Does Not Change Severity
Critical.
135. Suppression Policy
Policy decides whether suppressed findings count toward gate.
136. Security Critical
May disallow suppression entirely.
137. Suppression Expiry
Durable timer/reevaluation.
138. False Positive
Can be suppression reason category.
139. Accepted Risk
Separate reason category.
140. Compensating Control
Separate.
141. SuppressionReasonKind
#![allow(unused)]
fn main() {
pub enum SuppressionReasonKind {
FalsePositive,
AcceptedRisk,
CompensatingControl,
TemporaryException,
ToolLimitation,
}
}
142. Risk Acceptance
Can require security approver.
143. Finding Ownership
Derived from source ownership/component.
144. FindingOwner
#![allow(unused)]
fn main() {
pub struct FindingOwner {
pub principal: Option<PrincipalId>,
pub team: Option<OrganizationUnitId>,
pub source: OwnershipSource,
}
}
145. Ownership Is Routing
Not authorization.
146. Remediation State
#![allow(unused)]
fn main() {
pub enum RemediationState {
Untriaged,
Triaged,
Planned,
FixInProgress,
FixedPendingVerification,
VerifiedFixed,
Won'tFix,
}
}
147. Won’tFix
Requires reason/policy.
148. VerifiedFixed
Needs a later comparable analysis showing absence.
149. No Human “Fixed” Without Evidence
Can mark planned/fix-in-progress, but VerifiedFixed requires analysis.
150. Finding Ticket
Can link external issue tracker.
151. No Mandatory Issue Tracker
Forgeyard manages core lifecycle itself.
152. Finding Triage
Set:
owner
priority
remediation state
suppression request
153. Security Priority
Separate from analyzer severity if organization needs.
154. FindingPriority
#![allow(unused)]
fn main() {
pub enum FindingPriority {
P0,
P1,
P2,
P3,
P4,
}
}
155. New Finding Gate
Common PR policy:
no new High/Critical security findings
156. Existing Debt
Can be allowed while preventing new debt.
157. Baseline Strategy
Useful migration path for existing codebase.
158. “No New Findings”
Must ensure scanner completeness comparable.
159. Scanner Failure
Cannot yield zero-new-findings Pass.
160. Quality Fact
#![allow(unused)]
fn main() {
pub enum AnalysisQualityFact {
FindingCount(FindingCountFact),
NewFindings(NewFindingFact),
SecurityFindings(SecurityFindingFact),
SecretFindings(SecretFindingFact),
AnalysisCompleteness(AnalysisCompletenessFact),
}
}
161. Policy
Part 11 central policy.
162. Example
AnalysisCompleteness == Complete
AND
new Critical == 0
AND
new High security == 0
AND
secret findings == 0
163. Change Proposal
Bind exact ProposalRevisionId/SourceSnapshot.
164. Release Candidate
Can require full scan and freshness.
165. Deployment
DAST/runtime findings can gate rollout if policy.
166. Evidence Freshness
#![allow(unused)]
fn main() {
pub enum AnalysisEvidenceFreshness {
Fresh,
Stale,
Unknown,
}
}
167. Freshness Inputs
subject
analyzer version
ruleset
vulnerability DB version
time
168. Static Lint
Often tied mostly to subject/tool version.
169. Vulnerability Scanner
Database freshness matters.
170. Secret Scan
Subject exact; rule updates may matter.
171. Re-scan Without Rebuild
Possible for source/artifact.
172. Artifact Scan
Same immutable artifact can be rescanned with newer rules/database.
173. Finding History
Logical finding across observations.
174. First Seen
Exact source/release.
175. Last Seen
Exact.
176. Reopened
Previously absent then returns.
177. Severity Change
Preserve per-observation severity.
178. Logical Current Severity
Derived latest comparable observation.
179. Rule Rename
Adapter mapping may preserve logical identity if explicitly known.
180. Do Not Guess Rule Equivalence
Critical.
181. SARIF Fingerprints
Use when available.
182. PartialFingerprints
Can aid correlation.
183. Code Flows
Store sanitized structured evidence/CAS.
184. Fix Suggestion
Analyzer may provide patch.
185. Suggested Fix
Not auto-applied baseline.
186. SuggestedPatchId
#![allow(unused)]
fn main() {
pub struct SuggestedPatchId(Digest);
}
187. Apply Fix
Creates source change/Change Proposal revision.
188. Never Mutate Source Behind Review
Critical.
189. Auto-Fix
Can generate explicit patch/Change Proposal.
190. AI-Assisted Fix
Future optional, same rule: proposal only, never hidden mutation.
191. SCM Check Publishing
Checks:
Static Analysis
Security
Secrets
Code Quality
192. Annotations
Top findings.
193. Provider Limits
Respect limits; full detail in Forgeyard.
194. Annotation Location
Exact file/line from proposal source.
195. Stale Location
If revision changed, old annotation not reused.
196. Diff-Only Annotation
Prefer new findings.
197. Dioxus UI
Pages:
Findings
Security
Code Quality
Secrets
Suppressions
Analyzers
198. Finding Detail
Shows:
rule
severity
confidence
location
first seen
last seen
owner
suppression
raw evidence
analysis version
199. Security Finding View
CWE/CVE links where applicable.
200. Secret Finding View
Never show secret value.
201. Suppression UI
Requires:
reason
scope
expiry
tracking issue
202. Bulk Suppression
High risk.
203. Baseline UI
Compare candidate vs base.
204. New Findings Tab
High value.
205. Resolved Findings Tab
Motivating.
206. Scanner Health Page
Shows:
version
ruleset
last run
failure rate
coverage/completeness
207. Analyzer Configuration
RON.
208. Example
(
analyzers: [
(
id: "clippy",
scope: Full,
required: true,
),
(
id: "secret-scan",
scope: AffectedOnly,
required: true,
),
],
)
209. Pipeline Integration
Analysis declaration can be first-class metadata around normal jobs.
210. AnalysisReportDeclaration
#![allow(unused)]
fn main() {
pub struct AnalysisReportDeclaration {
pub format: AnalysisReportFormat,
pub path: RelativePath,
pub analyzer: AnalyzerRef,
pub required: bool,
}
}
211. Missing Required Report
AnalysisCompleteness::Failed/Partial, gate accordingly.
212. Scanner Exit Code
Does not replace parsed evidence.
213. Some Scanners Exit Nonzero on Findings
Adapter understands semantics.
214. Some Exit Nonzero on Tool Failure
Differentiate.
215. AnalyzerRunOutcome
#![allow(unused)]
fn main() {
pub enum AnalyzerRunOutcome {
Completed,
FindingsDetected,
ToolFailed,
TimedOut,
InfrastructureError,
InvalidReport,
}
}
216. FindingsDetected
Not automatically job failure.
Policy decides.
217. ToolFailed
Analysis incomplete.
218. Scanner Retry
New JobAttempt.
219. Original failure retained.
220. Analyzer Determinism
Some analyzers may be nondeterministic.
221. Repeatability
Can record.
222. Do Not Cache Scanner Result Blindly
Cache key must include:
subject
analyzer version
ruleset
database snapshot
scope
223. Vulnerability DB Snapshot
Part 13.
224. Static Analyzer Cache
Possible.
225. Secret Scanner Cache
Possible exact subject/ruleset.
226. Dynamic Scan
Usually run fresh.
227. Analysis Cache Hit
Still records evidence reuse, not new execution.
228. EvidenceReuseRecord
Explicit.
229. Analyzer Provider Trait
#![allow(unused)]
fn main() {
#[async_trait]
pub trait AnalysisAdapter {
async fn normalize(
&self,
report: CasObjectRef,
context: AnalysisNormalizationContext,
) -> Result<NormalizedAnalysis, AnalysisAdapterError>;
}
}
230. Parser Isolation
Complex/untrusted parsers can run sandboxed.
231. SARIF Parser
Bound:
file size
result count
codeflow depth
message size
locations
232. JSON Bomb
Bound.
233. Path Traversal
Reject invalid paths.
234. HTML
Escape.
235. Scanner Output URLs
Never auto-fetch.
236. Remote Scanner Callback
Authenticated webhook if used.
237. Callback Result
Bind analysis run/request ID.
238. Replay
Dedupe.
239. Remote Scanner Unknown Outcome
Inspect/reconcile.
240. Multi-Tenant
Every AnalysisRun/Finding belongs to tenant/project.
241. Cross-Tenant Finding
Forbidden.
242. Shared Analyzer Infrastructure
Does not share finding visibility.
243. External Scanner Tenant Isolation
Separate project/account/token or explicit provider mapping.
244. Source Confidentiality
Policy.
245. High-Assurance
Local analyzers only.
246. Audit
Audit:
suppression create/update/remove
risk acceptance
baseline admin change
analyzer policy change
manual override
247. Not Audit Every Finding
Finding store already evidence.
248. Critical Secret Finding
Can produce audit/security notification.
249. Notifications
Examples:
new Critical finding
secret exposure
suppression expiring
scanner repeatedly failing
250. Security Incident
Some findings may open IncidentId.
251. Do Not Auto-Escalate Every High Finding
Policy.
252. Search
Part 31 indexes:
rule
severity
category
owner
path
status
253. Analytics
Examples:
open finding count
new findings/week
mean remediation time
suppression age
scanner failure rate
254. No Vanity Single “Code Quality Score” Baseline
Critical.
255. If Score Added
Presentation only, with transparent formula.
256. Quality Trend
Prefer explicit counts/categories.
257. Metrics
analysis_runs_total
analysis_run_failures_total
analysis_findings_total
analysis_new_findings_total
analysis_secret_findings_total
analysis_suppressions_active
analysis_suppressions_expired
analysis_normalization_failures_total
258. Labels
Low-cardinality:
analyzer_kind
severity
category
result
259. No Rule/File/Tenant metric labels.
260. Tracing
analysis.run
analysis.normalize
analysis.correlate
analysis.diff
analysis.suppression
analysis.quality
261. Health
Checks:
analyzer registry
normalizer backlog
required analyzer failures
remote provider health
suppression expiry reconciliation
262. Doctor
forgeyard analysis doctor
263. Doctor Checks
analyzer installed/version
ruleset validity
report parser
required scanner availability
remote provider credentials
264. CLI
forgeyard analysis list
forgeyard findings
forgeyard findings new
forgeyard findings show <id>
forgeyard findings suppress <id>
forgeyard analysis compare
forgeyard analysis explain
265. analysis explain
Shows:
subject
analyzer/ruleset
scope
completeness
baseline
new/existing/resolved counts
policy result
266. Permissions
analysis.read
analysis.run
analysis.manage
finding.triage
finding.suppress
finding.risk_accept
analysis.override
dast.run
dast.production
267. Risk Acceptance
Higher permission than simple triage.
268. Manual Override
Does not mutate findings.
269. Override
Exact quality fact + subject + reason + principal.
270. Release Override
Stricter.
271. Finding Retention
Normalized history may be long-lived.
272. Raw Reports
Retention policy.
273. Secret Scan Raw Report
Especially sensitive—may require shorter/stricter retention.
274. CAS Classification
Restricted.
275. DR
Normalized metadata + suppression records backed up.
276. Raw reports in CAS according to retention.
277. Rebuild
Findings can be re-normalized from raw reports if retained.
278. Scanner Rerun
Can recreate current evidence from immutable source/artifact.
279. Historical Suppression
Must remain.
280. HA
Analysis ingest workers idempotent.
281. Duplicate Report
No duplicate logical observations.
282. Ingest Key
AnalysisRunId + report digest + normalizer version
283. Reconciler
Checks:
completed jobs with unprocessed reports
stuck processing
expired suppression
missing baseline diff
284. Finding Correlation Reconciler
Can improve derived logical links without rewriting observations.
285. Correlation Revision
Versioned.
286. Testkit
forgeyard-analysis-testkit/src/
├── lib.rs
├── report.rs
├── finding.rs
├── baseline.rs
├── suppression.rs
├── correlation.rs
├── quality.rs
└── assertions.rs
287. Unit Tests
Severity/confidence mapping.
288. SARIF Tests
Multiple producers.
289. Secret Leakage Test
Detected secret never persists plaintext.
290. Scanner Failure Test
Does not produce false zero findings.
291. Baseline Comparability Test
Different ruleset cannot silently claim resolved findings.
292. New Finding Test
Exact baseline/candidate.
293. Resolved Test
Only when analysis Complete/comparable.
294. Heuristic Correlation Test
Does not auto-merge as Exact.
295. Suppression Test
Finding remains present.
296. Suppression Expiry Test
Gate re-evaluates.
297. Broad Suppression Permission Test
Restricted.
298. Risk Acceptance Audit Test
Mandatory.
299. Source Revision Test
Old analysis cannot satisfy new ProposalRevision.
300. Release Full Scope Test
Affected-only PR scan cannot satisfy full-scan release requirement.
301. DAST Target Authorization Test
Cannot scan arbitrary host.
302. Cross-Tenant Test
No finding leakage.
303. Parser Bomb Test
Bounded.
304. Path Traversal Test
Rejected.
305. Remote Provider Replay Test
Dedupe.
306. Remote Provider Unknown Test
Reconcile.
307. Analyzer Cache Test
Ruleset/database change invalidates reuse.
308. DR Test
Suppressions/history restored.
309. Fuzzing
Fuzz:
SARIF
scanner JSON
path/location parsing
fingerprint inputs
310. Failure Injection
scanner crash
CAS report missing
remote scanner timeout
DB restart
normalizer worker loss
311. Scale Test
Millions of findings.
312. Implementation Phase 1 — Core Finding Model
Analyzer/subject/finding.
313. Phase 2 — SARIF + Rust/Clippy
First dogfood.
314. Phase 3 — Baseline/New-Finding Diff
Change Proposal value.
315. Phase 4 — Secret Scanning
Security-critical.
316. Phase 5 — Suppression/Risk Acceptance
Governance.
317. Phase 6 — Security Analyzer Integration
Semgrep/CodeQL-like adapters.
318. Phase 7 — Artifact/Dependency Projection
Unified findings view.
319. Phase 8 — Policy/Release Gates
Quality evidence.
320. Phase 9 — DAST
Controlled deployment targets.
321. Phase 10 — Search/Analytics/Notifications
Operational UX.
322. Phase 11 — Remote Scanner/Plugin Providers
Enterprise integrations.
323. Phase 12 — Scale/Fuzz/DR Hardening
Production readiness.
324. Acceptance Tests
- Analyzer output is evidence, not merge/release authority.
- Every analysis binds an exact immutable subject.
- Analyzer version and ruleset identity are recorded.
- Raw reports are preserved according to retention.
- Finding observations are immutable.
- Logical finding lifecycle is separate from observations.
- Severity and confidence are distinct.
- Analyzer-native severity is retained.
- Secret scanner never stores detected secret plaintext.
- Scanner failure cannot be interpreted as zero findings.
- Partial analysis cannot masquerade as Complete.
- Baseline comparison resolves mutable refs to exact subjects.
- New/existing/resolved finding classification requires comparable analysis.
- A finding is not declared resolved when scanner execution failed.
- Heuristic correlation cannot silently merge findings as exact identity.
- Suppression never edits/deletes finding evidence.
- Suppression has scope, reason, actor, and expiry.
- Broad suppressions require stronger permission.
- Risk acceptance is audited.
- Suppressed findings remain visible.
- Policy decides whether suppressed findings block.
- PR affected-only analysis cannot satisfy full release scan if policy requires full.
- Old analysis cannot satisfy a changed ProposalRevision.
- DAST can target only authorized Forgeyard-managed targets.
- Production DAST requires explicit permission/policy.
- Remote analyzer source egress is policy-controlled.
- External scanner credentials use SecretRef.
- Analyzer cache identity includes version/ruleset/database/scope.
- Search/analytics are derived from canonical findings.
- Tenant findings are isolated.
- Remote callback replay is deduplicated.
- Ingestion/normalization is idempotent/reconcilable.
- Parser upgrades create new normalization revisions.
- Standalone/distributed share finding semantics.
- Forgeyard dogfoods static/security analysis for its own codebase.
325. Production Readiness Gates
Do not call analysis/findings production-ready until:
immutable finding model is stable
SARIF/Rust adapters are hardened
scanner failure/completeness semantics are explicit
secret-value leakage tests pass
baseline/new-finding logic is exact
suppression/risk acceptance is audited and expiring
policy gates bind exact subjects
DAST target authorization is enforced
tenant isolation passes
normalization/reconciliation survives worker failures
326. Architectural Invariants
- analyzers produce evidence, not authority;
- every analysis binds exact subject identity;
- analyzer/ruleset/version is explicit;
- finding observations are immutable;
- logical lifecycle is separate from evidence;
- severity and confidence are separate;
- raw secret values never enter finding storage;
- scanner failure is never equivalent to no findings;
- partial analysis is explicit;
- baseline refs resolve to immutable subjects;
- resolved classification requires comparable complete analysis;
- correlation confidence is explicit;
- heuristic correlation never masquerades as exact;
- suppressions never rewrite findings;
- suppressions are scoped/reasoned/expiring/audited;
- policy decides suppression effect;
- source/artifact scan scope is explicit;
- affected-only analysis cannot satisfy broader policy silently;
- old findings cannot satisfy new revisions;
- remote source egress is policy-controlled;
- DAST targets are authorized and bounded;
- parser inputs are hostile/bounded;
- analyzer caches include all semantic inputs;
- search/analytics are derived;
- tenant finding data is isolated;
- remediation state never fabricates verified fix;
- parser upgrades create new interpretations;
- HA ingestion is idempotent/reconcilable;
- standalone/distributed share semantics;
- Forgeyard dogfoods its own findings system.
327. Final Target Architecture
Analyzer Job
│
▼
Raw Report
│
▼
Normalizer
│
┌───────────┼───────────┐
▼ ▼ ▼
Findings Scope Completeness
│ │ │
└───────────┼───────────┘
▼
Baseline / Correlation
│
┌───────────┼───────────┐
▼ ▼ ▼
New Existing Resolved
│ │ │
└───────────┼───────────┘
▼
Suppression Overlay
│
▼
Policy Facts
│
▼
Merge / Release Gate
328. Final Architectural Position
Static/security analysis:
exact source/artifact
+
analyzer version
+
ruleset
+
scan scope
↓
raw report
↓
normalized immutable findings
Baseline comparison:
candidate findings
+
exact comparable baseline
+
correlation confidence
+
analysis completeness
↓
new / existing / resolved / unknown
Suppression:
finding
+
reason
+
scope
+
actor
+
expiry
↓
suppression overlay
↓
policy evaluation
The key guarantee is:
Forgeyard can unify code quality and security analysis without trusting any single scanner as the arbiter of software safety. Findings remain immutable evidence tied to exact subjects, suppressions remain visible and auditable, scanner failures cannot produce false green results, and policy remains the final authority over merge, release, and deployment gates.
329. Extended Architecture 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
27 Multi-Tenancy / Quotas / Resource Governance
28 Audit / Compliance / Evidence Retention / Security Governance
29 Notifications / Alerting / Human Workflow
30 Entitlements / Licensing / Subscription / Commercial Access Control
31 Search / Indexing / Query / Operational Analytics
32 Test Results / Quality Gates / Coverage / Flaky-Test Intelligence
33 Benchmarking / Performance Regression / Load-Test / Capacity Intelligence
34 Monorepo Intelligence / Dependency Graph / Affected-Change / Incremental Execution
35 Developer Experience / Local Dev Environment / CLI Workflows / Reproducible Workstation
36 Dependency / Package Registry / Artifact Mirror / Software-Source Governance
37 Static Analysis / Code Quality / Security Scanning / Findings Management