Change Estimated Number of Rows if post-optimization rewrite pushes a filter Predicate into an operator.
There are cases (the ones I know of are related to bitmap filters) where a predicate is evaluated by an explicit Filter operator in the final output of the optimizer process, but then pushed into a scan operator in a post-optimization rewrite.
Example: Node ID 10 is a Clustered Index Scan with no Predicate, that returns 10 million rows to Node ID 9, a Filter with Predicate "PROBE(...)"; the 20 thousand rows that match the bitmap are then returned to Node ID 8. After the post-optimization rewrite, this changes and now there is no Node ID 9; Node ID 10 is still a Clustered Index Scan, now with a Predicate: "PROBE(...)", and returning 20 thousand rows to Node ID 8.
When such a post-optimization rewrite takes place, the Estimated Number of Rows property of the affected operator(s) is not changed. So in the example above, the Estimated Number of Rows for Node ID 10 would still be 10 million (based on the predicate still being evaluated in a separate Filter operator); but the Actual Number of Rows would be 20 thousand (based on the PROBE filter being pushed into the scan). If a user looks at this execution plan to troubleshoot a performance issue, they would be misled into thinking there's a huge cardinality estimation mistake.
Please add logic to the post-optimization rewrite to ensure that, when a Filter operator is removed by pushing its Predicate in its child operator, the Estimated Number of Rows property of that child operator is set to the Estimated Number of Rows property of the Filter operator. This will make troubleshooting execution plans easier.