TL;DR:git revert,git rebase, andgit cherry-pickall resolve a replayed diff with the same three-way comparison (oursvs.basevs.theirs) — a conflict is only raised whenourshas diverged frombasefor the same lines. Content nobody touches passes that check for free, silently, regardless of which of the three commands replays the diff over it. PR 2240 dropped six unrelated CSS rules this way. PR 2245 addedscripts/revert-safety-check.shto PR validation — a narrow, notify-only detector for the pattern — and it was proven live against a real pipeline run before AB#2610 closed.
PR 2204 ("Dynamic Feed Work") merged into imp/blazor-hybrid at
42499b9e.
It was judged premature and backed out via PR 2213
(commit e9dfe1a3,
landing as a20e73a6) —
a standard git revert, which restored whatever PR 2204 had removed or changed.
~30 unrelated real commits then landed on imp/blazor-hybrid normally — bug fixes, CI fixes, the AB#2602 signed-out home UI among them — up to
6101fb0b,
the branch tip immediately before PR 2240 merged. None of them touched the content the original revert had restored — it simply sat there, unchanged.
PR 2240 ("Reintroducing Dynamic Feed Logic and Architecture w/ Fallback to Mocks")
then reverted the revert — Revert "revert: back out premature Dynamic Feed merge (PR 2204) AB#2510", commit
7a63d96a —
literally re-applying PR 2204's original diff on top of 6101fb0b. This silently erased six CSS rule blocks in
ALPAMobile.Presentation/wwwroot/css/alpa-components.css, none related to Dynamic Feed — they simply happened to live in a file PR 2204 also touched:
| Ticket | What was dropped | Impact |
|---|---|---|
| AB#2528 / AB#2536 / AB#2391 | Bottom-nav clustering (reduced slot count + tablet idiom) | Signed-out / tablet nav stretches instead of clustering |
| AB#2527 | Landscape side-bar inset padding | Content runs under the side system bar/cutout in landscape |
| AB#2570 / AB#2526 / AB#2529 | Inline-PDF retry placement, iframe/canvas toggle, per-page canvas sizing | DocumentOpenPage.razor/alpa-pdf.js still emit these classes with no CSS to style them |
| AB#2545 / AB#2391 | Large-text-band hero-card image hiding, shortcut glyph-icon scaling | Accessibility regression at the largest OS text size |
| AB#2528 | Login CTA full-width inside LoginModal | CTA renders content-width instead of full-width in the modal path |
| AB#2538 | Immersive-landscape chrome-hiding media query, deleted outright | Feature silently does nothing — the triggering JS still fires |
A developer's own fixup — commit 4fcc5923,
"fixup: repair silent regressions from the revert-the-revert auto-merge" — caught and repaired a similar class of drop in Main.razor.cs and a handful of other files
before PR 2240 merged. It did not catch this one. A second merge in the same PR branch
(a3a2a4ca,
merging a stale feat/DynamicFeedWork branch) was independently audited and found to be a real, conflict-resolved merge with no leftover damage — the CSS drops trace to the revert-of-revert alone.
Tracked as AB#2608. A restoration covering all six blocks was opened as PR 2242 — since abandoned in favor of one focused PR per dropped block, so each can be reviewed and documented on its own rather than as one bundled change. PR 2244 restored the bottom-nav clustering rule (the first row in the table above, AB#2608's original finding) and has merged; the other five blocks (AB#2527, AB#2570/2526/2529, AB#2545/2391, the login CTA fix, and AB#2538) are not covered by any active PR as of this writing.
Logged-out 3-tab bar (HOME/JUMPSEAT/KCM), cropped to the tab strip, same device and app state across all three builds:
6101fb0b (pre-merge) — clustered, expected
Same revert-of-revert mechanism as the six AB#2608 blocks above, but a different rule discovered independently while verifying PR 2244 (not one of the six, and not caught by the automated check's initial sweep):
.alpa-bottom-nav's padding shorthand. git log -S on the line traces both halves to a single commit,
7e7defc3 "fix: bring the bottom tab bar to the design's 88pt AB#2536" — the vertical value stopped double-counting the safe-area
inset (bar was 114pt against an 88pt design), and the horizontal value moved 16px → 28px to match the design's 337pt content slot.
PR 2240 reverted both back to the pre-fix formula in one silent drop. Fixed in two PRs since the second half (horizontal) wasn't
recognized as part of the same ticket until after the first shipped:
6101fb0b (pre-merge) — 28px side inset
The 12px delta is hard to see at native resolution in the three shots above. Zoomed 3x on the bar's left edge, with the design-correct 28px inset marked as a yellow dashed line and the actual content edge marked solid (green = correct, red = wrong). The shaded red band on the bug frame is the exact 12px the icon encroaches into space that should be empty padding:
Found in the same investigation window as AB#2536/AB#2608, but different root causes — both fixed together in PR 2250 since they land in the exact same CSS rule. AB#2611 is
added code in PR 2240, not a dropped rule: AlpaPageTitleBar is app-wide chrome (AlpaScreen's shared title row), not orchestrator-rendered feed content, but PR 2240
wired TypographyTokenStyle.ClassFor(TitleToken) onto its title span. .alpa-typo-* rules are deliberately placed last in alpa-components.css specifically to win
same-specificity battles against every component rule — this let a feed-supplied MEC typography preset override the title bar's font-family/size/weight/transform/color, defeating the documented
--alpa-shell-font pin that keeps chrome on ALPA's Futura PT regardless of the active MEC theme. Fixed by dropping the class binding; TitleToken stays as an available-but-unused
parameter.
AB#2616 was found while verifying AB#2611's screenshots: the same rule also hardcoded text-transform: uppercase — a plain literal, not a token, so no theme mechanism
(leaked or otherwise) can explain it. git blame traces it to the same original commit as the (correct) --alpa-shell-font fix, 03bef3f8 "Implement typography token
style tests and document typography pipeline" (Bryon Bauer) — landed with no Figma/ticket reference, unlike every other deliberate rule in this file. Checked directly against the Figma source
(KCM Airports, node 4562:8250): the title reads "Airports" in title case, not uppercase — confirming this
was a deviation from spec, not intentional design work. Removed the line.
Reproduced on all three builds (/kcm/airports, signed-out reachable) by injecting the class the old code emits when TitleToken is populated (current feed/mock data never sets
it, so the AB#2611 leak isn't reachable through real content yet, but the mechanism is live) plus a simulated MEC theme matching UAL's real PageTitle preset — cropped to just the title-bar
chrome row:
6101fb0b (pre-merge) — no typography mechanism
Found investigating AB#2621 (a separate, non-revert theme.ALPA.json data defect). Three discrepancies against Figma node 21139:18737
landed on the same two selectors and were bundled into one PR (AB#2622) for that reason, but only one of the three is actually a revert-drop.
The drop: .alpa-btn's LOGIN button renders at font-weight: 700; the pre-merge build (6101fb0b) has font-weight: 400 at the identical rule,
confirmed live via computed style. git blame traces the 700 line to commit 7a63d96a3 — PR 2240's own revert-of-revert commit — the same silent-drop mechanism as
AB#2536/AB#2608 above: PR 2204's old diff carried 700; something later, deliberately, corrected it to 400; PR 2240's blind revert-of-revert re-applied the old value with no
conflict raised either way.
Not drops — pre-existing, confirmed identical on the pre-merge build too: the hero title and shortcut-card titles render at weight 700 where the established Lora-degradation convention
(.alpa-policy-detail-name, "Bold reads far heavier than the design") calls for 400; and the LOGIN button uses Futura PT where Figma specifies League Spartan (fully licensed, unlike Lora,
simply never wired for this button). Both predate PR 2240 entirely.
6101fb0b (pre-merge)
git revert, git rebase, and git cherry-pick are three different commands, but underneath they resolve a replayed diff identically — a three-way comparison of base (the diff's own starting point), theirs (what the diff changes to), and ours (the current branch). A tool raises a conflict only when ours has diverged from base for the same lines. If nobody touched those lines, ours == base, and theirs applies with zero conflict — full stop, regardless of which command orchestrates it.
This is not theoretical for this incident — it was verified directly. git diff between PR 2204's own pre-merge base
(42499b9e^1) and
6101fb0b (the tip immediately before PR 2240) shows zero difference for the dropped CSS hunks — byte-identical from before PR 2204 through the moment PR 2240 merged. Three attempts, same outcome:
| Attempt | What it does | Outcome |
|---|---|---|
| Revert (what actually happened) | git revert e9dfe1a3 on top of 6101fb0b | 0 conflicts. Silent drop. |
| Rebase (hypothetical) | Replay PR 2204's original commits onto 6101fb0b via git rebase --onto | Same base, same incoming diff — still 0 conflicts, same drop. |
| Cherry-pick (hypothetical) | Pick PR 2204's commits individually onto 6101fb0b | Same three-way check per commit — still 0 conflicts on the commit touching this file. |
The actual gap is a review gap, not a tooling gap. None of the three commands can flag "this diff deletes something nobody else touched" — that is a valid, conflict-free merge from git's point of view, not an error. Catching it requires reading the resulting diff against a known-good point before merging, specifically for content unrelated to the feature being reintroduced.
Diffing the full branch against the pre-merge tip and scanning every removed hunk for a ticket reference unrelated to the feature landing:
git diff 6101fb0b origin/imp/blazor-hybrid -- '**/*.css' \
| grep -B15 '^-' | grep -oE 'AB#[0-9]+|BUG-[0-9]+' | sort -u
# cross-reference each ticket found against AB#2510 / PR 2204 —
# anything unrelated is a drop candidate, not a supersede
Every ticket-tagged rationale comment removed by a real drop carries its own, unrelated ticket in its own text — the nth-last-child bottom-nav clustering rule's comment cited AB#2528/AB#2536, nothing to do with Dynamic Feed. That is the signature this doc's mitigation automates below.
scripts/revert-safety-check.sh, added to AzureDevOps/azdo_pr_validation_ios.yml in PR 2245, runs the detection above automatically:
Revert "..." commit (git's own convention for git revert).azdo_pr_validation_ios.yml, since it needs only git history and python3.Validated against this incident directly: pointed at PR 2240's real diff, it surfaces all six confirmed AB#2608 drops among 18 total candidates flagged (the rest were legitimate supersessions from unrelated work landing in the same files).
Known blind spots, by design (narrow detector, not exhaustive): a drop with no ticket-tagged comment nearby is invisible to this check. A silent drop inside a plain merge or rebase that never used git revert is not caught either — the trigger gate is deliberately "this PR contains a revert commit," the highest-risk, cheapest-to-detect pattern, not the only way this class of bug can occur.
PR 2245's own diff never touches a path azdo_pr_validation_ios.yml watches (AzureDevOps/* and scripts/* aren't in that pipeline's paths.include list) — so merging it proved the YAML was well-formed, but not that the step actually runs and flags correctly on a real hosted agent. That took a second, disposable PR.
Branched from the current imp/blazor-hybrid tip and made one commit: git revert -m 1 18c218cf — a clean revert of PR 2244, the real, already-merged fix for this incident's original finding (the bottom-nav clustering rule, §1 above). Real content already on the branch, not a synthetic fixture — a same-PR add-then-revert nets to zero diff against merge-base and would prove nothing (the check deliberately diffs merge-base→tip, the net effect, exactly so a same-PR fixup isn't flagged; see §4). Opened as PR 2248, titled TEST — DO NOT MERGE, targeting imp/blazor-hybrid — required, since that's the only branch the iOS pipeline triggers on.
ALPA Mobile PR Validation - iOS, run 4955, step "Check revert safety (non-blocking)":
revert-safety-check: PR contains 1 revert commit(s) -- scanning full diff for orphaned tickets
##[warning]revert-safety-check: this PR reverts something and its diff removes 3 ticket(s) this PR doesn't own -- may be a silent drop (docs/detail/revert-safety-postmortem.html). Verify each is a deliberate supersession, not a regression:
AB#2391: ALPAMobile.Presentation/wwwroot/css/alpa-components.css
AB#2528: ALPAMobile.Presentation/wwwroot/css/alpa-components.css
AB#2536: ALPAMobile.Presentation/wwwroot/css/alpa-components.css
Matched the prediction exactly: flagged AB#2391/AB#2528/AB#2536 — the tickets cited in the removed CSS comments — and correctly did not flag AB#2608, since that ticket appears in the revert commit's own auto-generated subject (Revert "Merged PR 2244: ... (AB#2608)") and is therefore treated as a ticket this PR already owns. The step itself reported succeeded, and the overall pipeline stayed green — the notify-only posture held under a real failure case, not just a clean one.
Cleanup: PR 2248 was abandoned immediately after this log was captured — never approved, auto-complete never set. The remote branch (test/AB2610-revert-safety-check-proof-01) and local worktrees were deleted the same session. Nothing from this test landed on imp/blazor-hybrid. Full trail on AB#2610, closed the same day.