findReplacements in packages/@aws-cdk/toolkit-lib/lib/api/deployments/deploy-stack.ts decides whether a change set contains a replacement from ResourceChange.PolicyAction alone, never consulting ResourceChange.Replacement. It has been that way since the initial commit. That predicate gates the --no-rollback replacement prompt and the Express Mode guard in #1969, so a replacement reported without a policy action would not be gated.
The gap is mechanical rather than merely unobserved. Per the SDK, Replacement is derived from RequiresRecreation and Evaluation: Always + Static yields True, while Always + Dynamic yields Conditional. A change that always recreates the resource is therefore reported as Conditional whenever CloudFormation can only evaluate it dynamically — and Conditional is precisely what detection excludes.
No change set with Replacement: "True" and no PolicyAction has been observed yet. Describing a real change set for the replacing change in #1931 gives both fields, so existing detection covered #1931 — which is why widening it was left out of #1969.
"PolicyAction": "ReplaceAndDelete",
"LogicalResourceId": "TaskDef54694570",
"ResourceType": "AWS::ECS::TaskDefinition",
"Replacement": "True"
Worth establishing whether a True-without-PolicyAction change set can be produced at all — an explicit DeletionPolicy: Retain, or a resource type without a deletion policy, are the candidates. If it can, widen to || change.Replacement === 'True'.
That still would not cover the Always + Dynamic case, which surfaces as Conditional, and no fix may gate on Conditional wholesale: CDKMetadata reports it on essentially every CDK deployment, so doing so would gate almost every express deployment. Separating the two means reading Details[].RequiresRecreation together with Evaluation instead of the summarised Replacement field.
#NNNN here means aws/aws-cdk-cli; note aws/aws-cdk#1931 is an unrelated closed issue.
findReplacementsinpackages/@aws-cdk/toolkit-lib/lib/api/deployments/deploy-stack.tsdecides whether a change set contains a replacement fromResourceChange.PolicyActionalone, never consultingResourceChange.Replacement. It has been that way since the initial commit. That predicate gates the--no-rollbackreplacement prompt and the Express Mode guard in #1969, so a replacement reported without a policy action would not be gated.The gap is mechanical rather than merely unobserved. Per the SDK,
Replacementis derived fromRequiresRecreationandEvaluation:Always+StaticyieldsTrue, whileAlways+DynamicyieldsConditional. A change that always recreates the resource is therefore reported asConditionalwhenever CloudFormation can only evaluate it dynamically — andConditionalis precisely what detection excludes.No change set with
Replacement: "True"and noPolicyActionhas been observed yet. Describing a real change set for the replacing change in #1931 gives both fields, so existing detection covered #1931 — which is why widening it was left out of #1969.Worth establishing whether a
True-without-PolicyActionchange set can be produced at all — an explicitDeletionPolicy: Retain, or a resource type without a deletion policy, are the candidates. If it can, widen to|| change.Replacement === 'True'.That still would not cover the
Always+Dynamiccase, which surfaces asConditional, and no fix may gate onConditionalwholesale:CDKMetadatareports it on essentially every CDK deployment, so doing so would gate almost every express deployment. Separating the two means readingDetails[].RequiresRecreationtogether withEvaluationinstead of the summarisedReplacementfield.#NNNNhere meansaws/aws-cdk-cli; noteaws/aws-cdk#1931is an unrelated closed issue.