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.
Cross-action analysis catches these patterns.
How It Works
After evaluating each resource individually, RecourseOS runs cross-action analysis:
- Collect all resource changes and their individual verdicts
- Run each pattern's detection function against the plan
- For matches that meet confidence thresholds, upgrade the plan-level summary
- 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)
Patterns declare a minimumConfidence. The backup pattern requires probable or higher — naming-convention matches are too speculative for an UNRECOVERABLE verdict.
Scope Limitations
Cross-action analysis has explicit scope limits:
- 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 scopeWarning when relevant. Consumers know what the engine couldn't see.