@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/infrastructure/ARCHITECTURE.md +487 -0
  2. package/infrastructure/HEALTH.md +468 -0
  3. package/infrastructure/README.md +51 -0
  4. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  5. package/infrastructure/__tests__/template-generation.test.js +687 -0
  6. package/infrastructure/create-frigg-infrastructure.js +1 -1
  7. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  8. package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
  9. package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
  10. package/infrastructure/domains/database/aurora-builder.js +809 -0
  11. package/infrastructure/domains/database/aurora-builder.test.js +950 -0
  12. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  13. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  14. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  15. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  16. package/infrastructure/domains/database/migration-builder.js +633 -0
  17. package/infrastructure/domains/database/migration-builder.test.js +294 -0
  18. package/infrastructure/domains/database/migration-resolver.js +163 -0
  19. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  20. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  21. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  22. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  23. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  24. package/infrastructure/domains/health/application/ports/index.js +26 -0
  25. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  26. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  27. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  28. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  29. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  30. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  31. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  32. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  33. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  34. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  35. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  36. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  37. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  38. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  39. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  40. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  41. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  42. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  43. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  44. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  45. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  46. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  47. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  48. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  49. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  50. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  51. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  52. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  53. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  54. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  55. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  56. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  57. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  58. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  59. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  60. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  61. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  62. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  63. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  64. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  65. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  66. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  67. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  68. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  69. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  70. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  71. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  72. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  73. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  74. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  75. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  76. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  77. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  78. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  79. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  80. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  81. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  82. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  83. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  84. package/infrastructure/domains/integration/integration-builder.js +397 -0
  85. package/infrastructure/domains/integration/integration-builder.test.js +593 -0
  86. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  87. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  88. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  89. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  90. package/infrastructure/domains/networking/vpc-builder.js +1829 -0
  91. package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
  92. package/infrastructure/domains/networking/vpc-discovery.js +177 -0
  93. package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
  94. package/infrastructure/domains/networking/vpc-resolver.js +324 -0
  95. package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
  96. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  97. package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
  98. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  99. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  100. package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
  101. package/infrastructure/domains/security/kms-builder.js +366 -0
  102. package/infrastructure/domains/security/kms-builder.test.js +374 -0
  103. package/infrastructure/domains/security/kms-discovery.js +80 -0
  104. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  105. package/infrastructure/domains/security/kms-resolver.js +96 -0
  106. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  107. package/infrastructure/domains/shared/base-builder.js +112 -0
  108. package/infrastructure/domains/shared/base-resolver.js +186 -0
  109. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  110. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  111. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  112. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  113. package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
  114. package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
  115. package/infrastructure/domains/shared/environment-builder.js +119 -0
  116. package/infrastructure/domains/shared/environment-builder.test.js +247 -0
  117. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
  118. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
  119. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  120. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  121. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  122. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  123. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  124. package/infrastructure/domains/shared/resource-discovery.js +192 -0
  125. package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
  126. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  127. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  128. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  129. package/infrastructure/domains/shared/types/index.js +46 -0
  130. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  131. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  132. package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
  133. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  134. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  135. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  136. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  137. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
  138. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
  139. package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
  140. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  141. package/infrastructure/esbuild.config.js +53 -0
  142. package/infrastructure/infrastructure-composer.js +87 -0
  143. package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
  144. package/infrastructure/scripts/build-prisma-layer.js +553 -0
  145. package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
  146. package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
  147. package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
  148. package/layers/prisma/nodejs/package.json +8 -0
  149. package/management-ui/server/utils/cliIntegration.js +1 -1
  150. package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
  151. package/package.json +11 -11
  152. package/frigg-cli/.eslintrc.js +0 -141
  153. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
  154. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
  155. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
  156. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
  157. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
  158. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
  159. package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
  160. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  161. package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
  162. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  163. package/frigg-cli/__tests__/utils/test-setup.js +0 -287
  164. package/frigg-cli/build-command/index.js +0 -65
  165. package/frigg-cli/db-setup-command/index.js +0 -193
  166. package/frigg-cli/deploy-command/index.js +0 -175
  167. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
  168. package/frigg-cli/generate-command/azure-generator.js +0 -43
  169. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  170. package/frigg-cli/generate-command/index.js +0 -332
  171. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  172. package/frigg-cli/generate-iam-command.js +0 -118
  173. package/frigg-cli/index.js +0 -75
  174. package/frigg-cli/index.test.js +0 -158
  175. package/frigg-cli/init-command/backend-first-handler.js +0 -756
  176. package/frigg-cli/init-command/index.js +0 -93
  177. package/frigg-cli/init-command/template-handler.js +0 -143
  178. package/frigg-cli/install-command/backend-js.js +0 -33
  179. package/frigg-cli/install-command/commit-changes.js +0 -16
  180. package/frigg-cli/install-command/environment-variables.js +0 -127
  181. package/frigg-cli/install-command/environment-variables.test.js +0 -136
  182. package/frigg-cli/install-command/index.js +0 -54
  183. package/frigg-cli/install-command/install-package.js +0 -13
  184. package/frigg-cli/install-command/integration-file.js +0 -30
  185. package/frigg-cli/install-command/logger.js +0 -12
  186. package/frigg-cli/install-command/template.js +0 -90
  187. package/frigg-cli/install-command/validate-package.js +0 -75
  188. package/frigg-cli/jest.config.js +0 -124
  189. package/frigg-cli/package.json +0 -54
  190. package/frigg-cli/start-command/index.js +0 -149
  191. package/frigg-cli/start-command/start-command.test.js +0 -297
  192. package/frigg-cli/test/init-command.test.js +0 -180
  193. package/frigg-cli/test/npm-registry.test.js +0 -319
  194. package/frigg-cli/ui-command/index.js +0 -154
  195. package/frigg-cli/utils/app-resolver.js +0 -319
  196. package/frigg-cli/utils/backend-path.js +0 -25
  197. package/frigg-cli/utils/database-validator.js +0 -161
  198. package/frigg-cli/utils/error-messages.js +0 -257
  199. package/frigg-cli/utils/npm-registry.js +0 -167
  200. package/frigg-cli/utils/prisma-runner.js +0 -280
  201. package/frigg-cli/utils/process-manager.js +0 -199
  202. package/frigg-cli/utils/repo-detection.js +0 -405
  203. package/infrastructure/aws-discovery.js +0 -1176
  204. package/infrastructure/aws-discovery.test.js +0 -1220
  205. package/infrastructure/serverless-template.js +0 -2094
  206. /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
  207. /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
  208. /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
  209. /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
  210. /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
  211. /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
  212. /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
@@ -0,0 +1,386 @@
1
+ # Orphan Detection: Relationship Analysis & Multiple Resource Warning System
2
+
3
+ ## Problem Statement
4
+
5
+ When `frigg doctor` detects multiple orphaned resources of the same type (e.g., 3 VPCs, 10 subnets), users cannot easily determine:
6
+ 1. Which orphaned resources are actually relevant to import
7
+ 2. Which orphaned resources are old/unused and should be deleted
8
+ 3. Whether any orphaned resources are being actively referenced by drifted resources
9
+
10
+ ## Real-World Example: acme-integrations-dev Stack
11
+
12
+ ### AWS Reality (Verified 2025-10-27)
13
+
14
+ **CloudFormation Stack State:**
15
+ - Stack Name: `acme-integrations-dev`
16
+ - VPCs in stack: **0** (stack doesn't manage VPC)
17
+ - Lambda functions: 16 (all with VPC configuration drift)
18
+
19
+ **Lambda Functions Configuration:**
20
+ ```json
21
+ {
22
+ "VpcId": "vpc-01f21101d4ed6db59", // AWS Default VPC (172.31.0.0/16)
23
+ "SubnetIds": [
24
+ "subnet-020d32e3ca398a041", // In default VPC
25
+ "subnet-0c186318804aba790" // In default VPC
26
+ ],
27
+ "SecurityGroupIds": [
28
+ "sg-0aca40438d17344c4" // In default VPC
29
+ ]
30
+ }
31
+ ```
32
+
33
+ **Orphaned Resources Detected:**
34
+
35
+ **3 VPCs (ALL with Frigg CloudFormation tags):**
36
+ 1. `vpc-0eadd96976d29ede7` (10.0.0.0/16)
37
+ - Tags: `aws:cloudformation:stack-name=acme-integrations-dev`, `aws:cloudformation:logical-id=FriggVPC`
38
+ - Status: **NOT in CloudFormation stack, NOT used by Lambdas**
39
+ - Conclusion: **Old unused VPC, should be deleted**
40
+
41
+ 2. `vpc-0e2351eac99adcb83` (10.0.0.0/16)
42
+ - Tags: `aws:cloudformation:stack-name=acme-integrations-dev`, `aws:cloudformation:logical-id=FriggVPC`
43
+ - Status: **NOT in CloudFormation stack, NOT used by Lambdas**
44
+ - Conclusion: **Old unused VPC, should be deleted**
45
+
46
+ 3. `vpc-020a0365610c05f0b` (10.0.0.0/16)
47
+ - Tags: `aws:cloudformation:stack-name=acme-integrations-dev`, `aws:cloudformation:logical-id=FriggVPC`
48
+ - Status: **NOT in CloudFormation stack, NOT used by Lambdas**
49
+ - Conclusion: **Old unused VPC, should be deleted**
50
+
51
+ **10 Subnets:** All belong to the 3 orphaned VPCs (not the default VPC being used)
52
+
53
+ **3 Security Groups:** All belong to the 3 orphaned VPCs (not the default VPC being used)
54
+
55
+ ### Key Insights
56
+
57
+ 1. **CloudFormation tags are misleading**: All 3 VPCs have identical CFN tags but NONE are in the stack
58
+ 2. **Lambda drift is expected**: Lambdas use default VPC which has NO Frigg tags
59
+ 3. **All orphaned resources are unused**: None of the 16 orphaned resources are being referenced
60
+ 4. **Old deployment artifacts**: The orphaned VPCs are likely from previous Frigg deployments that failed cleanup
61
+
62
+ ## Solution Design
63
+
64
+ ### Phase 1: Relationship Analysis
65
+
66
+ Analyze connections between:
67
+ - **Drift Issues** → Resources being referenced in actual values
68
+ - **Orphaned Resources** → Resources detected but not in stack
69
+ - **Resource Hierarchies** → VPCs contain subnets/SGs, subnets belong to VPCs
70
+
71
+ ### Phase 2: Extract Referenced Resource IDs
72
+
73
+ From property mismatch drift issues, extract resource IDs that are **actually being used**:
74
+
75
+ ```javascript
76
+ // Example drift issue:
77
+ {
78
+ propertyPath: "VpcConfig.SubnetIds",
79
+ expectedValue: "subnet-old-1,subnet-old-2",
80
+ actualValue: "subnet-020d32e3ca398a041,subnet-0c186318804aba790"
81
+ }
82
+
83
+ // Extract: ["subnet-020d32e3ca398a041", "subnet-0c186318804aba790"]
84
+ // These are the subnets actually being used (even if not orphaned)
85
+ ```
86
+
87
+ **Property Paths to Analyze:**
88
+ - `VpcConfig.SubnetIds` → Subnet IDs
89
+ - `VpcConfig.SecurityGroupIds` → Security Group IDs
90
+ - `VpcId` → VPC IDs
91
+ - `SubnetId` → Subnet IDs (for other resources)
92
+
93
+ ### Phase 3: Build Resource Relationship Graph
94
+
95
+ ```javascript
96
+ {
97
+ orphanedResources: [
98
+ {
99
+ physicalId: "vpc-0eadd96976d29ede7",
100
+ resourceType: "AWS::EC2::VPC",
101
+ metadata: {
102
+ isReferencedByDrift: false, // NOT referenced in any drift
103
+ containsReferencedResources: false, // No subnets/SGs in this VPC are referenced
104
+ relatedOrphans: [ // Other orphans in this VPC
105
+ "subnet-0ad31b5ee6814b8fa",
106
+ "sg-03abddb7fb50aeaff"
107
+ ],
108
+ priority: "LOW" // Unused, should delete not import
109
+ }
110
+ },
111
+ {
112
+ physicalId: "subnet-020d32e3ca398a041",
113
+ resourceType: "AWS::EC2::Subnet",
114
+ vpcId: "vpc-01f21101d4ed6db59", // In default VPC (not orphaned)
115
+ metadata: {
116
+ isReferencedByDrift: true, // ✅ Referenced by 16 Lambda functions
117
+ referencedBy: [ // Which resources reference this
118
+ "acme-integrations-dev-attio",
119
+ "acme-integrations-dev-auth",
120
+ // ... 14 more
121
+ ],
122
+ priority: "N/A" // Not orphaned, just drift reference
123
+ }
124
+ }
125
+ ]
126
+ }
127
+ ```
128
+
129
+ ### Phase 4: Multi-Resource Warning System
130
+
131
+ When multiple resources of the same type are detected, show contextual warnings:
132
+
133
+ ```
134
+ ⚠ WARNING: Multiple VPCs detected (3 orphaned)
135
+
136
+ Analysis:
137
+ • vpc-0eadd96976d29ede7 - No active references, contains 3 orphaned subnets [UNUSED]
138
+ • vpc-0e2351eac99adcb83 - No active references, contains 4 orphaned subnets [UNUSED]
139
+ • vpc-020a0365610c05f0b - No active references, contains 3 orphaned subnets [UNUSED]
140
+
141
+ Recommendation:
142
+ ❌ Do NOT import these VPCs - they are old deployment artifacts
143
+ ✅ Lambda functions use default VPC (vpc-01f21101d4ed6db59) - drift is expected
144
+ 🧹 Consider cleaning up orphaned VPCs to reduce clutter:
145
+ $ aws ec2 delete-vpc --vpc-id vpc-0eadd96976d29ede7
146
+ ```
147
+
148
+ ## Implementation Plan
149
+
150
+ ### 1. Add Relationship Analysis to AWSResourceDetector
151
+
152
+ ```javascript
153
+ class AWSResourceDetector {
154
+ /**
155
+ * Find orphaned resources with relationship metadata
156
+ */
157
+ async findOrphanedResourcesWithRelationships({
158
+ stackIdentifier,
159
+ stackResources,
160
+ driftIssues = []
161
+ }) {
162
+ // 1. Find orphaned resources (existing logic)
163
+ const orphans = await this.findOrphanedResources({
164
+ stackIdentifier,
165
+ stackResources
166
+ });
167
+
168
+ // 2. Extract referenced resource IDs from drift
169
+ const referencedIds = this._extractReferencedResourceIds(driftIssues);
170
+
171
+ // 3. Enrich orphans with relationship metadata
172
+ return this._enrichWithRelationshipMetadata(orphans, referencedIds);
173
+ }
174
+
175
+ /**
176
+ * Extract resource IDs being referenced in drift actualValue
177
+ */
178
+ _extractReferencedResourceIds(driftIssues) {
179
+ const referenced = {
180
+ vpcIds: new Set(),
181
+ subnetIds: new Set(),
182
+ securityGroupIds: new Set()
183
+ };
184
+
185
+ for (const issue of driftIssues) {
186
+ if (issue.type !== 'PROPERTY_MISMATCH') continue;
187
+
188
+ const { propertyPath, actualValue } = issue.mismatch;
189
+
190
+ // Extract subnet IDs from VpcConfig.SubnetIds
191
+ if (propertyPath === 'VpcConfig.SubnetIds') {
192
+ const subnetIds = actualValue.split(',');
193
+ subnetIds.forEach(id => referenced.subnetIds.add(id));
194
+ }
195
+
196
+ // Extract SG IDs from VpcConfig.SecurityGroupIds
197
+ if (propertyPath === 'VpcConfig.SecurityGroupIds') {
198
+ const sgIds = actualValue.split(',');
199
+ sgIds.forEach(id => referenced.securityGroupIds.add(id));
200
+ }
201
+ }
202
+
203
+ return {
204
+ vpcIds: Array.from(referenced.vpcIds),
205
+ subnetIds: Array.from(referenced.subnetIds),
206
+ securityGroupIds: Array.from(referenced.securityGroupIds)
207
+ };
208
+ }
209
+
210
+ /**
211
+ * Add relationship metadata to orphaned resources
212
+ */
213
+ _enrichWithRelationshipMetadata(orphans, referencedIds) {
214
+ return orphans.map(orphan => {
215
+ const metadata = {
216
+ isReferencedByDrift: false,
217
+ referencedBy: [],
218
+ relatedOrphans: [],
219
+ priority: 'LOW'
220
+ };
221
+
222
+ // Check if this orphan is actually referenced
223
+ if (orphan.resourceType === 'AWS::EC2::Subnet') {
224
+ metadata.isReferencedByDrift = referencedIds.subnetIds.includes(orphan.physicalId);
225
+ } else if (orphan.resourceType === 'AWS::EC2::SecurityGroup') {
226
+ metadata.isReferencedByDrift = referencedIds.securityGroupIds.includes(orphan.physicalId);
227
+ }
228
+
229
+ if (metadata.isReferencedByDrift) {
230
+ metadata.priority = 'HIGH';
231
+ }
232
+
233
+ return { ...orphan, metadata };
234
+ });
235
+ }
236
+
237
+ /**
238
+ * Analyze orphan summary for warnings
239
+ */
240
+ analyzeOrphanSummary(orphans) {
241
+ const summary = {
242
+ warnings: [],
243
+ multipleResourceTypes: []
244
+ };
245
+
246
+ // Group by resource type
247
+ const grouped = {};
248
+ for (const orphan of orphans) {
249
+ grouped[orphan.resourceType] = grouped[orphan.resourceType] || [];
250
+ grouped[orphan.resourceType].push(orphan);
251
+ }
252
+
253
+ // Check for multiples
254
+ for (const [type, resources] of Object.entries(grouped)) {
255
+ if (resources.length > 1) {
256
+ const shortType = type.replace('AWS::EC2::', '');
257
+ summary.warnings.push(
258
+ `Multiple ${shortType}s detected (${resources.length}). Review relationships before importing.`
259
+ );
260
+ summary.multipleResourceTypes.push(type);
261
+ }
262
+ }
263
+
264
+ return summary;
265
+ }
266
+ }
267
+ ```
268
+
269
+ ### 2. Update Health Check to Use Relationship Analysis
270
+
271
+ ```javascript
272
+ // domains/health/application/use-cases/check-stack-health-use-case.js
273
+
274
+ class CheckStackHealthUseCase {
275
+ async execute({ stackName, region }) {
276
+ // ... existing drift detection ...
277
+
278
+ // Enhanced orphan detection with relationships
279
+ const orphanedResources = await this.resourceDetector.findOrphanedResourcesWithRelationships({
280
+ stackIdentifier,
281
+ stackResources,
282
+ driftIssues: issues // Pass drift issues for analysis
283
+ });
284
+
285
+ // Analyze for warnings
286
+ const orphanSummary = this.resourceDetector.analyzeOrphanSummary(orphanedResources);
287
+
288
+ return {
289
+ // ... existing fields ...
290
+ orphanedResources,
291
+ orphanAnalysis: orphanSummary
292
+ };
293
+ }
294
+ }
295
+ ```
296
+
297
+ ### 3. Update Frigg Doctor Output
298
+
299
+ ```javascript
300
+ // Show relationship warnings
301
+ if (orphanAnalysis.warnings.length > 0) {
302
+ console.log('\n⚠ ORPHAN ANALYSIS WARNINGS:\n');
303
+ for (const warning of orphanAnalysis.warnings) {
304
+ console.log(` ${warning}`);
305
+ }
306
+ }
307
+
308
+ // Show orphan details with metadata
309
+ console.log('\n CRITICAL ISSUES:');
310
+ for (const orphan of orphanedResources) {
311
+ console.log(` [ORPHANED_RESOURCE] ${orphan.physicalId}`);
312
+ console.log(` Resource: ${orphan.resourceType}`);
313
+
314
+ if (orphan.metadata) {
315
+ if (orphan.metadata.isReferencedByDrift) {
316
+ console.log(` ✅ ACTIVELY USED - Referenced by ${orphan.metadata.referencedBy.length} drifted resources`);
317
+ console.log(` Fix: Import to CloudFormation stack`);
318
+ } else {
319
+ console.log(` ❌ UNUSED - Not referenced by any stack resources`);
320
+ console.log(` Fix: Consider deleting instead of importing`);
321
+ }
322
+ }
323
+ }
324
+ ```
325
+
326
+ ### 4. Update Frigg Repair to Handle Multiples
327
+
328
+ ```javascript
329
+ // frigg repair --import <stack-name>
330
+
331
+ if (orphanAnalysis.multipleResourceTypes.includes('AWS::EC2::VPC')) {
332
+ console.log('\n⚠ WARNING: Multiple VPCs detected');
333
+ console.log('Please review and select which VPC to import:\n');
334
+
335
+ const vpcs = orphanedResources.filter(o => o.resourceType === 'AWS::EC2::VPC');
336
+ for (let i = 0; i < vpcs.length; i++) {
337
+ const vpc = vpcs[i];
338
+ console.log(` ${i + 1}. ${vpc.physicalId} (${vpc.cidrBlock})`);
339
+ console.log(` Referenced: ${vpc.metadata.isReferencedByDrift ? 'Yes' : 'No'}`);
340
+ console.log(` Related Resources: ${vpc.metadata.relatedOrphans.length} subnets/SGs\n`);
341
+ }
342
+
343
+ // Prompt user to select or skip
344
+ const selection = await promptUser('Select VPC number to import (or "skip" to skip all): ');
345
+
346
+ if (selection === 'skip') {
347
+ console.log('Skipping VPC import');
348
+ // Remove VPCs from import list
349
+ }
350
+ }
351
+ ```
352
+
353
+ ## Testing Strategy
354
+
355
+ ### Test 1: Detect Unused Orphans
356
+ ✅ 3 VPCs with CFN tags but NOT in stack, NOT referenced → mark as unused
357
+
358
+ ### Test 2: Detect Referenced Non-Orphans
359
+ ✅ Subnets in default VPC referenced by Lambdas but NOT orphaned → show in analysis
360
+
361
+ ### Test 3: Multi-Resource Warning
362
+ ✅ Multiple VPCs detected → show warning + require user selection
363
+
364
+ ### Test 4: VPC Hierarchy Analysis
365
+ ✅ VPC contains subnets → link orphaned subnets to orphaned VPC
366
+
367
+ ## Benefits
368
+
369
+ 1. **Prevents Bad Imports**: Users won't accidentally import unused old VPCs
370
+ 2. **Clarifies Drift**: Explains why drift exists (using default VPC vs Frigg VPC)
371
+ 3. **Cleanup Guidance**: Identifies resources that should be deleted, not imported
372
+ 4. **Informed Decisions**: Shows relationships between resources before action
373
+ 5. **Reduces Errors**: Requires explicit selection when multiple resources exist
374
+
375
+ ## Next Steps
376
+
377
+ 1. ✅ Write TDD tests for relationship analysis
378
+ 2. ⬜ Implement `findOrphanedResourcesWithRelationships` method
379
+ 3. ⬜ Implement `_extractReferencedResourceIds` helper
380
+ 4. ⬜ Implement `_enrichWithRelationshipMetadata` helper
381
+ 5. ⬜ Implement `analyzeOrphanSummary` method
382
+ 6. ⬜ Update `CheckStackHealthUseCase` to use new method
383
+ 7. ⬜ Update `frigg doctor` output to show warnings
384
+ 8. ⬜ Update `frigg repair` to handle multiple resources
385
+ 9. ⬜ Test with real acme-integrations-dev data
386
+ 10. ⬜ Document relationship analysis in HEALTH.md