Merge "Fix same-origin check for link rewrites"
diff --git a/Documentation/backend_licenses.txt b/Documentation/backend_licenses.txt
index 1ab3119..dbea391 100755
--- a/Documentation/backend_licenses.txt
+++ b/Documentation/backend_licenses.txt
@@ -69,9 +69,11 @@
 * jetty:http
 * jetty:io
 * jetty:jmx
+* jetty:nested
 * jetty:security
 * jetty:server
 * jetty:servlet
+* jetty:session
 * jetty:util
 * jetty:util-ajax
 * log:log4j
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index 23cbaed..7a3c608 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -71,9 +71,11 @@
 * jetty:http
 * jetty:io
 * jetty:jmx
+* jetty:nested
 * jetty:security
 * jetty:server
 * jetty:servlet
+* jetty:session
 * jetty:util
 * jetty:util-ajax
 * log:log4j
diff --git a/Documentation/release_war_jars.txt b/Documentation/release_war_jars.txt
index 76927bf..953e2dc 100644
--- a/Documentation/release_war_jars.txt
+++ b/Documentation/release_war_jars.txt
@@ -48,12 +48,16 @@
 javax.inject
 javax.servlet-api
 jcl-over-slf4j
+jetty-ee8-nested
+jetty-ee8-security
+jetty-ee8-servlet
 jetty-http
 jetty-io
 jetty-jmx
 jetty-security
 jetty-server
-jetty-servlet
+jetty-servlet-api
+jetty-session
 jetty-util
 jetty-util-ajax
 jgit
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index f66f25c..824a757 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -4297,7 +4297,7 @@
 
 .Request
 ----
-  POST /projects/testproj/migrate-labels HTTP/1.0
+  POST /projects/testproj/migrate-labels:review HTTP/1.0
   Content-Type: application/json; charset=UTF-8
 ----
 
diff --git a/configs/skills/gerrit_hygiene_operations/SKILL.md b/configs/skills/gerrit_hygiene_operations/SKILL.md
index d267731..095cfe4 100644
--- a/configs/skills/gerrit_hygiene_operations/SKILL.md
+++ b/configs/skills/gerrit_hygiene_operations/SKILL.md
@@ -43,10 +43,9 @@
 : Syntax Normalization**              : linting mandates for frontend          :
 :                                     : TypeScript components. Strict          :
 :                                     : enforcement of formatting rules, such  :
-:                                     : as whitespace removal and line-length  :
-:                                     : constraints, ensures optimal diff      :
-:                                     : readability and prevents automated CI  :
-:                                     : pipeline failures.                     :
+:                                     : as line-length constraints, ensures    :
+:                                     : optimal diff readability and prevents  :
+:                                     : automated CI pipeline failures.        :
 | **Proprietary Infrastructure        | Establishes strict boundaries for      |
 : Encapsulation**                     : documenting public APIs by forbidding  :
 :                                     : the leakage of proprietary backend     :
@@ -145,43 +144,40 @@
 
 **Context:** This section defines the structural code style and linting mandates
 for frontend TypeScript components. Strict enforcement of formatting rules, such
-as whitespace removal and line-length constraints, ensures optimal diff
-readability and prevents automated CI pipeline failures.
+as line-length constraints, ensures optimal diff readability and prevents
+automated CI pipeline failures.
 
 ### Summary
 
 | Rule ID   | Principle / Constraint | Priority | Primary Symptom / Trap    |
 | :-------- | :--------------------- | :------- | :------------------------ |
-| **T2-01** | Strict Trailing        | Medium   | Chaining long promise     |
-:           : Whitespace and Line    :          : callbacks or variable     :
-:           : Length Formatting      :          : assignments on a single   :
-:           :                        :          : line, or leaving trailing :
-:           :                        :          : spaces on empty lines.    :
+| **T2-01** | Strict Line Length     | Medium   | Chaining long promise     |
+:           : Formatting             :          : callbacks or variable     :
+:           :                        :          : assignments on a single   :
+:           :                        :          : line.                     :
 
 --------------------------------------------------------------------------------
 
 ### Rules
 
-#### T2-01: Strict Trailing Whitespace and Line Length Formatting
+#### T2-01: Strict Line Length Formatting
 
 > **Rule:** Always format TypeScript files to strictly adhere to linting limits
-> by wrapping long chained expressions and stripping trailing whitespace from
-> empty lines. Never commit code that triggers structural style violations.
+> by wrapping long chained expressions. Never commit code that triggers structural
+> style violations.
 >
 > **What:** TypeScript frontend files must strictly adhere to linting standards
-> by removing trailing whitespaces on empty lines and properly wrapping long
-> chained expressions.
+> by properly wrapping long chained expressions.
 >
 > **Applies To:** TypeScript UI components (e.g., Lit elements like
 > `gr-reply-dialog.ts`).
 >
-> **Why:** Inconsistent formatting, trailing whitespaces, and over-extended
-> lines caused unnecessary diff noise and failed automated linting checks in the
-> frontend CI pipeline. Failing to adhere to this typically results in **Linting
-> Pipeline Failure**.
+> **Why:** Inconsistent formatting and over-extended lines caused unnecessary diff
+> noise and failed automated linting checks in the frontend CI pipeline. Failing
+> to adhere to this typically results in **Linting Pipeline Failure**.
 
 **Trap 1: Chaining long promise callbacks or variable assignments on a single
-line, or leaving trailing spaces on empty lines.**
+line.**
 
 **Don't:**
 
diff --git a/external_deps.lock.json b/external_deps.lock.json
index 00a1a49..c556eb5 100644
--- a/external_deps.lock.json
+++ b/external_deps.lock.json
@@ -168,18 +168,17 @@
     "org.commonmark:commonmark-ext-autolink": -1853742120,
     "org.commonmark:commonmark-ext-gfm-strikethrough": 350394231,
     "org.commonmark:commonmark-ext-gfm-tables": 1881582931,
-    "org.eclipse.jetty.ee8:jetty-ee8-nested": -1812530566,
-    "org.eclipse.jetty.ee8:jetty-ee8-security": 925022545,
-    "org.eclipse.jetty.ee8:jetty-ee8-servlet": 1799016170,
-    "org.eclipse.jetty:jetty-http": 1984891007,
-    "org.eclipse.jetty:jetty-io": 1765684893,
-    "org.eclipse.jetty:jetty-jmx": -1704061949,
-    "org.eclipse.jetty:jetty-security": 1160320567,
-    "org.eclipse.jetty:jetty-server": 280305722,
-    "org.eclipse.jetty:jetty-servlet": 697742931,
-    "org.eclipse.jetty:jetty-session": -834663410,
-    "org.eclipse.jetty:jetty-util": -1520256775,
-    "org.eclipse.jetty:jetty-util-ajax": -726746674,
+    "org.eclipse.jetty.ee8:jetty-ee8-nested": -1627496831,
+    "org.eclipse.jetty.ee8:jetty-ee8-security": 1632270986,
+    "org.eclipse.jetty.ee8:jetty-ee8-servlet": -1338697711,
+    "org.eclipse.jetty:jetty-http": -235909997,
+    "org.eclipse.jetty:jetty-io": -44878927,
+    "org.eclipse.jetty:jetty-jmx": 1781043564,
+    "org.eclipse.jetty:jetty-security": -1819173301,
+    "org.eclipse.jetty:jetty-server": -130172210,
+    "org.eclipse.jetty:jetty-session": -1378386067,
+    "org.eclipse.jetty:jetty-util": -444622835,
+    "org.eclipse.jetty:jetty-util-ajax": -622085534,
     "org.hamcrest:hamcrest": 1547523135,
     "org.jruby:jruby-complete": -2103568068,
     "org.json:json": -811907600,
@@ -202,7 +201,7 @@
     "org.slf4j:slf4j-ext": -916772347,
     "org.slf4j:slf4j-log4j12": -630224096,
     "org.slf4j:slf4j-reload4j": 1021466445,
-    "org.slf4j:slf4j-simple": -487947767,
+    "org.slf4j:slf4j-simple": 1999565066,
     "org.tukaani:xz": -1743168321,
     "repositories": 2019057769,
     "xerces:xercesImpl": -1165914651,
@@ -429,32 +428,30 @@
     "org.commonmark:commonmark-ext-gfm-tables": -1205584749,
     "org.commonmark:commonmark-ext-gfm-tables:jar:sources": 1341057091,
     "org.commonmark:commonmark:jar:sources": -1511261547,
-    "org.eclipse.jetty.ee8:jetty-ee8-nested": -581707497,
-    "org.eclipse.jetty.ee8:jetty-ee8-nested:jar:sources": 1832439134,
-    "org.eclipse.jetty.ee8:jetty-ee8-security": -216309662,
-    "org.eclipse.jetty.ee8:jetty-ee8-security:jar:sources": 1985769014,
-    "org.eclipse.jetty.ee8:jetty-ee8-servlet": -1110646724,
-    "org.eclipse.jetty.ee8:jetty-ee8-servlet:jar:sources": 1045866630,
+    "org.eclipse.jetty.ee8:jetty-ee8-nested": -1480285467,
+    "org.eclipse.jetty.ee8:jetty-ee8-nested:jar:sources": -2141029892,
+    "org.eclipse.jetty.ee8:jetty-ee8-security": -1089777429,
+    "org.eclipse.jetty.ee8:jetty-ee8-security:jar:sources": -374234244,
+    "org.eclipse.jetty.ee8:jetty-ee8-servlet": 709534403,
+    "org.eclipse.jetty.ee8:jetty-ee8-servlet:jar:sources": 1796299448,
     "org.eclipse.jetty.toolchain:jetty-servlet-api": 1364182673,
     "org.eclipse.jetty.toolchain:jetty-servlet-api:jar:sources": 736604807,
-    "org.eclipse.jetty:jetty-http": -293230884,
-    "org.eclipse.jetty:jetty-http:jar:sources": -1107669868,
-    "org.eclipse.jetty:jetty-io": -783344138,
-    "org.eclipse.jetty:jetty-io:jar:sources": 1011133100,
-    "org.eclipse.jetty:jetty-jmx": 359701595,
-    "org.eclipse.jetty:jetty-jmx:jar:sources": -1366075600,
-    "org.eclipse.jetty:jetty-security": -109984940,
-    "org.eclipse.jetty:jetty-security:jar:sources": -256108816,
-    "org.eclipse.jetty:jetty-server": -1931992277,
-    "org.eclipse.jetty:jetty-server:jar:sources": 887545786,
-    "org.eclipse.jetty:jetty-servlet": -2029359212,
-    "org.eclipse.jetty:jetty-servlet:jar:sources": 607287657,
-    "org.eclipse.jetty:jetty-session": 1317046857,
-    "org.eclipse.jetty:jetty-session:jar:sources": -1905516033,
-    "org.eclipse.jetty:jetty-util": 2108090903,
-    "org.eclipse.jetty:jetty-util-ajax": -1216843848,
-    "org.eclipse.jetty:jetty-util-ajax:jar:sources": 884169258,
-    "org.eclipse.jetty:jetty-util:jar:sources": -1854246232,
+    "org.eclipse.jetty:jetty-http": -1578822522,
+    "org.eclipse.jetty:jetty-http:jar:sources": -1317713476,
+    "org.eclipse.jetty:jetty-io": -225847225,
+    "org.eclipse.jetty:jetty-io:jar:sources": 116677681,
+    "org.eclipse.jetty:jetty-jmx": 1202016563,
+    "org.eclipse.jetty:jetty-jmx:jar:sources": -1552312519,
+    "org.eclipse.jetty:jetty-security": 501899561,
+    "org.eclipse.jetty:jetty-security:jar:sources": 1985223928,
+    "org.eclipse.jetty:jetty-server": -963731471,
+    "org.eclipse.jetty:jetty-server:jar:sources": -466994191,
+    "org.eclipse.jetty:jetty-session": 1869188771,
+    "org.eclipse.jetty:jetty-session:jar:sources": -1630590830,
+    "org.eclipse.jetty:jetty-util": -1027747345,
+    "org.eclipse.jetty:jetty-util-ajax": 573093314,
+    "org.eclipse.jetty:jetty-util-ajax:jar:sources": 455294676,
+    "org.eclipse.jetty:jetty-util:jar:sources": -280937557,
     "org.hamcrest:hamcrest": 1282317766,
     "org.hamcrest:hamcrest-core": 649657847,
     "org.hamcrest:hamcrest-core:jar:sources": -1646511374,
@@ -499,8 +496,8 @@
     "org.slf4j:slf4j-ext:jar:sources": 194861628,
     "org.slf4j:slf4j-reload4j": -1779685465,
     "org.slf4j:slf4j-reload4j:jar:sources": -1558574593,
-    "org.slf4j:slf4j-simple": -444090931,
-    "org.slf4j:slf4j-simple:jar:sources": 1767487902,
+    "org.slf4j:slf4j-simple": -1055624668,
+    "org.slf4j:slf4j-simple:jar:sources": 751828941,
     "org.tukaani:xz": -321288404,
     "org.tukaani:xz:jar:sources": -893332662,
     "xerces:xercesImpl": -1852819301,
@@ -1289,24 +1286,24 @@
     },
     "org.eclipse.jetty.ee8:jetty-ee8-nested": {
       "shasums": {
-        "jar": "876fd83d52002d26ecb1a145ed7ae2397742a77c5e8ea9c6d309071b41cbb59c",
-        "sources": "c4f2633f8501679b59a0156cd64a6b49f34c27abf9896b6113f45fa7f6ae7d4a"
+        "jar": "240df6fb5bb28545183cbac87f191c0d21074b17a2c9d4562581a68a8d79c347",
+        "sources": "45b7a070430ced248fab480132c2a02c98b64181b619f11fdc27291fc60cdefc"
       },
-      "version": "12.1.8"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty.ee8:jetty-ee8-security": {
       "shasums": {
-        "jar": "5643ebb9781e97b4ebc5096bea9533fefdeef535b1afa8b8f2c7524901a26dbe",
-        "sources": "e29ee9d16fcbaa271c12bb43ee39b29b2c04e1af5087aabf8d1cd18754175eef"
+        "jar": "85351106e71c1036256488bbd2c84879d6316c67cdce3b44dda7835daf82e3c9",
+        "sources": "d5e6227dfbde0e6beb3cf2976cc64049a85b828143bb328f3de3ef06e5a4803f"
       },
-      "version": "12.1.8"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty.ee8:jetty-ee8-servlet": {
       "shasums": {
-        "jar": "183551aaff43938c5fc0fa649b62521b279eacbd53b3e5ee692cb1a3c08a6f8e",
-        "sources": "c0bb488146c42812a19c6f8e4c9f43aa55c5cf1c700bfcd2400c0493279159c5"
+        "jar": "f593838935e9c15b9cd6bbe8707af6f5e8204f2732c88c84b654962bac660dbe",
+        "sources": "53b2477db964532be722a1ff8f53ab4d7f88b87012f4e2b4e3360c5ecd08aff0"
       },
-      "version": "12.1.8"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty.toolchain:jetty-servlet-api": {
       "shasums": {
@@ -1317,66 +1314,59 @@
     },
     "org.eclipse.jetty:jetty-http": {
       "shasums": {
-        "jar": "02c6514977f0051dfdecf8d0799acf7a88fd8008a5fd9320a92f2e5db45d297b",
-        "sources": "1851f55b408241a6ae692730dd9bda9d1ecf7f0be6a9ccc471affa5bf8d07b9c"
+        "jar": "090f276739fd9bf8c30511007caec669ea3804b1df1061c37f44467474bee71d",
+        "sources": "4d416e2686881085327d3a28e349029d4ae9fc9b44db831291633f18bbf01625"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty:jetty-io": {
       "shasums": {
-        "jar": "f6246a2cf0abcee7f0971217c0ce4cd30d8ce15a91530363457113907ab38690",
-        "sources": "f21960b5fe18c1fa4281aa0ba1ce8f2f7d4d8f00e64e08d536b6d6577ca92489"
+        "jar": "448fc0f8f6f5f7251fc46de8e3aae7da14bd7c571a8043dac3dc381d08228fa0",
+        "sources": "bf40abffd40e759b16b642e12dfacb7bca1d0c937f5456b62506cc78169f6b56"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty:jetty-jmx": {
       "shasums": {
-        "jar": "24b24e205b7f1a7812c781e95ac6154b044e09abf0d13432777fc0e7fbd2f4ac",
-        "sources": "5627d14d6d36d68f14a2765adfafe18852099e19821fe73a68adb93ed8819eb5"
+        "jar": "baaaf76b139335125bfa51109c80d271de73f40ee8b5270674f56e90a66e9d81",
+        "sources": "b45a4c63d7b7ea44ca6eb9b9c5fbf1d100069bec66942c83ee553d05335a1fcb"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty:jetty-security": {
       "shasums": {
-        "jar": "af923d4f395a73bf8ddcb754f42d7617c6b7055e37e5a6b625ed894f73107ae9",
-        "sources": "0f8718fe938f0c8a5c3098129b67d13381c1fef0d56765627ccc2e017fc2654b"
+        "jar": "2f34b7895cec4e3547a1b52e12e7e92b3f3a110bf3c17637f8743ca3f4e42f0c",
+        "sources": "9e55b7f04431d4cc723a85a18ddc910e71f5b1a284c28b059d297d07509b4ff8"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty:jetty-server": {
       "shasums": {
-        "jar": "ba957ae07da647023cfa52c923732aea1c67f5273a594cee1863365dfebb9a02",
-        "sources": "089098dce0a947401a52bd00056427e1a2e348a71340a00e496c3139c14bafcb"
+        "jar": "4b0108e87abada7027123deca17186249413232dad0a2bd58a4e70a987b5354a",
+        "sources": "5e7693e35285d286dcd0bfdc73d74e6824027eeaaa0a9b4c70c0080475c22f71"
       },
-      "version": "9.4.57.v20241219"
-    },
-    "org.eclipse.jetty:jetty-servlet": {
-      "shasums": {
-        "jar": "c5e9517974dec9e4606b2d810f4995ea81091b1e24bd9640cb45d8b2aefd722c",
-        "sources": "d3214106ebbfa9034aa041ea26caa080a99c494ae6275f88c95bc63796bc0367"
-      },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty:jetty-session": {
       "shasums": {
-        "jar": "ebe84dd41942d7adda5f7d1304095d274e7915999451d848e887cbd409f947ea",
-        "sources": "3741c771d7204c47d8e72ae5b6e834ab4710c9fe6118d4777bfd910a17bb847f"
+        "jar": "164649123d15a3f2be5c196e82aa652dff05c75362db71b0f4ab6bb35165020a",
+        "sources": "b11e8cfb2db04ac2d1633bccfd47ba9e1b3afca0b75d4354a38140ba3e94c83e"
       },
-      "version": "12.1.8"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty:jetty-util": {
       "shasums": {
-        "jar": "6ccbf678716778e316cc097d8aada4fe2a2e16c0bbfd8a1763204d6724b423f4",
-        "sources": "77d5935c637276d08da2e1141a7fe4d9db4a2d072b6b418b625b261009d0cb4c"
+        "jar": "c52b4ff62cdacca8a399c611231129e6bd1074db7e3892e78cd106725cdd0ef1",
+        "sources": "209630667d8e042f187bed4e754c3f8af7c5247888fc83b8dc97b5bb2134d435"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.10"
     },
     "org.eclipse.jetty:jetty-util-ajax": {
       "shasums": {
-        "jar": "67af50dd7714803b1bcdfabac181dd8b279d0ba6ba7fd27ea80c5b2099016542",
-        "sources": "19888b386e9d4e81c81ce94465e8263680e1abcd83f324ba1992d7ded099abad"
+        "jar": "73dfbf388b46c9e2afbc78823ea556f8d25cf851902f99873d03df90becc2b97",
+        "sources": "4617309092393ed2f6ed76d915f24b518ccbff8c8731124bf109e346607e86b3"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.10"
     },
     "org.hamcrest:hamcrest": {
       "shasums": {
@@ -1534,10 +1524,10 @@
     },
     "org.slf4j:slf4j-simple": {
       "shasums": {
-        "jar": "ddfea59ac074c6d3e24ac2c38622d2d963895e17f70b38ed4bdae4d780be6964",
-        "sources": "30b660e79419bfcebd678e75bdfe3644eaf325f50253a68395d93634da5953df"
+        "jar": "8268bd018a5709b07209e0d8ca6221a37584ba1bc12ba985b8335a82c648bdd0",
+        "sources": "977f636bc90dc879c56cd6b6158abf0ee77a6f4619ab23342781a273b743786b"
       },
-      "version": "2.0.17"
+      "version": "2.0.18"
     },
     "org.tukaani:xz": {
       "shasums": {
@@ -1824,32 +1814,36 @@
     ],
     "org.eclipse.jetty:jetty-http": [
       "org.eclipse.jetty:jetty-io",
-      "org.eclipse.jetty:jetty-util"
+      "org.eclipse.jetty:jetty-util",
+      "org.slf4j:slf4j-api"
     ],
     "org.eclipse.jetty:jetty-io": [
-      "org.eclipse.jetty:jetty-util"
+      "org.eclipse.jetty:jetty-util",
+      "org.slf4j:slf4j-api"
     ],
     "org.eclipse.jetty:jetty-jmx": [
-      "org.eclipse.jetty:jetty-util"
+      "org.eclipse.jetty:jetty-util",
+      "org.slf4j:slf4j-api"
     ],
     "org.eclipse.jetty:jetty-security": [
-      "org.eclipse.jetty:jetty-server"
+      "org.eclipse.jetty:jetty-server",
+      "org.slf4j:slf4j-api"
     ],
     "org.eclipse.jetty:jetty-server": [
-      "javax.servlet:javax.servlet-api",
       "org.eclipse.jetty:jetty-http",
-      "org.eclipse.jetty:jetty-io"
-    ],
-    "org.eclipse.jetty:jetty-servlet": [
-      "org.eclipse.jetty:jetty-security",
-      "org.eclipse.jetty:jetty-util-ajax"
+      "org.eclipse.jetty:jetty-io",
+      "org.slf4j:slf4j-api"
     ],
     "org.eclipse.jetty:jetty-session": [
       "org.eclipse.jetty:jetty-server",
       "org.slf4j:slf4j-api"
     ],
+    "org.eclipse.jetty:jetty-util": [
+      "org.slf4j:slf4j-api"
+    ],
     "org.eclipse.jetty:jetty-util-ajax": [
-      "org.eclipse.jetty:jetty-util"
+      "org.eclipse.jetty:jetty-util",
+      "org.slf4j:slf4j-api"
     ],
     "org.mockito:mockito-core": [
       "net.bytebuddy:byte-buddy",
@@ -3778,35 +3772,34 @@
     "org.eclipse.jetty:jetty-http": [
       "org.eclipse.jetty.http",
       "org.eclipse.jetty.http.compression",
+      "org.eclipse.jetty.http.content",
       "org.eclipse.jetty.http.pathmap"
     ],
     "org.eclipse.jetty:jetty-io": [
       "org.eclipse.jetty.io",
+      "org.eclipse.jetty.io.content",
+      "org.eclipse.jetty.io.internal",
       "org.eclipse.jetty.io.jmx",
       "org.eclipse.jetty.io.ssl"
     ],
     "org.eclipse.jetty:jetty-jmx": [
-      "org.eclipse.jetty.jmx",
-      "org.eclipse.jetty.util.log.jmx"
+      "org.eclipse.jetty.jmx"
     ],
     "org.eclipse.jetty:jetty-security": [
       "org.eclipse.jetty.security",
-      "org.eclipse.jetty.security.authentication"
+      "org.eclipse.jetty.security.authentication",
+      "org.eclipse.jetty.security.internal",
+      "org.eclipse.jetty.security.jaas",
+      "org.eclipse.jetty.security.jaas.callback",
+      "org.eclipse.jetty.security.jaas.spi"
     ],
     "org.eclipse.jetty:jetty-server": [
       "org.eclipse.jetty.server",
       "org.eclipse.jetty.server.handler",
       "org.eclipse.jetty.server.handler.gzip",
       "org.eclipse.jetty.server.handler.jmx",
-      "org.eclipse.jetty.server.jmx",
-      "org.eclipse.jetty.server.nio",
-      "org.eclipse.jetty.server.resource",
-      "org.eclipse.jetty.server.session"
-    ],
-    "org.eclipse.jetty:jetty-servlet": [
-      "org.eclipse.jetty.servlet",
-      "org.eclipse.jetty.servlet.jmx",
-      "org.eclipse.jetty.servlet.listener"
+      "org.eclipse.jetty.server.internal",
+      "org.eclipse.jetty.server.jmx"
     ],
     "org.eclipse.jetty:jetty-session": [
       "org.eclipse.jetty.session"
@@ -3816,7 +3809,7 @@
       "org.eclipse.jetty.util.annotation",
       "org.eclipse.jetty.util.component",
       "org.eclipse.jetty.util.compression",
-      "org.eclipse.jetty.util.log",
+      "org.eclipse.jetty.util.jndi",
       "org.eclipse.jetty.util.preventers",
       "org.eclipse.jetty.util.resource",
       "org.eclipse.jetty.util.security",
@@ -4537,8 +4530,6 @@
       "org.eclipse.jetty:jetty-security:jar:sources",
       "org.eclipse.jetty:jetty-server",
       "org.eclipse.jetty:jetty-server:jar:sources",
-      "org.eclipse.jetty:jetty-servlet",
-      "org.eclipse.jetty:jetty-servlet:jar:sources",
       "org.eclipse.jetty:jetty-session",
       "org.eclipse.jetty:jetty-session:jar:sources",
       "org.eclipse.jetty:jetty-util",
@@ -4835,8 +4826,6 @@
       "org.eclipse.jetty:jetty-security:jar:sources",
       "org.eclipse.jetty:jetty-server",
       "org.eclipse.jetty:jetty-server:jar:sources",
-      "org.eclipse.jetty:jetty-servlet",
-      "org.eclipse.jetty:jetty-servlet:jar:sources",
       "org.eclipse.jetty:jetty-session",
       "org.eclipse.jetty:jetty-session:jar:sources",
       "org.eclipse.jetty:jetty-util",
@@ -5215,12 +5204,14 @@
     },
     "org.eclipse.jetty:jetty-http": {
       "org.eclipse.jetty.http.HttpFieldPreEncoder": [
-        "org.eclipse.jetty.http.Http1FieldPreEncoder"
+        "org.eclipse.jetty.http.Http10FieldPreEncoder",
+        "org.eclipse.jetty.http.Http11FieldPreEncoder"
       ]
     },
     "org.eclipse.jetty:jetty-http:jar:sources": {
       "org.eclipse.jetty.http.HttpFieldPreEncoder": [
-        "org.eclipse.jetty.http.Http1FieldPreEncoder"
+        "org.eclipse.jetty.http.Http10FieldPreEncoder",
+        "org.eclipse.jetty.http.Http11FieldPreEncoder"
       ]
     },
     "org.jruby:jruby-complete": {
diff --git a/java/com/google/gerrit/pgm/http/jetty/BUILD b/java/com/google/gerrit/pgm/http/jetty/BUILD
index e006c91..f6416fb 100644
--- a/java/com/google/gerrit/pgm/http/jetty/BUILD
+++ b/java/com/google/gerrit/pgm/http/jetty/BUILD
@@ -24,6 +24,7 @@
         "//lib/guice:guice-assistedinject",
         "//lib/guice:guice-servlet",
         "//lib/jetty:jmx",
+        "//lib/jetty:nested",
         "//lib/jetty:server",
         "//lib/jetty:servlet",
         "//lib/log:log4j",
diff --git a/java/com/google/gerrit/pgm/http/jetty/HiddenErrorHandler.java b/java/com/google/gerrit/pgm/http/jetty/HiddenErrorHandler.java
index 1c43240..ff8b1e5 100644
--- a/java/com/google/gerrit/pgm/http/jetty/HiddenErrorHandler.java
+++ b/java/com/google/gerrit/pgm/http/jetty/HiddenErrorHandler.java
@@ -20,14 +20,14 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.util.http.CacheHeaders;
 import java.io.IOException;
+import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.ee8.nested.ErrorHandler;
+import org.eclipse.jetty.ee8.nested.Request;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.server.HttpConnection;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.ErrorHandler;
 
 class HiddenErrorHandler extends ErrorHandler {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -35,18 +35,17 @@
   @Override
   public void handle(
       String target, Request baseRequest, HttpServletRequest req, HttpServletResponse res)
-      throws IOException {
-    HttpConnection conn = HttpConnection.getCurrentConnection();
+      throws IOException, ServletException {
     baseRequest.setHandled(true);
     try {
       log(req);
     } finally {
-      reply(conn, res);
+      reply(baseRequest, res);
     }
   }
 
-  private void reply(HttpConnection conn, HttpServletResponse res) throws IOException {
-    byte[] msg = message(conn);
+  private void reply(Request baseRequest, HttpServletResponse res) throws IOException {
+    byte[] msg = message(baseRequest);
     res.setHeader(HttpHeader.CONTENT_TYPE.asString(), "text/plain; charset=ISO-8859-1");
     res.setContentLength(msg.length);
     try {
@@ -58,15 +57,12 @@
     }
   }
 
-  private static byte[] message(HttpConnection conn) {
-    String msg;
-    if (conn == null) {
-      msg = "";
-    } else {
-      msg = conn.getHttpChannel().getResponse().getReason();
-      if (msg == null) {
-        msg = HttpStatus.getMessage(conn.getHttpChannel().getResponse().getStatus());
-      }
+  private static byte[] message(Request baseRequest) {
+    // Preserve a custom reason phrase (e.g. from sendError(int, String)) when
+    // present, falling back to the standard HTTP status phrase otherwise.
+    String msg = baseRequest.getResponse().getReason();
+    if (msg == null) {
+      msg = Strings.nullToEmpty(HttpStatus.getMessage(baseRequest.getResponse().getStatus()));
     }
     return msg.getBytes(ISO_8859_1);
   }
diff --git a/java/com/google/gerrit/pgm/http/jetty/HttpLog.java b/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
index 8ad89b2..f279150 100644
--- a/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
+++ b/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
@@ -100,26 +100,27 @@
             null // MDC properties
             );
 
-    String uri = req.getRequestURI();
-    if (!Strings.isNullOrEmpty(req.getQueryString())) {
-      uri += "?" + LogRedactUtil.redactQueryString(req.getQueryString());
-    }
+    String path = req.getHttpURI().getPath();
+    String query = req.getHttpURI().getQuery();
+    String uri =
+        Strings.isNullOrEmpty(query) ? path : path + "?" + LogRedactUtil.redactQueryString(query);
+
     String user = (String) req.getAttribute(GetUserFilter.USER_ATTR_KEY);
     if (user != null) {
       event.setProperty(P_USER, user);
     }
 
-    set(event, P_HOST, req.getRemoteAddr());
+    set(event, P_HOST, Request.getRemoteAddr(req));
     set(event, P_METHOD, req.getMethod());
     set(event, P_RESOURCE, uri);
-    set(event, P_PROTOCOL, req.getProtocol());
+    set(event, P_PROTOCOL, req.getConnectionMetaData().getProtocol());
     set(event, P_STATUS, rsp.getStatus());
-    set(event, P_CONTENT_LENGTH, rsp.getContentCount());
-    set(event, P_LATENCY, System.currentTimeMillis() - req.getTimeStamp());
-    set(event, P_REFERER, req.getHeader("Referer"));
-    set(event, P_USER_AGENT, req.getHeader("User-Agent"));
-    set(event, P_COMMAND_STATUS, rsp.getHeader(GIT_COMMAND_STATUS_HEADER));
-    String traceId = rsp.getHeader(RestApiServlet.X_GERRIT_TRACE);
+    set(event, P_CONTENT_LENGTH, Response.getContentBytesWritten(rsp));
+    set(event, P_LATENCY, System.currentTimeMillis() - Request.getTimeStamp(req));
+    set(event, P_REFERER, req.getHeaders().get("Referer"));
+    set(event, P_USER_AGENT, req.getHeaders().get("User-Agent"));
+    set(event, P_COMMAND_STATUS, rsp.getHeaders().get(GIT_COMMAND_STATUS_HEADER));
+    String traceId = rsp.getHeaders().get(RestApiServlet.X_GERRIT_TRACE);
     if (traceId != null) {
       set(event, P_TRACE_ID, traceId);
     }
diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
index 65eb405..7cde777 100644
--- a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
+++ b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -52,7 +52,14 @@
 import javax.servlet.Filter;
 import javax.servlet.http.HttpSessionEvent;
 import javax.servlet.http.HttpSessionListener;
+import org.eclipse.jetty.ee8.nested.SessionHandler;
+import org.eclipse.jetty.ee8.servlet.DefaultServlet;
+import org.eclipse.jetty.ee8.servlet.FilterHolder;
+import org.eclipse.jetty.ee8.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee8.servlet.ServletHolder;
 import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.UriCompliance;
 import org.eclipse.jetty.io.ConnectionStatistics;
 import org.eclipse.jetty.jmx.MBeanContainer;
 import org.eclipse.jetty.server.Connector;
@@ -60,21 +67,14 @@
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.SslConnectionFactory;
-import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
-import org.eclipse.jetty.server.handler.RequestLogHandler;
 import org.eclipse.jetty.server.handler.StatisticsHandler;
-import org.eclipse.jetty.server.session.SessionHandler;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.util.BlockingArrayQueue;
-import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.eclipse.jgit.lib.Config;
@@ -259,15 +259,11 @@
 
     Handler app = makeContext(env, cfg, sessionHandler);
     if (cfg.getBoolean("httpd", "requestLog", !reverseProxy)) {
-      RequestLogHandler handler = new RequestLogHandler();
-      handler.setRequestLog(httpLogFactory.get());
-      handler.setHandler(app);
-      app = handler;
+      httpd.setRequestLog(httpLogFactory.get());
     }
     if (cfg.getBoolean("httpd", "registerMBeans", false)) {
       MBeanContainer mbean = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
       httpd.addEventListener(mbean);
-      httpd.addBean(Log.getRootLogger());
       httpd.addBean(mbean);
     }
 
@@ -313,6 +309,24 @@
       final ServerConnector c;
       HttpConfiguration config = defaultConfig(requestHeaderSize);
 
+      // Jetty 12 changed the default UriCompliance to RFC3986 (strict),
+      // which rejects two URI shapes Gerrit's REST API depends on:
+      //   - AMBIGUOUS_PATH_SEPARATOR: encoded '/' (%2F) inside a path
+      //     segment, used for project/branch names (e.g.
+      //     DELETE /projects/foo%2Fbar/branches/refs%2Fheads%2Ftest);
+      //   - AMBIGUOUS_PATH_ENCODING: an encoded character that itself
+      //     decodes to a reserved one (e.g. %25 decoding to '%'),
+      //     hit by /changes/%3C%25%3DFOO%25%3E~1/detail where the
+      //     decoded identifier '<%=FOO%>' contains a literal '%'.
+      // Allow exactly these two violations; broader presets like LEGACY
+      // also permit suspicious characters, USER_INFO, FRAGMENT etc. that
+      // Gerrit's REST surface does not need.
+      config.setUriCompliance(
+          UriCompliance.from(
+              EnumSet.of(
+                  UriCompliance.Violation.AMBIGUOUS_PATH_SEPARATOR,
+                  UriCompliance.Violation.AMBIGUOUS_PATH_ENCODING)));
+
       if (AuthType.CLIENT_SSL_CERT_LDAP.equals(authType) && !"https".equals(u.getScheme())) {
         throw new IllegalArgumentException(
             "Protocol '"
@@ -373,11 +387,27 @@
       } else if ("proxy-https".equals(u.getScheme())) {
         defaultPort = 8080;
         config.addCustomizer(new ForwardedRequestCustomizer());
+        // For a proxy that terminates TLS, mark every request as HTTPS
+        // unconditionally. ForwardedRequestCustomizer alone only sets
+        // isSecure() when the proxy sends X-Forwarded-Proto=https or
+        // X-Proxied-Https=on; this wrapper covers proxies that don't.
+        // Jetty 12's HttpConfiguration.Customizer returns a (possibly
+        // wrapped) Request, so wrap the URI's scheme and override isSecure().
         config.addCustomizer(
-            (connector, channelConfig, request) -> {
-              request.setScheme(HttpScheme.HTTPS.asString());
-              request.setSecure(true);
-            });
+            (request, responseHeaders) ->
+                new Request.Wrapper(request) {
+                  @Override
+                  public HttpURI getHttpURI() {
+                    return HttpURI.build(super.getHttpURI())
+                        .scheme(HttpScheme.HTTPS.asString())
+                        .asImmutable();
+                  }
+
+                  @Override
+                  public boolean isSecure() {
+                    return true;
+                  }
+                });
         c = newServerConnector(server, acceptors, config);
 
       } else {
@@ -428,9 +458,21 @@
   private HttpConfiguration defaultConfig(int requestHeaderSize) {
     HttpConfiguration config = new HttpConfiguration();
     config.setRequestHeaderSize(requestHeaderSize);
+    // Jetty 12 changed the default for relativeRedirectAllowed from false to
+    // true (https://github.com/jetty/jetty.project/issues/11947); restore the
+    // pre-Jetty-12 behaviour Gerrit's redirect handling expects.
+    config.setRelativeRedirectAllowed(false);
     config.setSendServerVersion(false);
     config.setSendDateHeader(true);
-    config.setBlockingTimeout(0);
+    // TODO(davido): consider configuring HttpConfiguration.setMinResponseDataRate
+    // and setMinRequestDataRate. Jetty 12 removed setBlockingTimeout (deprecated
+    // in Jetty 9) in favour of these minimum bytes/sec knobs. Gerrit's previous
+    // setBlockingTimeout(0) was an explicit opt-in to Jetty 9's special
+    // "0 == use idle timeout" semantic (the Jetty 9 default was -1, i.e.
+    // blocking-timeout disabled). In Jetty 12 the blocking-timeout knob no
+    // longer exists; the connector's idle timeout governs all stalled IO.
+    // Revisit if slow-client mitigation becomes desirable.
+
     return config;
   }
 
@@ -469,6 +511,13 @@
     return site.resolve(path);
   }
 
+  // BlockingArrayQueue's 3-arg constructor is deprecated for removal in
+  // Jetty 12.1.2+ -- the public API no longer offers a bounded-but-growable
+  // queue matching Gerrit's historical (initial = minThreads, grow up to
+  // maxCapacity) semantics. Revisit when Jetty 12.2 drops it: either switch
+  // to the unbounded constructor (Jetty's own recommendation) or to the
+  // fixed-size 1-arg one.
+  @SuppressWarnings("removal")
   private QueuedThreadPool threadPool(Config cfg, ThreadSettingsConfig threadSettingsConfig) {
     int maxThreads = threadSettingsConfig.getHttpdMaxThreads();
     int minThreads = cfg.getInt("httpd", null, "minthreads", 5);
@@ -502,7 +551,7 @@
       paths.add(p);
     }
 
-    final List<ContextHandler> all = new ArrayList<>();
+    final List<Handler> all = new ArrayList<>();
     for (String path : paths) {
       all.add(makeContext(path, env, cfg, sessionHandler));
     }
@@ -522,7 +571,7 @@
     return r;
   }
 
-  private ContextHandler makeContext(
+  private Handler makeContext(
       final String contextPath, JettyEnv env, Config cfg, SessionHandler sessionHandler) {
     final ServletContextHandler app = new ServletContextHandler();
 
@@ -601,6 +650,8 @@
     ds.setInitParameter("gzip", "true");
 
     app.setWelcomeFiles(new String[0]);
-    return app;
+    // ee8 ContextHandler implements Supplier<org.eclipse.jetty.server.Handler>;
+    // unwrap to the core Handler for installation into the server's handler tree.
+    return app.get();
   }
 }
diff --git a/java/com/google/gerrit/pgm/http/jetty/SameSiteFilter.java b/java/com/google/gerrit/pgm/http/jetty/SameSiteFilter.java
index ae30cd2..9e8f2d6 100644
--- a/java/com/google/gerrit/pgm/http/jetty/SameSiteFilter.java
+++ b/java/com/google/gerrit/pgm/http/jetty/SameSiteFilter.java
@@ -28,13 +28,23 @@
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponseWrapper;
-import org.eclipse.jetty.http.HttpCookie;
 import org.eclipse.jgit.lib.Config;
 
 @Singleton
 public class SameSiteFilter implements Filter {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
+  // Magic strings inherited from Jetty 9's HttpCookie.SAME_SITE_*_COMMENT
+  // public constants. In Jetty 12 these constants moved to
+  // org.eclipse.jetty.ee{8,9}.nested.Response as protected fields, so they are
+  // no longer accessible from application code -- but the underlying mechanism
+  // is preserved: the ee8 nested Response inspects a Cookie's comment for
+  // these markers and emits a proper SameSite attribute on the wire.
+  // See org.eclipse.jetty.ee8.nested.Response (the values are kept verbatim).
+  private static final String SAME_SITE_LAX_COMMENT = "__SAME_SITE_LAX__";
+  private static final String SAME_SITE_STRICT_COMMENT = "__SAME_SITE_STRICT__";
+  private static final String SAME_SITE_NONE_COMMENT = "__SAME_SITE_NONE__";
+
   private final String sameSite;
 
   @Inject
@@ -55,9 +65,9 @@
     }
     String sameSiteComment =
         switch (sameSite.toLowerCase()) {
-          case "lax" -> HttpCookie.SAME_SITE_LAX_COMMENT;
-          case "strict" -> HttpCookie.SAME_SITE_STRICT_COMMENT;
-          case "none" -> HttpCookie.SAME_SITE_NONE_COMMENT;
+          case "lax" -> SAME_SITE_LAX_COMMENT;
+          case "strict" -> SAME_SITE_STRICT_COMMENT;
+          case "none" -> SAME_SITE_NONE_COMMENT;
           default ->
               throw new ServletException(String.format("Invalid sameSite value: %s", sameSite));
         };
diff --git a/java/com/google/gerrit/server/account/AccountManager.java b/java/com/google/gerrit/server/account/AccountManager.java
index 62981a2..c017765 100644
--- a/java/com/google/gerrit/server/account/AccountManager.java
+++ b/java/com/google/gerrit/server/account/AccountManager.java
@@ -298,28 +298,49 @@
       accountUpdates.add(a -> a.setFullName(who.getDisplayName()));
     }
 
-    if (!realm.allowsEdit(AccountFieldName.USER_NAME)
-        && who.getUserName().isPresent()
-        && !who.getUserName().equals(user.getUserName())) {
+    String backfilledUsername = null;
+    if (who.getUserName().isPresent() && !who.getUserName().equals(user.getUserName())) {
       if (user.getUserName().isPresent()) {
-        logger.atWarning().log(
-            "Not changing already set username %s to %s",
-            user.getUserName().get(), who.getUserName().get());
+        // Username renames on login are intentionally not supported; use PutUsername instead.
+        // Only warn when the realm owns usernames and would prevent an explicit rename anyway.
+        if (!realm.allowsEdit(AccountFieldName.USER_NAME)) {
+          logger.atWarning().log(
+              "Not changing already set username %s to %s",
+              user.getUserName().get(), who.getUserName().get());
+        }
       } else {
-        logger.atWarning().log("Not setting username to %s", who.getUserName().get());
+        backfilledUsername = who.getUserName().get();
+        ExternalId userNameExtId = createUsername(user.getAccountId(), backfilledUsername);
+        accountUpdates.add(u -> u.addExternalId(userNameExtId));
       }
     }
 
     if (!accountUpdates.isEmpty()) {
-      Optional<AccountState> updatedAccount =
-          accountsUpdateProvider
-              .get()
-              .update(
-                  "Update Account on Login",
-                  user.getAccountId(),
-                  AccountsUpdate.joinDeltaConfigures(accountUpdates));
-      if (!updatedAccount.isPresent()) {
-        throw new StorageException("Account " + user.getAccountId() + " has been deleted");
+      try {
+        Optional<AccountState> updatedAccount =
+            accountsUpdateProvider
+                .get()
+                .update(
+                    "Update Account on Login",
+                    user.getAccountId(),
+                    AccountsUpdate.joinDeltaConfigures(accountUpdates));
+        if (!updatedAccount.isPresent()) {
+          throw new StorageException("Account " + user.getAccountId() + " has been deleted");
+        }
+      } catch (DuplicateExternalIdKeyException e) {
+        throw new AccountException(
+            "Cannot assign external ID \""
+                + e.getDuplicateKey().get()
+                + "\" to account "
+                + user.getAccountId()
+                + "; external ID already in use.",
+            e);
+      }
+      if (backfilledUsername != null) {
+        sshKeyCache.evict(backfilledUsername);
+        logger.atInfo().log(
+            "Backfilled missing username external ID %s for account %s",
+            backfilledUsername, user.getAccountId());
       }
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
index a4d28c2..b30ed8f 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
@@ -76,7 +76,8 @@
       if (Strings.isNullOrEmpty(nameOrEmail)) {
         continue;
       }
-      membersToRemove.add(accountResolver.resolve(nameOrEmail).asUnique().account().id());
+      membersToRemove.add(
+          accountResolver.resolveIncludeInactive(nameOrEmail).asUnique().account().id());
     }
     AccountGroup.UUID groupUuid = internalGroup.getGroupUUID();
     try {
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AbstractAccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AbstractAccountIT.java
index 75c92b1..7b2e2dc 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AbstractAccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AbstractAccountIT.java
@@ -179,7 +179,6 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 import javax.servlet.http.HttpServletResponse;
 import org.apache.http.HttpResponse;
@@ -3115,7 +3114,8 @@
     GroupMembership testGroupMembership =
         new GroupMembership() {
           @Override
-          public ImmutableSet<AccountGroup.UUID> intersection(Iterable<AccountGroup.UUID> groupUuids) {
+          public ImmutableSet<AccountGroup.UUID> intersection(
+              Iterable<AccountGroup.UUID> groupUuids) {
             return StreamSupport.stream(groupUuids.spliterator(), /* parallel= */ false)
                 .filter(this::contains)
                 .collect(toImmutableSet());
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
index 1db2414..8d3bb85 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
@@ -188,6 +188,53 @@
   }
 
   @Test
+  public void authenticateBackfillsMissingUsernameExternalId() throws Exception {
+    String username = "foo";
+    Account.Id accountId = Account.id(seq.nextAccountId());
+    ExternalId.Key gerritExtIdKey = externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, username);
+    ExternalId.Key usernameExtIdKey =
+        externalIdKeyFactory.create(ExternalId.SCHEME_USERNAME, username);
+    accountsUpdate.insert(
+        "Create Test Account",
+        accountId,
+        u -> u.addExternalId(externalIdFactory.create(gerritExtIdKey, accountId)));
+    assertNoSuchExternalIds(usernameExtIdKey);
+
+    AuthRequest who = authRequestFactory.createForUser(username);
+    AuthResult authResult = accountManager.authenticate(who);
+
+    assertAuthResultForExistingAccount(authResult, accountId, gerritExtIdKey);
+    assertExternalId(usernameExtIdKey, accountId, null);
+  }
+
+  @Test
+  public void authenticateDoesNotRenameExistingUsername() throws Exception {
+    String existingUsername = "foo";
+    String renamedUsername = "bar";
+    Account.Id accountId = Account.id(seq.nextAccountId());
+    ExternalId.Key gerritExtIdKey =
+        externalIdKeyFactory.create(ExternalId.SCHEME_GERRIT, existingUsername);
+    ExternalId.Key existingUsernameExtIdKey =
+        externalIdKeyFactory.create(ExternalId.SCHEME_USERNAME, existingUsername);
+    ExternalId.Key renamedUsernameExtIdKey =
+        externalIdKeyFactory.create(ExternalId.SCHEME_USERNAME, renamedUsername);
+    accountsUpdate.insert(
+        "Create Test Account",
+        accountId,
+        u ->
+            u.addExternalId(externalIdFactory.create(gerritExtIdKey, accountId))
+                .addExternalId(externalIdFactory.create(existingUsernameExtIdKey, accountId)));
+
+    AuthRequest who = authRequestFactory.createForUser(existingUsername);
+    who.setUserName(renamedUsername);
+    AuthResult authResult = accountManager.authenticate(who);
+
+    assertAuthResultForExistingAccount(authResult, accountId, gerritExtIdKey);
+    assertExternalId(existingUsernameExtIdKey, accountId, null);
+    assertNoSuchExternalIds(renamedUsernameExtIdKey);
+  }
+
+  @Test
   public void authenticateWithExternalUser() throws Exception {
     String username = "foo";
     Account.Id accountId = Account.id(seq.nextAccountId());
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index d7007b4..48e4939 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -218,6 +218,37 @@
   }
 
   @Test
+  public void removeInactiveMemberByUserName() throws Exception {
+    String inactiveMemberUserName = name("inactiveUser");
+    Account.Id inactiveAccountId =
+        accountOperations.newAccount().username(inactiveMemberUserName).inactive().create();
+    AccountGroup.UUID group = groupOperations.newGroup().addMember(inactiveAccountId).create();
+
+    gApi.groups().id(group.get()).removeMembers(inactiveMemberUserName);
+
+    ImmutableSet<Account.Id> members = groupOperations.group(group).get().members();
+    assertThat(members).isEmpty();
+  }
+
+  @Test
+  public void removeInactiveMemberByEmail() throws Exception {
+    String inactiveMemberEmail = "inactiveUser@example.com";
+    Account.Id inactiveAccountId =
+        accountOperations
+            .newAccount()
+            .username(name("inactiveUser"))
+            .inactive()
+            .preferredEmail(inactiveMemberEmail)
+            .create();
+    AccountGroup.UUID group = groupOperations.newGroup().addMember(inactiveAccountId).create();
+
+    gApi.groups().id(group.get()).removeMembers(inactiveMemberEmail);
+
+    ImmutableSet<Account.Id> members = groupOperations.group(group).get().members();
+    assertThat(members).isEmpty();
+  }
+
+  @Test
   public void addExternalGroups() throws Exception {
     AccountGroup.UUID group1 = groupOperations.newGroup().create();
     AccountGroup.UUID group2 = groupOperations.newGroup().create();
diff --git a/javatests/com/google/gerrit/pgm/BUILD b/javatests/com/google/gerrit/pgm/BUILD
index d30e7ee..34cce11 100644
--- a/javatests/com/google/gerrit/pgm/BUILD
+++ b/javatests/com/google/gerrit/pgm/BUILD
@@ -10,6 +10,7 @@
         "//java/com/google/gerrit/pgm/util",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/server/securestore/testing",
+        "//javatests/com/google/gerrit/util/http/testutil",
         "//lib:guava",
         "//lib:jgit",
         "//lib:jgit-junit",
diff --git a/javatests/com/google/gerrit/pgm/http/jetty/ProjectQoSFilterTest.java b/javatests/com/google/gerrit/pgm/http/jetty/ProjectQoSFilterTest.java
index 23c9724..8253b90 100644
--- a/javatests/com/google/gerrit/pgm/http/jetty/ProjectQoSFilterTest.java
+++ b/javatests/com/google/gerrit/pgm/http/jetty/ProjectQoSFilterTest.java
@@ -30,8 +30,6 @@
 import javax.servlet.AsyncEvent;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import org.eclipse.jetty.server.Request;
 import org.eclipse.jgit.lib.Config;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -163,12 +161,8 @@
     }
   }
 
-  private static final class FakeHttpServletRequest extends HttpServletRequestWrapper {
-
-    FakeHttpServletRequest() {
-      super(new Request(null, null));
-    }
-
+  private static final class FakeHttpServletRequest
+      extends com.google.gerrit.util.http.testutil.FakeHttpServletRequest {
     @Override
     public String getRemoteHost() {
       return "1.2.3.4";
diff --git a/lib/jetty/BUILD b/lib/jetty/BUILD
index fc029ed..10bcc82 100644
--- a/lib/jetty/BUILD
+++ b/lib/jetty/BUILD
@@ -6,16 +6,29 @@
     visibility = ["//visibility:public"],
     exports = [
         ":util-ajax",
-        "@external_deps//:org_eclipse_jetty_jetty_servlet",
+        "@external_deps//:org_eclipse_jetty_ee8_jetty_ee8_servlet",
     ],
-    runtime_deps = [":security"],
+    runtime_deps = [
+        ":nested",
+        ":security",
+    ],
+)
+
+java_library(
+    name = "nested",
+    data = ["//lib:LICENSE-Apache2.0"],
+    visibility = ["//visibility:public"],
+    exports = ["@external_deps//:org_eclipse_jetty_ee8_jetty_ee8_nested"],
 )
 
 java_library(
     name = "security",
     data = ["//lib:LICENSE-Apache2.0"],
     visibility = ["//visibility:public"],
-    exports = ["@external_deps//:org_eclipse_jetty_jetty_security"],
+    exports = [
+        "@external_deps//:org_eclipse_jetty_ee8_jetty_ee8_security",
+        "@external_deps//:org_eclipse_jetty_jetty_security",
+    ],
     runtime_deps = [":server"],
 )
 
@@ -25,6 +38,7 @@
     visibility = ["//visibility:public"],
     exports = [
         ":http",
+        ":session",
         "@external_deps//:org_eclipse_jetty_jetty_server",
     ],
 )
@@ -65,6 +79,12 @@
 )
 
 java_library(
+    name = "session",
+    data = ["//lib:LICENSE-Apache2.0"],
+    exports = ["@external_deps//:org_eclipse_jetty_jetty_session"],
+)
+
+java_library(
     name = "util-ajax",
     data = ["//lib:LICENSE-Apache2.0"],
     exports = ["@external_deps//:org_eclipse_jetty_jetty_util_ajax"],
diff --git a/modules/jgit b/modules/jgit
index 0675211..c46df0c 160000
--- a/modules/jgit
+++ b/modules/jgit
@@ -1 +1 @@
-Subproject commit 067521170e2a7308c6a1292a99900eadcee0b790
+Subproject commit c46df0c50f75b68430dcb12a200ee2cbdd7e50e1
diff --git a/plugins/commit-message-length-validator b/plugins/commit-message-length-validator
index 1e08c1e..275c528 160000
--- a/plugins/commit-message-length-validator
+++ b/plugins/commit-message-length-validator
@@ -1 +1 @@
-Subproject commit 1e08c1ef59b4c812ee046746869e0debc7569d9e
+Subproject commit 275c528d58a12d8c90cd9755240be06844fdea07
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 7d5c0df..caa60af 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -1320,7 +1320,7 @@
       {
         label: 'URL and title',
         shortcut: 'r',
-        value: `${changeURL}: ${this.change?.subject}`,
+        value: `${changeURL} - ${this.change?.subject}`,
       },
       {
         label: 'Markdown',
diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
index 9e538f6..ff1d059 100644
--- a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+++ b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
@@ -26,12 +26,12 @@
   ['application/xquery', 'xquery'],
   ['application/x-epp', 'epp'],
   ['application/x-erb', 'erb'],
-  ['text/ada', 'ada'],
   ['text/css', 'css'],
   ['text/html', 'html'],
   ['text/javascript', 'js'],
   ['text/jsx', 'jsx'],
   ['text/tsx', 'jsx'],
+  ['text/x-ada', 'ada'],
   ['text/x-c', 'cpp'],
   ['text/x-c++src', 'cpp'],
   ['text/x-clojure', 'clojure'],
diff --git a/tools/deps.toml b/tools/deps.toml
index 3b4b7af..75ffd76 100644
--- a/tools/deps.toml
+++ b/tools/deps.toml
@@ -8,7 +8,7 @@
 gitiles = "1.6.0"
 greenmail = "1.5.5"
 httpcomp = "4.5.14"
-jetty = "9.4.57.v20241219"
+jetty = "12.1.10"
 mail = "1.6.0"
 mime4j = "0.8.1"
 ow2 = "9.9.1"
@@ -73,12 +73,15 @@
 javax-inject = { module = "javax.inject:javax.inject", version = "1" }
 javax-mail = { module = "com.sun.mail:javax.mail", version.ref = "mail" }
 javax-servlet-api = { module = "javax.servlet:javax.servlet-api", version = "4.0.1" }
+jetty-ee8-nested = { module = "org.eclipse.jetty.ee8:jetty-ee8-nested", version.ref = "jetty" }
+jetty-ee8-security = { module = "org.eclipse.jetty.ee8:jetty-ee8-security", version.ref = "jetty" }
+jetty-ee8-servlet = { module = "org.eclipse.jetty.ee8:jetty-ee8-servlet", version.ref = "jetty" }
 jetty-http = { module = "org.eclipse.jetty:jetty-http", version.ref = "jetty" }
 jetty-io = { module = "org.eclipse.jetty:jetty-io", version.ref = "jetty" }
 jetty-jmx = { module = "org.eclipse.jetty:jetty-jmx", version.ref = "jetty" }
 jetty-security = { module = "org.eclipse.jetty:jetty-security", version.ref = "jetty" }
 jetty-server = { module = "org.eclipse.jetty:jetty-server", version.ref = "jetty" }
-jetty-servlet = { module = "org.eclipse.jetty:jetty-servlet", version.ref = "jetty" }
+jetty-session = { module = "org.eclipse.jetty:jetty-session", version.ref = "jetty" }
 jetty-util = { module = "org.eclipse.jetty:jetty-util", version.ref = "jetty" }
 jetty-util-ajax = { module = "org.eclipse.jetty:jetty-util-ajax", version.ref = "jetty" }
 json-smart = { module = "net.minidev:json-smart", version = "1.1.1" }
diff --git a/tools/java_deps.MODULE.bazel b/tools/java_deps.MODULE.bazel
index d456542..a899132 100644
--- a/tools/java_deps.MODULE.bazel
+++ b/tools/java_deps.MODULE.bazel
@@ -126,12 +126,6 @@
     "com.googlecode.javaewah:JavaEWAH",
     "commons-codec:commons-codec",
     "org.apache.commons:commons-lang3",
-    "org.eclipse.jetty:jetty-http",
-    "org.eclipse.jetty:jetty-io",
-    "org.eclipse.jetty:jetty-security",
-    "org.eclipse.jetty:jetty-server",
-    "org.eclipse.jetty:jetty-util",
-    "org.eclipse.jetty:jetty-util-ajax",
 ]
 
 [