Scorecard
After each run, Forgecroft produces a scorecard: one entry per active invariant that intersected with the change. Entries are sorted by severity and include the evidence that supported the verdict.
Verdict Labels
Section titled “Verdict Labels”Each scorecard entry carries one of three customer-facing labels:
| Label | Meaning |
|---|---|
| Compliant | Forgecroft found evidence that the property holds for this change. Evidence references are included in the entry. |
| Needs evidence | The invariant intersects with this change, but the available evidence is insufficient to prove or disprove the claim. The entry includes what would resolve it. |
| Violation | Forgecroft found evidence that the property is violated. The entry includes remediation guidance. |
Invariants that do not intersect with the change are not present in the scorecard. Absence means “not applicable,” not “passed.”
Severity Ordering
Section titled “Severity Ordering”Entries are always sorted from most to least severe:
- Violation
- Needs evidence
- Compliant
The rollup counts in the scorecard header reflect the same order.
Where the Scorecard Appears
Section titled “Where the Scorecard Appears”PR Comments
Section titled “PR Comments”When a run is triggered by a pull request, Forgecroft posts a scorecard summary as a PR comment. The header badge shows the rollup:
❌ 1 · ⚠ 2 · ✅ 5Each invariant with a Violation or Needs evidence verdict is listed individually with its label, intent text, and remediation or gap description.
forgecroft runs scorecard <run-id>Output:
Run: run-01j4k...Rollup: ❌ 1 ⚠ 2 ✅ 5
STATE SOURCE INTENT REMEDIATIONViolation policy no public S3 buckets Remove public-read ACL from aws_s3_bucket.assetsNeeds evidence policy all IAM roles require MFA Runtime MFA enforcement is not observable from plan outputNeeds evidence policy deletion protection on all production databases --Compliant policy all resources tagged with cost-center --...Add --json to get the full structured response.
Go SDK
Section titled “Go SDK”card, err := client.GetRunScorecard(ctx, runID)if err != nil { return err}fmt.Printf("Rollup: ❌ %d ⚠ %d ✅ %d\n", card.Rollup.Violation, card.Rollup.NeedsEvidence, card.Rollup.Compliant,)for _, entry := range card.Entries { fmt.Printf("[%s] %s\n", entry.StateLabel, entry.IntentText) if entry.Remediation != "" { fmt.Printf(" Remediation: %s\n", entry.Remediation) }}GET /runs/{id}/scorecardResponse:
{ "run_id": "run-01j4k...", "entries": [ { "invariant_id": "inv-uuid", "intent_text": "no public S3 buckets", "source": "customer", "state": "blocked", "state_label": "Violation", "remediation": "Remove public-read ACL from aws_s3_bucket.assets", "evidence_refs": ["ev-uuid-1"], "last_evaluated_at": "2026-05-11T10:05:00Z" }, { "invariant_id": "inv-uuid-2", "intent_text": "all IAM roles require MFA", "source": "customer", "state": "unprovable", "state_label": "Needs evidence", "remediation": "Runtime MFA enforcement is not observable from plan output", "evidence_refs": [], "last_evaluated_at": "2026-05-11T10:05:00Z" }, { "invariant_id": "inv-uuid-3", "intent_text": "all resources tagged with cost-center", "source": "customer", "state": "proven", "state_label": "Compliant", "evidence_refs": ["ev-uuid-2", "ev-uuid-3"], "last_evaluated_at": "2026-05-11T10:05:00Z" } ], "rollup": { "compliant": 5, "needs_evidence": 2, "violation": 1 }}Scorecard Entry Fields
Section titled “Scorecard Entry Fields”| Field | Description |
|---|---|
invariant_id | ID of the invariant this entry evaluates |
intent_text | The invariant’s intent text at evaluation time |
source | Who created the invariant (customer or system) |
state | Internal state value: proven, unprovable, or blocked |
state_label | Customer-facing label: Compliant, Needs evidence, or Violation |
remediation | Guidance for resolving a Violation or Needs evidence entry (omitted when Compliant) |
evidence_refs | IDs of the evidence records that support the verdict |
last_evaluated_at | Timestamp of the most recent evaluation |
Rollup Fields
Section titled “Rollup Fields”| Field | Description |
|---|---|
compliant | Count of entries with Compliant verdict |
needs_evidence | Count of entries with Needs evidence verdict |
violation | Count of entries with Violation verdict |
No Entries
Section titled “No Entries”If a run touches no resources that any active invariant covers, the entries array is empty:
No scorecard entries — change touched no active invariants.This is normal for changes that are entirely outside the scope of your authored invariants.
Related Guides
Section titled “Related Guides”- Invariants Overview — what invariants are and how they evaluate
- Authoring Invariants — creating and managing invariants
- Run Lifecycle — run statuses and state transitions