Problem Statement
RecourseOS evaluates each resource change individually. This misses dangerous patterns where:
- • Each action is safe in isolation
- • The combination is unrecoverable
Example: Deleting an RDS snapshot returns RECOVERABLE_WITH_EFFORT (you can recreate it). Deleting the RDS instance with backups enabled returns RECOVERABLE_FROM_BACKUP. But if both are in the same plan, and the snapshot was the backup, the sequence is UNRECOVERABLE.
How It Works
After evaluating each resource individually, RecourseOS runs cross-action analysis:
- 1. Collect all resource changes and their individual verdicts
- 2.Run each pattern's detection function against the plan
- 3. For matches that meet confidence thresholds, upgrade the plan-level summary
- 4. Include
crossActionRisksarray in the consequence report
Per-resource verdicts remain unchanged. The plan-level worstRecoverability and riskAssessment reflect the elevated risk when cross-action patterns match.
Pattern Catalog
Pattern 1: Backup and Protected Resource Both Deleted
ID: backup_and_protected_both_deleted
Detects: A backup/snapshot is deleted in the same plan as the resource it backs up.
| Scenario | Resources | Expected |
|---|---|---|
| Dangerous | Delete aws_db_snapshot.final + Delete aws_db_instance.prod | Match, upgrade to UNRECOVERABLE |
| Benign | Delete aws_db_snapshot.old + Modify aws_db_instance.prod | No match (instance not deleted) |
Pattern 2: Replica and Primary Both Deleted
ID: replica_and_primary_both_deleted
Detects: A replica is deleted in the same plan as its primary.
| Scenario | Resources | Expected |
|---|---|---|
| Dangerous | Delete aws_db_instance.replica + Delete aws_db_instance.primary | Match, upgrade to UNRECOVERABLE |
| Benign | Delete aws_db_instance.replica only | No match (primary survives) |
Pattern 3: Protection Disabled Then Resource Deleted
ID: protection_disabled_then_deleted
Detects: Deletion protection is removed AND the resource is deleted in the same plan.
| Scenario | Resources | Expected |
|---|---|---|
| Dangerous | Update deletion_protection: true→false + Delete same resource | Match, upgrade to UNRECOVERABLE |
| Benign | Update deletion_protection: true→false only | No match (not deleted) |
Output Schema
Cross-action risks appear in the crossActionRisks array on the consequence report:
{
"crossActionRisks": [
{
"pattern": "backup_and_protected_both_deleted",
"patternName": "Backup and protected resource both deleted",
"affectedResources": [
"aws_db_instance.production",
"aws_db_snapshot.production_final"
],
"relationship": {
"type": "backup",
"source": "aws_db_snapshot.production_final",
"target": "aws_db_instance.production",
"detectionMethod": "explicit_reference",
"confidence": "definite"
},
"explanation": "The backup is being deleted...",
"upgradedTier": 4,
"scopeWarning": "Analysis limited to resources in this plan..."
}
]
}The array is always present. Empty array means "we checked, nothing found."
Confidence Levels
Relationship detection produces confidence levels:
- • definite: Structural proof (explicit attribute reference, e.g., snapshot's
db_instance_identifier) - • probable: Strong inference from state lookup
- • possible: Heuristic match (naming convention)
Scope Limitations
- • Plan scope only: Can only analyze resources in the current plan. Cross-state, cross-account, or externally-managed resources are invisible.
- • State scope: Relationship detection relies on Terraform state when available. Without state, detection is limited to plan references.
- • Confidence levels: Naming-convention matches produce
possibleconfidence. Patterns can choose not to fire on low-confidence matches.
Every CrossActionRisk includes a scopeWarningwhen relevant. Consumers know what the engine couldn't see.