Merge "EventFactory: Use DiffSummaryCache for patch set size"
diff --git a/.agents/rules.json b/.agents/rules.json
new file mode 100644
index 0000000..bcd4b27
--- /dev/null
+++ b/.agents/rules.json
@@ -0,0 +1,7 @@
+{
+  "entries": [
+    {
+      "path": ".agents/rules/upload_rationale.md"
+    }
+  ]
+}
diff --git a/.agents/rules/upload_rationale.md b/.agents/rules/upload_rationale.md
new file mode 100644
index 0000000..f7a13a4
--- /dev/null
+++ b/.agents/rules/upload_rationale.md
@@ -0,0 +1,18 @@
+---
+trigger: always_on
+description: Mandate uploading agent rationale for Gerrit changes
+---
+
+## Gerrit Rationale Upload Policy
+
+Whenever you create a new Gerrit change or upload a patchset (via `git push`,
+`hg upload`, `g4 upload`, or any other VCS command), you **MUST** immediately
+upload AI rationales for that patchset.
+
+Follow this two-step workflow:
+
+1.  **Push Code**: Execute your VCS push/upload command so the new patchset is
+    created on Gerrit.
+2.  **Upload Rationale**: Read the `gerrit` skill (specifically the
+    `upload-rationale` section) and follow its instructions to construct and
+    upload the complete set of rationales for the patchset.
diff --git a/.agents/skills.json b/.agents/skills.json
new file mode 100644
index 0000000..88387c1
--- /dev/null
+++ b/.agents/skills.json
@@ -0,0 +1,7 @@
+{
+  "entries": [
+    {
+      "path": "/google/src/files/head/depot/google3/learning/gemini/agents/skills/gerrit"
+    }
+  ]
+}
diff --git a/.bazelrc b/.bazelrc
index 454bfbf..b864a3b 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,9 +1,4 @@
-# TODO(davido): Migrate all dependencies from WORKSPACE to MODULE.bazel
-# https://issues.gerritcodereview.com/issues/303819949
-# Remove hybrid mode option once bzlmod migration is completed
-common --enable_workspace
-common --enable_bzlmod --lockfile_mode=error
-common --incompatible_enable_proto_toolchain_resolution
+common --lockfile_mode=error
 # Enable Gerrit-tree-only plugin checks (standalone plugin builds skip them).
 common --@com_googlesource_gerrit_bazlets//flags:in_gerrit_tree=true
 common --incompatible_disallow_struct_provider_syntax=false
diff --git a/.bazelversion b/.bazelversion
index acd405b..47da986 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-8.6.0
+9.1.0
diff --git a/.gitignore b/.gitignore
index 6f9b13d..cc71eb5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@
 /.aspect/
 /.apt_generated
 /.apt_generated_tests
+/.eslintcache
 /.bazel_path
 /.classpath
 /.factorypath
@@ -57,7 +58,6 @@
 !/plugins/commit-message-length-validator
 !/plugins/delete-project
 !/plugins/download-commands
-!/plugins/external_plugin_deps.bzl
 !/plugins/gitiles
 !/plugins/hooks
 !/plugins/plugin-manager
diff --git a/AGENTS.md b/AGENTS.md
index a8199e8..be46d98 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -110,6 +110,7 @@
 - Java: Google Java Style Guide, use `./tools/gjf.sh run` before committing
 - Commit messages: max 72 chars/line, present tense, include Change-Id (added by git hook)
 - **Release-Notes footer required**: Every commit must have `Release-Notes:` footer. Use `Release-Notes: skip` for small fixes/refactorings, or add a summary for notable changes
+- Optional Google-Bug-Id footer: Commits might have a `Google-Bug-Id: b/<number>` footer. If your context provides a bug number, use it.
 - **Copyright headers**: All new Java files must include the Apache 2.0 license header with the current year (e.g., `// Copyright (C) 2026 The Android Open Source Project`)
 
 ## Key Patterns
diff --git a/Documentation/BUILD b/Documentation/BUILD
index 9ab713a..9a8451b 100644
--- a/Documentation/BUILD
+++ b/Documentation/BUILD
@@ -1,3 +1,4 @@
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
 load("//tools/bzl:asciidoc.bzl", "documentation_attributes", "genasciidoc", "genasciidoc_zip")
 load("//tools/bzl:license.bzl", "license_map")
 load("//tools/bzl:war_checks.bzl", "war_jars_allowlist_test")
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/cmd-show-caches.txt b/Documentation/cmd-show-caches.txt
index 65f05b1..ceca34b 100644
--- a/Documentation/cmd-show-caches.txt
+++ b/Documentation/cmd-show-caches.txt
@@ -8,20 +8,39 @@
 --
 _ssh_ -p <port> <host> _gerrit show-caches_
   [--show-jvm]
+  [--include-diskstats]
+  [--cache <NAME> ...]
 --
 
 == DESCRIPTION
 Display statistics about the size and hit ratio of in-memory caches.
 
+By default, the command displays statistics for all registered caches.
+Because collecting data for numerous large caches on a busy server
+can cause delays, the --cache option can be used (one or more times)
+to limit the output to specific caches.
+
 == OPTIONS
 --show-jvm::
 	List the name and version of the Java virtual machine, host
 	operating system, and other details about the environment
 	that Gerrit Code Review is running in.
 
+--include-diskstats::
+	Include disk stat collection for persistent caches.
+
 --show-threads::
 	Show detailed counts for Gerrit specific threads.
 
+--cache <NAME>::
+	Show statistics only for the cache called <NAME>. May be
+	supplied more than once to display multiple caches in a single
+	command execution. Cache names are matched case-insensitively
+	against the full registered cache name (including any plugin
+	prefix). Unknown cache names are silently ignored. The
+	summary sections (SSH, tasks, memory, threads, JVM) are not
+	affected by this option.
+
 --width::
 -w::
 	Width of the output table.
@@ -84,6 +103,12 @@
 Threads: 4 CPUs available, 371 threads
 ----
 
+Show statistics for only the "accounts" and "projects" caches:
+
+----
+$ ssh -p 29418 review.example.com gerrit show-caches --cache accounts --cache projects
+----
+
 == SEE ALSO
 
 * link:cmd-flush-caches.html[gerrit flush-caches]
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 42b4c10d..d8c1f6b 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -430,6 +430,26 @@
 HTTP header to trust the username from, or unset to select HTTP basic
 authentication.  Only used if `auth.type` is set to `HTTP`.
 
+[[auth.httpTrustedProxyNetworks]]auth.httpTrustedProxyNetworks::
++
+List of IPv4 networks CIDRs (e.g., `192.168.0.0/16`) that are trusted proxies for receiving
+HTTP header authentication.  Specify multiple networks by adding multiple entries.
+It applies only when `auth.type` is set to `HTTP` or `HTTP_LDAP`. The trusted-proxy
+remote address must be IPv4; if Gerrit is reached over IPv6, header-based
+authentication is rejected.
++
+[NOTE]
+====
+Accepting a trusted authentication HTTP header involves a leap of faith in the security
+of the incoming HTTP traffic. By default, the only secure way to ensure that all the
+traffic is coming from a trusted authentication proxy is via firewall rules or using
+a local proxy on the loopback interface. When using a remote proxy, the security of
+the connection between the proxy and Gerrit is in doubt, and this can be mitigated by
+the use of `auth.httpTrustedProxyNetworks`.
+====
++
+Default: accept any incoming IPs.
+
 [[auth.httpDisplaynameHeader]]auth.httpDisplaynameHeader::
 +
 HTTP header to retrieve the user's display name from.  Only used if `auth.type`
@@ -912,6 +932,32 @@
 Valid values are 0, and positive integers. Setting this to 0 will
 cause the filter to never be rebuilt.
 +
+[[cache.startupThreads]]cache.startupThreads::
++
+Number of threads used to open persistent caches in parallel during startup.
+Each persistent cache is backed by its own H2 database file and builds a
+BloomFilter by reading all keys from disk when it is opened. With the default
+of 1, caches are opened sequentially. Increasing this value allows the
+BloomFilter build phase to run concurrently across caches.
++
+Setting this value greater than 1 creates threads in a dedicated pool, separate
+from the H2 thread pool (`DiskCache-Store`) that handles cache updates.
++
+Default is 1.
+
+[[cache.preWarmForBloomFilter]]cache.preWarmForBloomFilter::
++
+When enabled, each persistent cache reads its H2 database file into the OS
+page cache before building its BloomFilter. The BloomFilter build requires
+a full table scan of all keys which involves reading close to the entire file
+in a scattered fashion. Doing such a read tends to not perform well I/O wise as
+it generally cannot take advantage of operating system level file system
+readahead. Reading the file sequentially up front tends to take advantage of
+readahead to fully populate the file system caches which then can make scattered
+reads much faster.
++
+Default is true.
+
 [[cache.openFiles]]cache.openFiles::
 +
 The number of file descriptors to add to the limit set by the Gerrit daemon.
@@ -5080,6 +5126,7 @@
 [[plugins.loadPriority]]plugins.loadPriority::
 +
 List of `pluginName`s required to have a specific loading order during Gerrit startup.
+Plugins will be unloaded in reverse order.
 +
 Each entry should contain a plugin name defined in the `MANIFEST.MF` under
 `Gerrit-PluginName` or a plugin JAR file name. During the Gerrit startup
diff --git a/Documentation/config-mail.txt b/Documentation/config-mail.txt
index 49ec3f4..c440eb3 100644
--- a/Documentation/config-mail.txt
+++ b/Documentation/config-mail.txt
@@ -226,7 +226,7 @@
 
 $change.shortOriginalSubject::
 +
-The original subject limited to 72 characters, with an ellipsis if it exceeds
+The original subject limited to 72 characters, with an ellipsis if it exceeds.
 that.
 
 $change.sizeBucket::
@@ -251,7 +251,7 @@
 
 $instanceAndProjectName::
 +
-The Gerrit instance name, followed by the short project name
+The Gerrit instance name, followed by the short project name.
 
 $addInstanceNameInSubject::
 +
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index f75f101..f48b2b66 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -519,34 +519,6 @@
   bazelisk test //plugins/replication/...
 ----
 
-[[known-issues]]
-=== Known Issues
-
-[[byte-buddy-not-initialized-or-unavailable]]
-==== The Byte Buddy agent is not initialized or unavailable
-
-If running tests that make use of mocks fail with the exception below, set the
-`sandbox_tmpfs_path` flag for running tests in `.bazelrc` as described in this
-link:https://github.com/mockito/mockito/issues/1879#issuecomment-922459131[
-issue], e.g. add this line: `test --sandbox_tmpfs_path=/tmp`
-
-.Exception:
-----
-...
-Caused by: org.mockito.exceptions.base.MockitoInitializationException:
-Could not initialize inline Byte Buddy mock maker.
-
-It appears as if your JDK does not supply a working agent attachment mechanism.
-...
-Caused by: java.lang.IllegalStateException: The Byte Buddy agent is not initialized or unavailable
-at net.bytebuddy.agent.ByteBuddyAgent.getInstrumentation(ByteBuddyAgent.java:230)
-at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:617)
-at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:568)
-at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:545)
-at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.<clinit>(InlineDelegateByteBuddyMockMaker.java:115)
-... 47 more
-----
-
 [[debugging-tests]]
 == Debugging Unit Tests
 In some cases it may be necessary to debug a test while running it in bazel. For example, when we
@@ -809,15 +781,15 @@
     --disk-size=200
 ```
 
-Note, that we are using Ubuntu2204 docker image from bazel project:
+Note, that we are using Ubuntu2404 docker image from bazel project:
 
 
 ```
-docker pull gcr.io/bazel-public/ubuntu2204-java17@sha256:ffe37746a34537d8e73cef5a20ccd3a4e3ec7af3e7410cba87387ba97c0e520f
+docker pull gcr.io/bazel-public/ubuntu2404@sha256:a5e969f2208ae4ab3cbe18cd85971f1f602345a650495674736a66b3ef9a55d1
 ```
 
 Re-build rbe_autoconfig project, conduct a new release and switch to using it
-in `WORKSPACE` file. For more details see this
+in `MODULE.bazel` file. For more details see this
 link:https://github.com/davido/rbe_autoconfig[repository,role=external,window=_blank]
 
 Note, to authenticate to the gcr.io registry, the following command must be
diff --git a/Documentation/dev-build-plugins.txt b/Documentation/dev-build-plugins.txt
index c0cf52e..cc43706 100644
--- a/Documentation/dev-build-plugins.txt
+++ b/Documentation/dev-build-plugins.txt
@@ -83,13 +83,6 @@
 
 === Plugins with external dependencies ===
 
-[NOTE]
-As of Gerrit 3.14 using the `external_plugin_deps.bzl` file for adding external
-dependencies of plugins to the build has been deprecated. This feature will be
-removed with Gerrit 3.15. Please migrate to using Bazel modules as described
-below. The documentation of the deprecated `external_plugin_deps.bzl`
-functionality has been moved to a dedicated section below.
-
 If a plugin requires external Java dependencies, it can install them in its
 `MODULE.bazel` using `rules_jvm_external`. The Maven repository containing the
 plugin runtime dependencies must be plugin-scoped (for example
@@ -269,51 +262,6 @@
 bazel build reviewers
 ----
 
-== Managing external dependencies using `external_plugin_deps.bzl` ==
-
-[NOTE]
-This functionality has been deprecated.
-
-If the plugin has external dependencies, then they can be included from Gerrit's
-own WORKSPACE file. This can be achieved by including them in `external_plugin_deps.bzl`.
-During the build in Gerrit tree, this file must be copied over the dummy one in
-`plugins` directory.
-
-Example for content of `external_plugin_deps.bzl` file:
-
-----
-load("//tools/bzl:maven_jar.bzl", "maven_jar")
-
-def external_plugin_deps():
-  maven_jar(
-      name = 'org_apache_tika_tika_core',
-      artifact = 'org.apache.tika:tika-core:1.12',
-      sha1 = '5ab95580d22fe1dee79cffbcd98bb509a32da09b',
-  )
-----
-
-If the plugin(s) being bundled in the release have external dependencies, include them
-in `plugins/external_plugin_deps`. Create symbolic link from plugin's own
-`external_plugin_deps()` file in plugins directory and prefix the file with
-plugin name, e.g.:
-
-----
-  $ cd plugins
-  $ ln -s oauth/external_plugin_deps.bzl oauth_external_plugin_deps.bzl
-  $ ln -s uploadvalidator/external_plugin_deps.bzl uploadvalidator_external_plugin_deps.bzl
-----
-
-Now the plugin specific dependency files can be imported:
-
-----
-load(":oauth_external_plugin_deps.bzl", oauth_deps="external_plugin_deps")
-load(":uploadvalidator_external_plugin_deps.bzl", uploadvalidator_deps="external_plugin_deps")
-
-def external_plugin_deps():
-  oauth_deps()
-  uploadvalidator_deps()
-----
-
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-crafting-changes.txt b/Documentation/dev-crafting-changes.txt
index a30e10f..a00cdca 100644
--- a/Documentation/dev-crafting-changes.txt
+++ b/Documentation/dev-crafting-changes.txt
@@ -147,9 +147,9 @@
 
 To format Java source code, Gerrit uses the
 link:https://github.com/google/google-java-format[`google-java-format`,role=external,window=_blank]
-tool (version 1.35.0), and to format Bazel BUILD, WORKSPACE and .bzl files the
+tool (version 1.35.0), and to format Bazel BUILD, MODULE.bazel and .bzl files the
 link:https://github.com/bazelbuild/buildtools/tree/master/buildifier[`buildifier`,role=external,window=_blank]
-tool (version 4.0.0). Unused dependencies are found and removed using the
+tool (version 8.0.0). Unused dependencies are found and removed using the
 link:https://github.com/bazelbuild/buildtools/tree/master/unused_deps[`unused_deps`,role=external,window=_blank]
 build tool, a sibling of `buildifier`.
 
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index d8bfb77..bf26fc2 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -43,6 +43,35 @@
 Filters on a folder, they will be overwritten the next time you run
 `tools/eclipse/project.py`.
 
+=== JGit servlet-facing sources
+
+Gerrit consumes JGit from source through `modules/jgit`, but JGit master uses
+`jakarta.servlet` in servlet-facing modules while Gerrit still runs on Jetty 12
+EE8 and `javax.servlet`.
+
+For those modules, Gerrit uses JGit-provided Bazel EE8 bridge targets:
+
+----
+  @jgit//org.eclipse.jgit.http.server.ee8:jgit-servlet-ee8
+  @jgit//org.eclipse.jgit.lfs.server.ee8:jgit-lfs-server-ee8
+----
+
+The generated artifacts keep the original JGit Java packages and source line
+numbers. They only rewrite servlet imports from `jakarta.servlet` to
+`javax.servlet`, so Gerrit Java code keeps its existing imports.
+
+The Eclipse project generator does not import
+`modules/jgit/org.eclipse.jgit.http.server/src` as a source folder. Instead, it
+attaches the generated EE8 source jar when the generated servlet jar is on the
+classpath. Put breakpoints in the generated source attachment when debugging
+runtime HTTP servlet classes.
+
+The same source attachment rule applies to
+`org.eclipse.jgit.lfs.server.ee8:jgit-lfs-server-ee8` for plugin consumers such
+as the Gerrit LFS plugin. Core Gerrit does not depend on that jar today, but the
+Eclipse generator is prepared to attach its generated source jar when a plugin
+classpath includes it.
+
 === Eclipse project on MacOS
 
 By default, bazel uses `/private/var/tmp` as the
diff --git a/Documentation/dev-intellij.txt b/Documentation/dev-intellij.txt
index be4196c..a4b7776 100644
--- a/Documentation/dev-intellij.txt
+++ b/Documentation/dev-intellij.txt
@@ -86,6 +86,45 @@
 advantage of this is that project files don't need to be excluded from version
 control.
 
+=== JGit servlet-facing sources
+
+Gerrit consumes JGit from source through `modules/jgit`, but JGit master uses
+`jakarta.servlet` in servlet-facing modules while Gerrit still runs on Jetty 12
+EE8 and `javax.servlet`.
+
+For those modules, Gerrit uses JGit-provided Bazel EE8 bridge targets:
+
+----
+  @jgit//org.eclipse.jgit.http.server.ee8:jgit-servlet-ee8
+  @jgit//org.eclipse.jgit.lfs.server.ee8:jgit-lfs-server-ee8
+----
+
+The generated artifacts keep the original JGit Java packages and source line
+numbers. They only rewrite servlet imports from `jakarta.servlet` to
+`javax.servlet`, so Gerrit Java code keeps its existing imports.
+
+Use the current JetBrains Bazel plugin (the new, actively developed one). The
+legacy "Bazel for IntelliJ" plugin does not support Bazel 9 / Bzlmod, which
+Gerrit requires. The Bazel plugin imports `libjgit-servlet-ee8.jar` together
+with its generated source jar, so the servlet classes resolve to the EE8
+`javax.servlet` sources. Put breakpoints there when debugging runtime HTTP
+servlet classes; because line numbers are preserved, breakpoints bind and step
+correctly. The same applies to `jgit-lfs-server-ee8` for plugin consumers such
+as the Gerrit LFS plugin.
+
+The canonical, editable sources stay in SCM under
+`modules/jgit/org.eclipse.jgit.http.server` (and `org.eclipse.jgit.lfs.server`).
+To change the servlet code, edit the canonical `jakarta.servlet` sources,
+rebuild, and the change flows into the generated EE8 artifacts.
+
+NOTE: The IDE may flag these classes with "Library source does not match the
+bytecode" and report unresolved `javax.servlet` symbols. This is a known
+limitation of the Bazel IntelliJ integration with generated and duplicate
+sources: the project indexes both the canonical `jakarta.servlet` source and
+the EE8 `javax.servlet` jar, which share class names. It is a static-analysis
+artifact only and does not affect debugging, which uses the class actually
+loaded at runtime.
+
 == Recommended settings
 
 === Code style
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/metrics.txt b/Documentation/metrics.txt
index 7da1c18..5c13aef 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -208,12 +208,20 @@
 * `caches/memory_eviction_count`: Memory eviction count.
 ** `cache_name`:
    The name of the cache.
+* `caches/memory_request_count`: Total number of lookups (hits + misses) against
+  the in-memory cache.
+** `cache_name`:
+   The name of the cache.
 * `caches/disk_cached`: Disk entries used by persistent cache.
 ** `cache_name`:
    The name of the cache.
 * `caches/disk_hit_ratio`: Disk hit ratio for persistent cache.
 ** `cache_name`:
    The name of the cache.
+* `caches/disk_request_count`: Total number of lookups (hits + misses) against
+  the persistent disk cache.
+** `cache_name`:
+   The name of the cache.
 * `caches/refresh_count`: The number of refreshes per cache with an indicator if
   a reload was necessary.
 ** `cache`:
diff --git a/Documentation/pg-plugin-endpoints.txt b/Documentation/pg-plugin-endpoints.txt
index 80d20c3..ed63caa 100644
--- a/Documentation/pg-plugin-endpoints.txt
+++ b/Documentation/pg-plugin-endpoints.txt
@@ -68,6 +68,20 @@
 current revision displayed, an instance of
 link:rest-api-changes.html#revision-info[RevisionInfo]
 
+=== change-view-commit-aside
+The `change-view-commit-aside` extension point is located on the change view
+page, next to the commit message above the related changes list.
+
+* `change`
++
+current change displayed, an instance of
+link:rest-api-changes.html#change-info[ChangeInfo]
+
+* `revision`
++
+current revision displayed, an instance of
+link:rest-api-changes.html#revision-info[RevisionInfo]
+
 === change-metadata-item
 The `change-metadata-item` extension point is located on the change view
 left panel, below the `Submit Requirements` and `Links` sections by default.
diff --git a/Documentation/release_war_jars.txt b/Documentation/release_war_jars.txt
index 76927bf..099aae1 100644
--- a/Documentation/release_war_jars.txt
+++ b/Documentation/release_war_jars.txt
@@ -35,7 +35,7 @@
 gson
 guava-33.5.0-jre
 guava-retrying
-guice
+guice-6.0.0-classes
 guice-assistedinject
 guice-servlet
 h2
@@ -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-config.txt b/Documentation/rest-api-config.txt
index 8c3c972..f7e477d 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -330,11 +330,25 @@
 
 The entries in the map are sorted by cache name.
 
+.Query Options
+[options="header",cols="1,3"]
+|===
+|Field Name          |Description
+|`include-diskstats` |If `true`, disk stat collection is included for persistent caches.
+|===
+
 .Request
 ----
   GET /config/server/caches/ HTTP/1.0
 ----
 
+To include disk stats for persistent caches:
+
+.Request
+----
+  GET /config/server/caches/?include-diskstats=true HTTP/1.0
+----
+
 .Response
 ----
   HTTP/1.1 200 OK
@@ -1548,8 +1562,7 @@
   {
     "changes": [
       "foo~101",
-      "bar~202",
-      "303"
+      "bar~202"
     ],
     "delete_missing": "true"
   }
@@ -1561,8 +1574,13 @@
   Content-Disposition: attachment
 ----
 
-When `delete_missing` is set to `true` changes to be reindexed which are missing in NoteDb
-will be deleted in the index.
+[NOTE]
+When `delete_missing` is set to `true`, only Change-IDs in the format
+project~changeNumber are accepted. Changes to be reindexed which are
+missing in NoteDb will be deleted in the index. If any of the Change-IDs
+are in an incorrect format, the server will return a 400 - Bad Request,
+and none of the operations will be executed.
+
 
 [[list-indexes]]
 === List Indexes
@@ -2573,11 +2591,11 @@
 |================================
 |Field Name         ||Description
 |`changes`   ||
-List of link:rest-api-changes.html#change-id[change-ids]
+List of link:rest-api-changes.html#change-id[change-ids]. When `delete_missing` is `true`, each entry must be in `project~changeNumber` format.
 |`delete_missing`  |optional|
 Delete changes which are missing in NoteDb from the index. This can be used
 to get rid of stale index entries. Possible values are `true` and `false`.
-By default set to `false`.
+By default set to `false`. When `true`, all entries in `changes` must be in `project~changeNumber` format; otherwise the request fails with `400 Bad Request` and no operations are executed.
 |================================
 
 [[jvm-summary-info]]
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/MODULE.bazel b/MODULE.bazel
index 0e77e78..037a989 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -1,26 +1,33 @@
-# TODO(davido): Migrate all dependencies from WORKSPACE to MODULE.bazel
-# https://issues.gerritcodereview.com/issues/303819949
 module(name = "gerrit")
 
 # Core Bazel deps.
-bazel_dep(name = "bazel_features", version = "1.41.0")
+bazel_dep(name = "bazel_features", version = "1.45.0")
 bazel_dep(name = "platforms", version = "1.0.0")
-bazel_dep(name = "rules_jvm_external", version = "6.10")
+bazel_dep(name = "rules_jvm_external", version = "7.0")
 
 # Language rules.
 bazel_dep(name = "aspect_bazel_lib", version = "2.22.5")
-bazel_dep(name = "aspect_rules_js", version = "3.0.3")
+bazel_dep(name = "aspect_rules_js", version = "3.1.2")
 bazel_dep(name = "aspect_rules_rollup", version = "2.0.1")
 bazel_dep(name = "aspect_rules_ts", version = "3.8.8")
-bazel_dep(name = "rules_go", version = "0.51.0-rc2")
+bazel_dep(name = "rules_android", version = "0.7.2")
+bazel_dep(name = "rules_go", version = "0.60.0")
+bazel_dep(name = "rules_java", version = "9.3.0")
 bazel_dep(name = "rules_nodejs", version = "6.7.4")
-bazel_dep(name = "rules_java", version = "8.16.1")
-bazel_dep(name = "rules_proto", version = "7.1.0")
-bazel_dep(name = "rules_python", version = "1.8.0-rc1")
-bazel_dep(name = "rules_shell", version = "0.6.1")
+bazel_dep(name = "rules_python", version = "2.0.2")
+bazel_dep(name = "rules_shell", version = "0.8.0")
+
+# Pin rules_java to Gerrit's chosen version. Transitive bzlmod modules can
+# otherwise raise it through MVS, which changes the Java toolchain inputs
+# and can break custom plugin builds. When merging up, keep this override
+# but update the version to the target branch's chosen rules_java version.
+single_version_override(
+    module_name = "rules_java",
+    version = "9.3.0",
+)
 
 # Libraries / toolchains.
-bazel_dep(name = "protobuf", version = "34.1")
+bazel_dep(name = "protobuf", version = "35.0")
 
 # In-tree modules.
 bazel_dep(name = "jgit")
@@ -33,7 +40,7 @@
 bazel_dep(name = "rbe_autoconfig")
 git_override(
     module_name = "rbe_autoconfig",
-    commit = "c4d733d0399ebf2ee1ffb897be5fbaba23738e04",
+    commit = "eb944ce4fc29a1608eae9adf0b0e0df2f9e05e33",
     remote = "https://github.com/davido/rbe_autoconfig.git",
 )
 
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index 2eead3e..fe7c2be 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -1,5 +1,5 @@
 {
-  "lockFileVersion": 24,
+  "lockFileVersion": 26,
   "registryFileHashes": {
     "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
     "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
@@ -7,21 +7,23 @@
     "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0",
     "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb",
     "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16",
-    "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915",
     "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed",
     "https://bcr.bazel.build/modules/abseil-cpp/20240116.2/MODULE.bazel": "73939767a4686cd9a520d16af5ab440071ed75cec1a876bf2fcfaf1f71987a16",
     "https://bcr.bazel.build/modules/abseil-cpp/20250127.0/MODULE.bazel": "d1086e248cda6576862b4b3fe9ad76a214e08c189af5b42557a6e1888812c5d5",
     "https://bcr.bazel.build/modules/abseil-cpp/20250127.1/MODULE.bazel": "c4a89e7ceb9bf1e25cf84a9f830ff6b817b72874088bf5141b314726e46a57c1",
     "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/MODULE.bazel": "d209fdb6f36ffaf61c509fcc81b19e81b411a999a934a032e10cd009a0226215",
-    "https://bcr.bazel.build/modules/abseil-cpp/20250814.0/MODULE.bazel": "c43c16ca2c432566cdb78913964497259903ebe8fb7d9b57b38e9f1425b427b8",
-    "https://bcr.bazel.build/modules/abseil-cpp/20250814.0/source.json": "b88bff599ceaf0f56c264c749b1606f8485cec3b8c38ba30f88a4df9af142861",
+    "https://bcr.bazel.build/modules/abseil-cpp/20250814.1/MODULE.bazel": "51f2312901470cdab0dbdf3b88c40cd21c62a7ed58a3de45b365ddc5b11bcab2",
+    "https://bcr.bazel.build/modules/abseil-cpp/20250814.1/source.json": "cea3901d7e299da7320700abbaafe57a65d039f10d0d7ea601c4a66938ea4b0c",
     "https://bcr.bazel.build/modules/abseil-py/2.1.0/MODULE.bazel": "5ebe5bf853769c65707e5c28f216798f7a4b1042015e6a36e6d03094d94bec8a",
     "https://bcr.bazel.build/modules/abseil-py/2.1.0/source.json": "0e8fc4f088ce07099c1cd6594c20c7ddbb48b4b3c0849b7d94ba94be88ff042b",
     "https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896",
     "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85",
-    "https://bcr.bazel.build/modules/apple_support/1.16.0/MODULE.bazel": "e785295d21ccab339c3af131752bfbe50fc33dd8215b357492d05bfad0232400",
-    "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442",
-    "https://bcr.bazel.build/modules/apple_support/1.23.1/source.json": "d888b44312eb0ad2c21a91d026753f330caa48a25c9b2102fae75eb2b0dcfdd2",
+    "https://bcr.bazel.build/modules/apple_support/1.21.0/MODULE.bazel": "ac1824ed5edf17dee2fdd4927ada30c9f8c3b520be1b5fd02a5da15bc10bff3e",
+    "https://bcr.bazel.build/modules/apple_support/1.21.1/MODULE.bazel": "5809fa3efab15d1f3c3c635af6974044bac8a4919c62238cce06acee8a8c11f1",
+    "https://bcr.bazel.build/modules/apple_support/1.24.1/MODULE.bazel": "f46e8ddad60aef170ee92b2f3d00ef66c147ceafea68b6877cb45bd91737f5f8",
+    "https://bcr.bazel.build/modules/apple_support/1.24.2/MODULE.bazel": "0e62471818affb9f0b26f128831d5c40b074d32e6dda5a0d3852847215a41ca4",
+    "https://bcr.bazel.build/modules/apple_support/2.3.0/MODULE.bazel": "d48f824ae8eeea5f837eb3038cef3615075d996d3e82eb9187192c2820605a81",
+    "https://bcr.bazel.build/modules/apple_support/2.3.0/source.json": "d99b0a50918c4484856d773026b2fd60a694668c70fc0e19d592786dc7e5e469",
     "https://bcr.bazel.build/modules/aspect_bazel_lib/2.11.0/MODULE.bazel": "cb1ba9f9999ed0bc08600c221f532c1ddd8d217686b32ba7d45b0713b5131452",
     "https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f",
     "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.2/MODULE.bazel": "30dfabbfae0139b1f0036e01c201dd4c0167da3017f0b7ef3820d78e07622989",
@@ -31,8 +33,8 @@
     "https://bcr.bazel.build/modules/aspect_bazel_lib/2.7.7/MODULE.bazel": "491f8681205e31bb57892d67442ce448cda4f472a8e6b3dc062865e29a64f89c",
     "https://bcr.bazel.build/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "812d2dd42f65dca362152101fbec418029cc8fd34cbad1a2fde905383d705838",
     "https://bcr.bazel.build/modules/aspect_rules_js/2.0.0/MODULE.bazel": "b45b507574aa60a92796e3e13c195cd5744b3b8aff516a9c0cb5ae6a048161c5",
-    "https://bcr.bazel.build/modules/aspect_rules_js/3.0.3/MODULE.bazel": "28a30e8fc33bf64a67835d64d124f6e05a7d59648dcb27b110fb3502f761e503",
-    "https://bcr.bazel.build/modules/aspect_rules_js/3.0.3/source.json": "bb8fff9a304452e1042af9522ad1d54d6f1d1fdf71c5127deadb6fd156654193",
+    "https://bcr.bazel.build/modules/aspect_rules_js/3.1.2/MODULE.bazel": "e3685502155d3cc65f3bf98e714f7435de67d7f8f355d63478a80197310311fc",
+    "https://bcr.bazel.build/modules/aspect_rules_js/3.1.2/source.json": "a32ab71831452b945f3f83a1b1feb9402007e600bce55ac76e15ef0c1e08b520",
     "https://bcr.bazel.build/modules/aspect_rules_rollup/2.0.1/MODULE.bazel": "296e3a053658c2af989ba9bd62a205e6d1fa84bdd6dd5249196546e6b84770ec",
     "https://bcr.bazel.build/modules/aspect_rules_rollup/2.0.1/source.json": "2fe8ac1ccb4de74bf884761e070010280b272d94e3997205b361b91c75409726",
     "https://bcr.bazel.build/modules/aspect_rules_ts/3.8.8/MODULE.bazel": "b52b929a948438665809d49af610f58d1b14f63d6d21ab748f47b6050be4c1f6",
@@ -55,16 +57,21 @@
     "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d",
     "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9",
     "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
+    "https://bcr.bazel.build/modules/bazel_features/1.32.0/MODULE.bazel": "095d67022a58cb20f7e20e1aefecfa65257a222c18a938e2914fd257b5f1ccdc",
     "https://bcr.bazel.build/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6",
     "https://bcr.bazel.build/modules/bazel_features/1.34.0/MODULE.bazel": "e8475ad7c8965542e0c7aac8af68eb48c4af904be3d614b6aa6274c092c2ea1e",
+    "https://bcr.bazel.build/modules/bazel_features/1.36.0/MODULE.bazel": "596cb62090b039caf1cad1d52a8bc35cf188ca9a4e279a828005e7ee49a1bec3",
     "https://bcr.bazel.build/modules/bazel_features/1.39.0/MODULE.bazel": "28739425c1fc283c91931619749c832b555e60bcd1010b40d8441ce0a5cf726d",
     "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
     "https://bcr.bazel.build/modules/bazel_features/1.41.0/MODULE.bazel": "6e0f87fafed801273c371d41e22a15a6f8abf83fdd7f87d5e44ad317b94433d0",
-    "https://bcr.bazel.build/modules/bazel_features/1.41.0/source.json": "8fd525b31b0883c47e0593443cdd10219b94a7556b3195fc02d75c86c66cfe30",
+    "https://bcr.bazel.build/modules/bazel_features/1.42.1/MODULE.bazel": "275a59b5406ff18c01739860aa70ad7ccb3cfb474579411decca11c93b951080",
+    "https://bcr.bazel.build/modules/bazel_features/1.45.0/MODULE.bazel": "7daec6d87ab0703417486d4cb948af0b06f55d4d7c08cbb5978c80e79b538edf",
+    "https://bcr.bazel.build/modules/bazel_features/1.45.0/source.json": "635e4536e09ff125b8972e0fa239c135fde5f18701f7d5115680560651dfb41d",
     "https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b",
     "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
     "https://bcr.bazel.build/modules/bazel_lib/3.0.0-rc.0/MODULE.bazel": "d6e00979a98ac14ada5e31c8794708b41434d461e7e7ca39b59b765e6d233b18",
     "https://bcr.bazel.build/modules/bazel_lib/3.0.0/MODULE.bazel": "22b70b80ac89ad3f3772526cd9feee2fa412c2b01933fea7ed13238a448d370d",
+    "https://bcr.bazel.build/modules/bazel_lib/3.1.0/MODULE.bazel": "6809765c14e3c766a9b9286c7b0ec56ed87a73326e48fe01749f0c0fdcfe3287",
     "https://bcr.bazel.build/modules/bazel_lib/3.2.2/MODULE.bazel": "e2c890c8a515d6bca9c66d47718aa9e44b458fde64ec7204b8030bf2d349058c",
     "https://bcr.bazel.build/modules/bazel_lib/3.2.2/source.json": "9e84e115c20e14652c5c21401ae85ff4daa8702e265b5c0b3bf89353f17aa212",
     "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
@@ -83,22 +90,26 @@
     "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/MODULE.bazel": "72997b29dfd95c3fa0d0c48322d05590418edef451f8db8db5509c57875fb4b7",
     "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/source.json": "7ad77c1e8c1b84222d9b3f3cae016a76639435744c19330b0b37c0a3c9da7dc0",
     "https://bcr.bazel.build/modules/bazel_worker_api/0.0.1/MODULE.bazel": "02a13b77321773b2042e70ee5e4c5e099c8ddee4cf2da9cd420442c36938d4bd",
+    "https://bcr.bazel.build/modules/bazel_worker_api/0.0.10/MODULE.bazel": "a426df551b40c3997c351d05f00f0d1f86b618ddb646012f1e9c72efce8ed939",
+    "https://bcr.bazel.build/modules/bazel_worker_api/0.0.10/source.json": "7f220d3edfeba5d1f61535fd8400338df74f8bdeb241ebe660534c6926a5a645",
     "https://bcr.bazel.build/modules/bazel_worker_api/0.0.4/MODULE.bazel": "460aa12d01231a80cce03c548287b433b321d205b0028ae596728c35e5ee442e",
     "https://bcr.bazel.build/modules/bazel_worker_api/0.0.8/MODULE.bazel": "396c1ef53835aafe3d42ce6619080531ee770648303731f16cfaa33fa056bf0c",
-    "https://bcr.bazel.build/modules/bazel_worker_api/0.0.8/source.json": "abaf8ac9d2ab2f47bda9af4c0c080ff7907378888e1f4bc62a0539dd13ba61e8",
+    "https://bcr.bazel.build/modules/bazel_worker_java/0.0.10/MODULE.bazel": "538f21f715cab81c4a43f73e1a91a0c1f8accd9b263dd2a831952805fcfc1a62",
+    "https://bcr.bazel.build/modules/bazel_worker_java/0.0.10/source.json": "1646d3aaf5a4bc0d587ecb33d81e93ea9284ff96f5ef579006a9c054945bbe7a",
     "https://bcr.bazel.build/modules/bazel_worker_java/0.0.4/MODULE.bazel": "82494a01018bb7ef06d4a17ec4cd7a758721f10eb8b6c820a818e70d669500db",
     "https://bcr.bazel.build/modules/bazel_worker_java/0.0.8/MODULE.bazel": "e76479eae70bd4e8f5f4c2dfc5d03ab971cfb18750246c7b3f3454c5c2ee6629",
-    "https://bcr.bazel.build/modules/bazel_worker_java/0.0.8/source.json": "9395c4679444bc47bf7e51a710366a4480aa371c6f6bed01868e2fabcf11acec",
-    "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
-    "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
+    "https://bcr.bazel.build/modules/buildozer/8.5.1/MODULE.bazel": "a35d9561b3fc5b18797c330793e99e3b834a473d5fbd3d7d7634aafc9bdb6f8f",
+    "https://bcr.bazel.build/modules/buildozer/8.5.1/source.json": "e3386e6ff4529f2442800dee47ad28d3e6487f36a1f75ae39ae56c70f0cd2fbd",
     "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "cdf8cbe5ee750db04b78878c9633cc76e80dcf4416cbe982ac3a9222f80713c8",
-    "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/source.json": "fa7b512dfcb5eafd90ce3959cf42a2a6fe96144ebbb4b3b3928054895f2afac2",
+    "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.3/MODULE.bazel": "f1b7bb2dd53e8f2ef984b39485ec8a44e9076dda5c4b8efd2fb4c6a6e856a31d",
+    "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.3/source.json": "ebe931bfe362e4b41e59ee00a528db6074157ff2ced92eb9e970acab2e1089c9",
     "https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8",
     "https://bcr.bazel.build/modules/gazelle/0.33.0/MODULE.bazel": "a13a0f279b462b784fb8dd52a4074526c4a2afe70e114c7d09066097a46b3350",
     "https://bcr.bazel.build/modules/gazelle/0.34.0/MODULE.bazel": "abdd8ce4d70978933209db92e436deb3a8b737859e9354fb5fd11fb5c2004c8a",
     "https://bcr.bazel.build/modules/gazelle/0.36.0/MODULE.bazel": "e375d5d6e9a6ca59b0cb38b0540bc9a05b6aa926d322f2de268ad267a2ee74c0",
     "https://bcr.bazel.build/modules/gazelle/0.40.0/MODULE.bazel": "42ba5378ebe845fca43989a53186ab436d956db498acde790685fe0e8f9c6146",
-    "https://bcr.bazel.build/modules/gazelle/0.40.0/source.json": "1e5ef6e4d8b9b6836d93273c781e78ff829ea2e077afef7a57298040fa4f010a",
+    "https://bcr.bazel.build/modules/gazelle/0.47.0/MODULE.bazel": "b61bb007c4efad134aa30ee7f4a8e2a39b22aa5685f005edaa022fbd1de43ebc",
+    "https://bcr.bazel.build/modules/gazelle/0.47.0/source.json": "aeb2e5df14b7fb298625d75d08b9c65bdb0b56014c5eb89da9e5dd0572280ae6",
     "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb",
     "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
     "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6",
@@ -110,12 +121,14 @@
     "https://bcr.bazel.build/modules/jq.bzl/0.4.0/MODULE.bazel": "a7b39b37589f2b0dad53fd6c1ccaabbdb290330caa920d7ef3e6aad068cd4ab2",
     "https://bcr.bazel.build/modules/jq.bzl/0.4.0/source.json": "52ec7530c4618e03f634b30ff719814a68d7d39c235938b7aa2abbfe1eb1c52c",
     "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075",
-    "https://bcr.bazel.build/modules/jsoncpp/1.9.6.bcr.1/MODULE.bazel": "be2a0db3289111ad410f91ca7e9a84da2dec4441a53a4026892cb29c92a60ca7",
-    "https://bcr.bazel.build/modules/jsoncpp/1.9.6.bcr.1/source.json": "089bd0e9656760a0b872bb78d6e2694820170a76edd10adf368f08ab2d246360",
+    "https://bcr.bazel.build/modules/jsoncpp/1.9.6.bcr.2/MODULE.bazel": "64f508885f907ac2518039b3d0c5c1703a8f6137d9f5d635067ba93d33c2670a",
+    "https://bcr.bazel.build/modules/jsoncpp/1.9.6.bcr.2/source.json": "13199fa0a267ca46814e9e6ab313a71890c8f1822e57376fdc23a2d2cd384051",
     "https://bcr.bazel.build/modules/jsoncpp/1.9.6/MODULE.bazel": "2f8d20d3b7d54143213c4dfc3d98225c42de7d666011528dc8fe91591e2e17b0",
     "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
     "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74",
     "https://bcr.bazel.build/modules/package_metadata/0.0.2/MODULE.bazel": "fb8d25550742674d63d7b250063d4580ca530499f045d70748b1b142081ebb92",
+    "https://bcr.bazel.build/modules/package_metadata/0.0.3/MODULE.bazel": "77890552ecea9e284b5424c9de827a58099348763a4359e975c359a83d4faa83",
+    "https://bcr.bazel.build/modules/package_metadata/0.0.5/MODULE.bazel": "ef4f9439e3270fdd6b9fd4dbc3d2f29d13888e44c529a1b243f7a31dfbc2e8e4",
     "https://bcr.bazel.build/modules/package_metadata/0.0.7/MODULE.bazel": "7adb03933fc8401f495800cf4eafcff0edc6da0ff55c7db223ef69d19f689486",
     "https://bcr.bazel.build/modules/package_metadata/0.0.7/source.json": "50639625e937b56115012674c797cca7a05a96b4878c87d803c13dc2b31de8a0",
     "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
@@ -134,6 +147,7 @@
     "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c",
     "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d",
     "https://bcr.bazel.build/modules/protobuf/27.2/MODULE.bazel": "32450b50673882e4c8c3d10a83f3bc82161b213ed2f80d17e38bece8f165c295",
+    "https://bcr.bazel.build/modules/protobuf/29.0-rc2.bcr.1/MODULE.bazel": "52f4126f63a2f0bbf36b99c2a87648f08467a4eaf92ba726bc7d6a500bbf770c",
     "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df",
     "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92",
     "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e",
@@ -144,8 +158,10 @@
     "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858",
     "https://bcr.bazel.build/modules/protobuf/31.1/MODULE.bazel": "379a389bb330b7b8c1cdf331cc90bf3e13de5614799b3b52cdb7c6f389f6b38e",
     "https://bcr.bazel.build/modules/protobuf/32.1/MODULE.bazel": "89cd2866a9cb07fee9ff74c41ceace11554f32e0d849de4e23ac55515cfada4d",
-    "https://bcr.bazel.build/modules/protobuf/34.1/MODULE.bazel": "41b4bf342b86021f7be1ee912cd140ef300fe527c4949b435f037cda74677f90",
-    "https://bcr.bazel.build/modules/protobuf/34.1/source.json": "9cf0686caa5d920a7e7c0401f76f075df76ea7ad7c064df442b6c029106a6d06",
+    "https://bcr.bazel.build/modules/protobuf/33.1/MODULE.bazel": "982c8a0cceab4d790076f72b7677faf836b0dfadc2b66a34aab7232116c4ae39",
+    "https://bcr.bazel.build/modules/protobuf/33.4/MODULE.bazel": "114775b816b38b6d0ca620450d6b02550c60ceedfdc8d9a229833b34a223dc42",
+    "https://bcr.bazel.build/modules/protobuf/35.0/MODULE.bazel": "ee97170c013d94c366bbb5fe8a97b0650477cbc00e5e0f6a2c297484bc34d81b",
+    "https://bcr.bazel.build/modules/protobuf/35.0/source.json": "2162e80adef862606ba85ae42e0df85eb519376fd6ab49caddee6fb776a1281e",
     "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
     "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34",
     "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/source.json": "6900fdc8a9e95866b8c0d4ad4aba4d4236317b5c1cd04c502df3f0d33afed680",
@@ -156,12 +172,13 @@
     "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8",
     "https://bcr.bazel.build/modules/rules_android/0.6.4/MODULE.bazel": "b4cde12d506dd65d82b2be39761f49f5797303343a3d5b4ee191c0cdf9ef387c",
     "https://bcr.bazel.build/modules/rules_android/0.6.6/MODULE.bazel": "b0fb569752aab65ab1a9db0a8f6cfaf5aa1754965e17e95dcf0e4d88e192a68d",
-    "https://bcr.bazel.build/modules/rules_android/0.6.6/source.json": "a9d8dc2d5a102dc03269a94acc886a4cab82cdcb9ccbc77b0f665d6d17a6ae09",
+    "https://bcr.bazel.build/modules/rules_android/0.7.1/MODULE.bazel": "a806fc382a774252f228a40e3b11b9fcc6276f8778c7fb33e9f72937c6258363",
+    "https://bcr.bazel.build/modules/rules_android/0.7.2/MODULE.bazel": "0862d727582c8d117ac09cd3451b5e93d8b91033f0f13bb61a4a10334958130d",
+    "https://bcr.bazel.build/modules/rules_android/0.7.2/source.json": "bf2dfc8dc72ffd2b58643c9d547f1b7faea4de65aa28ac4894687fa6afa09861",
     "https://bcr.bazel.build/modules/rules_apple/3.16.0/MODULE.bazel": "0d1caf0b8375942ce98ea944be754a18874041e4e0459401d925577624d3a54a",
+    "https://bcr.bazel.build/modules/rules_apple/4.1.0/MODULE.bazel": "76e10fd4a48038d3fc7c5dc6e63b7063bbf5304a2e3bd42edda6ec660eebea68",
     "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647",
-    "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002",
     "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191",
-    "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac",
     "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc",
     "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87",
     "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a",
@@ -170,11 +187,16 @@
     "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
     "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
     "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
+    "https://bcr.bazel.build/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0",
     "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8",
     "https://bcr.bazel.build/modules/rules_cc/0.2.0/MODULE.bazel": "b5c17f90458caae90d2ccd114c81970062946f49f355610ed89bebf954f5783c",
+    "https://bcr.bazel.build/modules/rules_cc/0.2.13/MODULE.bazel": "eecdd666eda6be16a8d9dc15e44b5c75133405e820f620a234acc4b1fdc5aa37",
     "https://bcr.bazel.build/modules/rules_cc/0.2.14/MODULE.bazel": "353c99ed148887ee89c54a17d4100ae7e7e436593d104b668476019023b58df8",
+    "https://bcr.bazel.build/modules/rules_cc/0.2.15/MODULE.bazel": "6a0a4a75a57aa6dc888300d848053a58c6b12a29f89d4304e1c41448514ec6e8",
     "https://bcr.bazel.build/modules/rules_cc/0.2.16/MODULE.bazel": "9242fa89f950c6ef7702801ab53922e99c69b02310c39fb6e62b2bd30df2a1d4",
-    "https://bcr.bazel.build/modules/rules_cc/0.2.16/source.json": "d03d5cde49376d87e14ec14b666c56075e5e3926930327fd5d0484a1ff2ac1cc",
+    "https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84",
+    "https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07",
+    "https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc",
     "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642",
     "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
     "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
@@ -183,47 +205,32 @@
     "https://bcr.bazel.build/modules/rules_go/0.46.0/MODULE.bazel": "3477df8bdcc49e698b9d25f734c4f3a9f5931ff34ee48a2c662be168f5f2d3fd",
     "https://bcr.bazel.build/modules/rules_go/0.50.1/MODULE.bazel": "b91a308dc5782bb0a8021ad4330c81fea5bda77f96b9e4c117b9b9c8f6665ee0",
     "https://bcr.bazel.build/modules/rules_go/0.51.0-rc2/MODULE.bazel": "edfc3a9cea7bedb0eaaff37b0d7817c1a4bf72b3c615580b0ffcee6c52690fd4",
-    "https://bcr.bazel.build/modules/rules_go/0.51.0-rc2/source.json": "6b5cd0b3da2bd0e6949580851db990a04af0a285f072b9a0f059424457cd8cc9",
-    "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
-    "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
-    "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39",
-    "https://bcr.bazel.build/modules/rules_java/6.3.0/MODULE.bazel": "a97c7678c19f236a956ad260d59c86e10a463badb7eb2eda787490f4c969b963",
-    "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6",
-    "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31",
-    "https://bcr.bazel.build/modules/rules_java/7.1.0/MODULE.bazel": "30d9135a2b6561c761bd67bd4990da591e6bdc128790ce3e7afd6a3558b2fb64",
-    "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a",
-    "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6",
-    "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
-    "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2",
-    "https://bcr.bazel.build/modules/rules_java/7.4.0/MODULE.bazel": "a592852f8a3dd539e82ee6542013bf2cadfc4c6946be8941e189d224500a8934",
-    "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe",
-    "https://bcr.bazel.build/modules/rules_java/8.13.0/MODULE.bazel": "0444ebf737d144cf2bb2ccb368e7f1cce735264285f2a3711785827c1686625e",
-    "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615",
-    "https://bcr.bazel.build/modules/rules_java/8.16.1/MODULE.bazel": "0f20b1cecaa8e52f60a8f071e59a20b4e3b9a67f6c56c802ea256f6face692d3",
-    "https://bcr.bazel.build/modules/rules_java/8.16.1/source.json": "072f8d11264edc499621be2dc9ea01d6395db5aa6f8799c034ae01a3e857f2e4",
-    "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017",
-    "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939",
-    "https://bcr.bazel.build/modules/rules_java/8.6.0/MODULE.bazel": "9c064c434606d75a086f15ade5edb514308cccd1544c2b2a89bbac4310e41c71",
-    "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2",
-    "https://bcr.bazel.build/modules/rules_java/8.6.3/MODULE.bazel": "e90505b7a931d194245ffcfb6ff4ca8ef9d46b4e830d12e64817752e0198e2ed",
-    "https://bcr.bazel.build/modules/rules_java/8.9.0/MODULE.bazel": "e17c876cb53dcd817b7b7f0d2985b710610169729e8c371b2221cacdcd3dce4a",
+    "https://bcr.bazel.build/modules/rules_go/0.53.0/MODULE.bazel": "a4ed760d3ac0dbc0d7b967631a9a3fd9100d28f7d9fcf214b4df87d4bfff5f9a",
+    "https://bcr.bazel.build/modules/rules_go/0.59.0/MODULE.bazel": "b7e43e7414a3139a7547d1b4909b29085fbe5182b6c58cbe1ed4c6272815aeae",
+    "https://bcr.bazel.build/modules/rules_go/0.60.0/MODULE.bazel": "4a57ff2ffc2a3570e3c5646575c5a4b07287e91bcdac5d1f72383d51502b48cb",
+    "https://bcr.bazel.build/modules/rules_go/0.60.0/source.json": "1e21368c5e0c3013a110bd79a8fcff8ca46b5bcb2b561713a7273cbfcff7c464",
+    "https://bcr.bazel.build/modules/rules_java/9.3.0/MODULE.bazel": "f657c72d65ac449caae9abf2e68e66c0d36f9416848c4c4903d0b3234229e7f2",
+    "https://bcr.bazel.build/modules/rules_java/9.3.0/source.json": "59ae7e662c3c7042b88bbb42ad12483523e234c65ebe4c51611baa43e85cb248",
     "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
     "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909",
     "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036",
     "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d",
     "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4",
     "https://bcr.bazel.build/modules/rules_jvm_external/6.10/MODULE.bazel": "33e636ca6bc9ee0fa090a38aa33c631ded2d8cf6fead4124181d1b35dc474f7c",
-    "https://bcr.bazel.build/modules/rules_jvm_external/6.10/source.json": "c191249787625db72616a3fb3cc2786ab57355a2e3b615402b8b3b66b0f995b7",
     "https://bcr.bazel.build/modules/rules_jvm_external/6.2/MODULE.bazel": "36a6e52487a855f33cb960724eb56547fa87e2c98a0474c3acad94339d7f8e99",
     "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0",
     "https://bcr.bazel.build/modules/rules_jvm_external/6.6/MODULE.bazel": "153042249c7060536dc95b6bb9f9bb8063b8a0b0cb7acdb381bddbc2374aed55",
     "https://bcr.bazel.build/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd",
+    "https://bcr.bazel.build/modules/rules_jvm_external/6.9/MODULE.bazel": "07c5db05527db7744a54fcffd653e1550d40e0540207a7f7e6d0a4de5bef8274",
+    "https://bcr.bazel.build/modules/rules_jvm_external/7.0/MODULE.bazel": "421482bdbcf05709f933c96b867a599deb517f2804ceb3e74511880610cfbf71",
+    "https://bcr.bazel.build/modules/rules_jvm_external/7.0/source.json": "714cd003eadf5be5c83268311fe8e951db39f802babeaddc536b3560dc8f6faf",
     "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59",
     "https://bcr.bazel.build/modules/rules_kotlin/1.9.5/MODULE.bazel": "043a16a572f610558ec2030db3ff0c9938574e7dd9f58bded1bb07c0192ef025",
     "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3",
     "https://bcr.bazel.build/modules/rules_kotlin/2.1.3/MODULE.bazel": "ce7def6d576aa8d3a9c6d10e13b4d157296229674371f67dbf788dae0afae3d5",
     "https://bcr.bazel.build/modules/rules_kotlin/2.2.2/MODULE.bazel": "00d39c5e0fa78cd86193946265bb849e7878c24e44260f9525108428852b315c",
-    "https://bcr.bazel.build/modules/rules_kotlin/2.2.2/source.json": "7a32c2259c79ae0c9a036121f120de825e3ba5f0f3a209ffbbdccf4dc62489b9",
+    "https://bcr.bazel.build/modules/rules_kotlin/2.3.20/MODULE.bazel": "3443d53d275e14fecfebd0b491f01d06ea3883c04a1b3336e7ae9d5ec9066bef",
+    "https://bcr.bazel.build/modules/rules_kotlin/2.3.20/source.json": "5a5553cffea43f2c5156c8ad0de4a14ad95413ceb39cd4d08f50e2aea86927e8",
     "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
     "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
     "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c",
@@ -252,23 +259,28 @@
     "https://bcr.bazel.build/modules/rules_python/0.37.1/MODULE.bazel": "3faeb2d9fa0a81f8980643ee33f212308f4d93eea4b9ce6f36d0b742e71e9500",
     "https://bcr.bazel.build/modules/rules_python/0.37.2/MODULE.bazel": "b5ffde91410745750b6c13be1c5dc4555ef5bc50562af4a89fd77807fdde626a",
     "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
-    "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
     "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43",
+    "https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13",
     "https://bcr.bazel.build/modules/rules_python/1.4.1/MODULE.bazel": "8991ad45bdc25018301d6b7e1d3626afc3c8af8aaf4bc04f23d0b99c938b73a6",
     "https://bcr.bazel.build/modules/rules_python/1.6.0/MODULE.bazel": "7e04ad8f8d5bea40451cf80b1bd8262552aa73f841415d20db96b7241bd027d8",
     "https://bcr.bazel.build/modules/rules_python/1.7.0/MODULE.bazel": "d01f995ecd137abf30238ad9ce97f8fc3ac57289c8b24bd0bf53324d937a14f8",
-    "https://bcr.bazel.build/modules/rules_python/1.8.0-rc1/MODULE.bazel": "d5348333fd8be9589c3ea8d9110fd00b1c7d84bc7b505668307cbe9f105e9c8f",
-    "https://bcr.bazel.build/modules/rules_python/1.8.0-rc1/source.json": "d26719d5b92a569eaa387bad9667a042d4810193f279ea64590c9d76ae38f3ba",
+    "https://bcr.bazel.build/modules/rules_python/2.0.2/MODULE.bazel": "9d26ecb43f248c2663fb29f3c9b833aa86c31e736096e58c7077f80038f8d32f",
+    "https://bcr.bazel.build/modules/rules_python/2.0.2/source.json": "36a74dedc1498bfbf72855568a24e6aa30c70b5ac7e114bc244c62b3a58b47fc",
     "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/MODULE.bazel": "d44fec647d0aeb67b9f3b980cf68ba634976f3ae7ccd6c07d790b59b87a4f251",
     "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/source.json": "37c10335f2361c337c5c1f34ed36d2da70534c23088062b33a8bdaab68aa9dea",
+    "https://bcr.bazel.build/modules/rules_rust/0.69.0/MODULE.bazel": "4326fec48f2fef0d514de46346f7f77e200c82936dd08b91c9ef039fbdad5c10",
+    "https://bcr.bazel.build/modules/rules_rust/0.69.0/source.json": "0d094307d690cc18b3ab003998697be8070a206f65592c5c8476999796f11c4b",
     "https://bcr.bazel.build/modules/rules_shell/0.1.2/MODULE.bazel": "66e4ca3ce084b04af0b9ff05ff14cab4e5df7503973818bb91cbc6cda08d32fc",
     "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
     "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
     "https://bcr.bazel.build/modules/rules_shell/0.4.1/MODULE.bazel": "00e501db01bbf4e3e1dd1595959092c2fadf2087b2852d3f553b5370f5633592",
     "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b",
-    "https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c",
+    "https://bcr.bazel.build/modules/rules_shell/0.8.0/MODULE.bazel": "f6a89f1d6a669a26f28fe814503857055d76306b79cfc11d12399af08d0b80ae",
+    "https://bcr.bazel.build/modules/rules_shell/0.8.0/source.json": "eb53cc815bc503c6683c5fe12d943f98883f81fc22f51403ec8a95610cba4195",
     "https://bcr.bazel.build/modules/rules_swift/1.16.0/MODULE.bazel": "4a09f199545a60d09895e8281362b1ff3bb08bbde69c6fc87aff5b92fcc916ca",
     "https://bcr.bazel.build/modules/rules_swift/2.1.1/MODULE.bazel": "494900a80f944fc7aa61500c2073d9729dff0b764f0e89b824eb746959bc1046",
+    "https://bcr.bazel.build/modules/rules_swift/2.4.0/MODULE.bazel": "1639617eb1ede28d774d967a738b4a68b0accb40650beadb57c21846beab5efd",
+    "https://bcr.bazel.build/modules/rules_swift/3.1.2/MODULE.bazel": "72c8f5cf9d26427cee6c76c8e3853eb46ce6b0412a081b2b6db6e8ad56267400",
     "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
     "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
     "https://bcr.bazel.build/modules/stardoc/0.5.4/MODULE.bazel": "6569966df04610b8520957cb8e97cf2e9faac2c0309657c537ab51c16c18a2a4",
@@ -279,15 +291,18 @@
     "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5",
     "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216",
     "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91",
+    "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "75aab2373a4bbe2a1260b9bf2a1ebbdbf872d3bd36f80bff058dccd82e89422f",
+    "https://bcr.bazel.build/modules/tar.bzl/0.10.4/MODULE.bazel": "e8f9ff79199e8d9eaad7f1b0a77ad74b30bb82d794b87d8ca942bead5de83ae9",
+    "https://bcr.bazel.build/modules/tar.bzl/0.10.4/source.json": "20143442376c03426f6135292ba02d825cb75308aa47e6bf42dd4cc5a435c2ff",
     "https://bcr.bazel.build/modules/tar.bzl/0.2.1/MODULE.bazel": "52d1c00a80a8cc67acbd01649e83d8dd6a9dc426a6c0b754a04fe8c219c76468",
     "https://bcr.bazel.build/modules/tar.bzl/0.5.1/MODULE.bazel": "7c2eb3dcfc53b0f3d6f9acdfd911ca803eaf92aadf54f8ca6e4c1f3aee288351",
-    "https://bcr.bazel.build/modules/tar.bzl/0.6.0/MODULE.bazel": "a3584b4edcfafcabd9b0ef9819808f05b372957bbdff41601429d5fd0aac2e7c",
-    "https://bcr.bazel.build/modules/tar.bzl/0.6.0/source.json": "4a620381df075a16cb3a7ed57bd1d05f7480222394c64a20fa51bdb636fda658",
+    "https://bcr.bazel.build/modules/toml.bzl/0.3.0/MODULE.bazel": "5016e5dd1ad2200e119a4b28b2b3935e276c4b480f2fe3e952bea7eeba88f578",
+    "https://bcr.bazel.build/modules/toml.bzl/0.3.0/source.json": "0cf7c878c419b37ddb55f3dd93dd7c0c409bd7c4efacb3da504e0748780b2fa9",
     "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
     "https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "c0df5e35ad55e264160417fd0875932ee3c9dda63d9fccace35ac62f45e1b6f9",
     "https://bcr.bazel.build/modules/yq.bzl/0.1.1/MODULE.bazel": "9039681f9bcb8958ee2c87ffc74bdafba9f4369096a2b5634b88abc0eaefa072",
-    "https://bcr.bazel.build/modules/yq.bzl/0.3.2/MODULE.bazel": "0384efa70e8033d842ea73aa4b7199fa099709e236a7264345c03937166670b6",
-    "https://bcr.bazel.build/modules/yq.bzl/0.3.2/source.json": "c4ec3e192477e154f08769e29d69e8fd36e8a4f0f623997f3e1f6f7d328f7d7d",
+    "https://bcr.bazel.build/modules/yq.bzl/0.3.4/MODULE.bazel": "d3a270662f5d766cd7229732d65a5a5bc485240c3007343dd279edfb60c9ae27",
+    "https://bcr.bazel.build/modules/yq.bzl/0.3.4/source.json": "786dafdc2843722da3416e4343ee1a05237227f068590779a6e8496a2064c0f9",
     "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
     "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27",
     "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca",
@@ -298,13 +313,13 @@
   "moduleExtensions": {
     "@@aspect_rules_ts+//ts:extensions.bzl%ext": {
       "general": {
-        "bzlTransitiveDigest": "dhTbv9E6UfT1WJmmu3ORRPO6AKFJvgBjBxu+BO+u1RY=",
+        "bzlTransitiveDigest": "znGIWhsWB2BLftYOmjVGiqrZuccFABBogiS+i4Kg3tU=",
         "usagesDigest": "wmTY4QJrwyD3VUSOHaA0OPkvX0ywR/XPM2EecaJ8yGk=",
-        "recordedFileInputs": {
-          "@@//package.json": "f230bdb8c76c59a925ec0727a1decc727388d47d4b20d95b4e5bcf328edc0c79"
-        },
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "recordedInputs": [
+          "REPO_MAPPING:aspect_rules_ts+,aspect_rules_ts aspect_rules_ts+",
+          "REPO_MAPPING:aspect_rules_ts+,bazel_tools bazel_tools",
+          "FILE:@@//package.json 582559a124fbee5740e1c3a5b4167aa579a1deb338e413bd82f358940feae420"
+        ],
         "generatedRepoSpecs": {
           "npm_typescript": {
             "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version",
@@ -317,61 +332,36 @@
               ]
             }
           }
-        },
-        "recordedRepoMappingEntries": [
-          [
-            "aspect_rules_ts+",
-            "aspect_rules_ts",
-            "aspect_rules_ts+"
-          ],
-          [
-            "aspect_rules_ts+",
-            "bazel_tools",
-            "bazel_tools"
-          ]
-        ]
+        }
       }
     },
     "@@aspect_tools_telemetry+//:extension.bzl%telemetry": {
       "general": {
         "bzlTransitiveDigest": "cl5A2O84vDL6Tt+Qga8FCj1DUDGqn+e7ly5rZ+4xvcc=",
-        "usagesDigest": "mUE7TK/Hw4jOMe0LLCju5evCpF36GFqxeruiv8gPJ08=",
-        "recordedFileInputs": {},
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "usagesDigest": "VyFtm1DFYJGrU6mU+xj6UF6QCpzpFzBONF0/8BC6ZC8=",
+        "recordedInputs": [
+          "REPO_MAPPING:aspect_tools_telemetry+,bazel_lib bazel_lib+",
+          "REPO_MAPPING:aspect_tools_telemetry+,bazel_skylib bazel_skylib+"
+        ],
         "generatedRepoSpecs": {
           "aspect_tools_telemetry_report": {
             "repoRuleId": "@@aspect_tools_telemetry+//:extension.bzl%tel_repository",
             "attributes": {
               "deps": {
-                "aspect_rules_js": "3.0.3",
+                "aspect_rules_js": "3.1.2",
                 "aspect_rules_ts": "3.8.8",
                 "aspect_tools_telemetry": "0.3.3"
               }
             }
           }
-        },
-        "recordedRepoMappingEntries": [
-          [
-            "aspect_tools_telemetry+",
-            "bazel_lib",
-            "bazel_lib+"
-          ],
-          [
-            "aspect_tools_telemetry+",
-            "bazel_skylib",
-            "bazel_skylib+"
-          ]
-        ]
+        }
       }
     },
     "@@protobuf+//python/dist:system_python.bzl%system_python_extension": {
       "general": {
         "bzlTransitiveDigest": "qh0n9IrXU/xS94wxKQrG1J63zrLkA1Wy2Y3BQxptPcI=",
-        "usagesDigest": "AF5a9lHFrJtHw1GTt3jJOs7ZYl1+N2bkn1LbPFLoguA=",
-        "recordedFileInputs": {},
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "usagesDigest": "tCi55FyqtOJ2jXh9vcjrHCl4ov3kpWiwKl103nA9BOI=",
+        "recordedInputs": [],
         "generatedRepoSpecs": {
           "system_python": {
             "repoRuleId": "@@protobuf+//python/dist:system_python.bzl%system_python",
@@ -379,19 +369,17 @@
               "minimum_python_version": "3.9"
             }
           }
-        },
-        "recordedRepoMappingEntries": []
+        }
       }
     },
     "@@pybind11_bazel+//:internal_configure.bzl%internal_configure_extension": {
       "general": {
-        "bzlTransitiveDigest": "NFQjcZF+fAvf5fDH+pqsx4JrfzP9PuHBz6S6ZutIbnw=",
+        "bzlTransitiveDigest": "b+RP7Sgl8KN0VHamrgTqzGLuYPcQ/Mo4ptNkkHUIIlA=",
         "usagesDigest": "D1r3lfzMuUBFxgG8V6o0bQTLMk3GkaGOaPzw53wrwyw=",
-        "recordedFileInputs": {
-          "@@pybind11_bazel+//MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34"
-        },
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "recordedInputs": [
+          "REPO_MAPPING:pybind11_bazel+,bazel_tools bazel_tools",
+          "FILE:@@pybind11_bazel+//MODULE.bazel e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34"
+        ],
         "generatedRepoSpecs": {
           "pybind11": {
             "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
@@ -403,89 +391,69 @@
               ]
             }
           }
-        },
-        "recordedRepoMappingEntries": [
-          [
-            "pybind11_bazel+",
-            "bazel_tools",
-            "bazel_tools"
-          ]
-        ]
+        }
       }
     },
     "@@rules_android+//bzlmod_extensions:apksig.bzl%apksig_extension": {
       "general": {
-        "bzlTransitiveDigest": "By9qVNN7G4oL1vYOJXye7Dp/CbR2ar9oxAW8WXAVcVw=",
-        "usagesDigest": "xq6OVkELeJvOgYo3oY/sUBsGFbcqdV+9BYiNgSPV/po=",
-        "recordedFileInputs": {},
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "bzlTransitiveDigest": "O/gCjP4/VVnaP+zTRGN3DFrkkxLE5GMbE4M1HG3noGQ=",
+        "usagesDigest": "S8lLnnZxdeYUYq3kIGhVMk0wQ9Fd6elmCskvn+SL6iw=",
+        "recordedInputs": [
+          "REPO_MAPPING:rules_android+,bazel_tools bazel_tools"
+        ],
         "generatedRepoSpecs": {
           "apksig": {
             "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
             "attributes": {
-              "url": "https://android.googlesource.com/platform/tools/apksig/+archive/24e3075e68ebe17c0b529bb24bfda819db5e2f3b.tar.gz",
+              "urls": [
+                "https://mirror.bazel.build/android.googlesource.com/platform/tools/apksig/+archive/24e3075e68ebe17c0b529bb24bfda819db5e2f3b.tar.gz"
+              ],
+              "sha256": "12e44fdbd219c5e1cc62099c2a01d775957603d2d4f693f8285f9d95d9a04e77",
               "build_file": "@@rules_android+//bzlmod_extensions:apksig.BUILD"
             }
           }
-        },
-        "recordedRepoMappingEntries": [
-          [
-            "rules_android+",
-            "bazel_tools",
-            "bazel_tools"
-          ]
-        ]
+        }
       }
     },
     "@@rules_android+//bzlmod_extensions:com_android_dex.bzl%com_android_dex_extension": {
       "general": {
-        "bzlTransitiveDigest": "rvWbJQc8jInfIAaXIMhSOqUlwM9HVeLey6q0ISvg08Y=",
-        "usagesDigest": "toF8IFMu98H/VU2p1sfVC5fVXVYJunpbbmtM6tOsQXY=",
-        "recordedFileInputs": {},
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "bzlTransitiveDigest": "fVTI/3B6KjJ93jRf+pOa6ExSALb1hgRndpTPBrJKoZQ=",
+        "usagesDigest": "0hluQmaWiWak6sVMP5L4wXhNyIwv9fw0y5JJ8lnPb1c=",
+        "recordedInputs": [
+          "REPO_MAPPING:rules_android+,bazel_tools bazel_tools"
+        ],
         "generatedRepoSpecs": {
           "com_android_dex": {
             "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
             "attributes": {
-              "url": "https://android.googlesource.com/platform/dalvik/+archive/5a81c499a569731e2395f7c8d13c0e0d4e17a2b6.tar.gz",
-              "build_file": "@@rules_android+//bzlmod_extensions:com_android_dex.BUILD"
+              "urls": [
+                "https://mirror.bazel.build/android.googlesource.com/platform/dalvik/+archive/5a81c499a569731e2395f7c8d13c0e0d4e17a2b6.tar.gz"
+              ],
+              "build_file": "@@rules_android+//bzlmod_extensions:com_android_dex.BUILD",
+              "sha256": "86b4848c038bf687fadc812239cb01fb8d1d15cef3125b480a0448360992b95d"
             }
           }
-        },
-        "recordedRepoMappingEntries": [
-          [
-            "rules_android+",
-            "bazel_tools",
-            "bazel_tools"
-          ]
-        ]
+        }
       }
     },
     "@@rules_android+//rules/android_sdk_repository:rule.bzl%android_sdk_repository_extension": {
       "general": {
-        "bzlTransitiveDigest": "NAy+0M15JNVEBb8Tny6t7j3lKqTnsAMjoBB6LJ+C370=",
-        "usagesDigest": "g9Ur6X6qhf9a8MmY9qXU/jFjkyk/aZVBegI0yVMF0z4=",
-        "recordedFileInputs": {},
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "bzlTransitiveDigest": "qHbR00gVzVzkxX+PRtv4UGcUFMtBz7TK9CNYUWH8nIE=",
+        "usagesDigest": "tTIw/WMyb1t/LzacDY8lDjUznzxQ3MyXW6474WIS3WQ=",
+        "recordedInputs": [],
         "generatedRepoSpecs": {
           "androidsdk": {
             "repoRuleId": "@@rules_android+//rules/android_sdk_repository:rule.bzl%_android_sdk_repository",
             "attributes": {}
           }
-        },
-        "recordedRepoMappingEntries": []
+        }
       }
     },
     "@@rules_nodejs+//nodejs:extensions.bzl%node": {
       "general": {
         "bzlTransitiveDigest": "oZFClfRhTTwsYzpxVPkOpOt/r0+OzEfEV37au0jFZ0s=",
-        "usagesDigest": "a45mOhMotVLUg4hDZfmiBvLMS6oc1CgLVcTACIvW1hU=",
-        "recordedFileInputs": {},
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "usagesDigest": "rmuj3zuYtKpBbldwwQvwLpB0VwZVHNwpx5rco2PvuLY=",
+        "recordedInputs": [],
         "generatedRepoSpecs": {
           "nodejs_linux_amd64": {
             "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
@@ -897,252 +865,17 @@
               "user_node_repository_name": "nodejs"
             }
           }
-        },
-        "recordedRepoMappingEntries": []
-      }
-    },
-    "@@rules_python+//python/extensions:config.bzl%config": {
-      "general": {
-        "bzlTransitiveDigest": "TRGIl0CDmorwyNiblOYyhWuyKzi/kWFHT2uIofq7o9Y=",
-        "usagesDigest": "tIEieEA/gbsjNF3L/Oouyg6UdqGOVxFsPqBjFxkTAKM=",
-        "recordedFileInputs": {},
-        "recordedDirentsInputs": {},
-        "envVariables": {},
-        "generatedRepoSpecs": {
-          "rules_python_internal": {
-            "repoRuleId": "@@rules_python+//python/private:internal_config_repo.bzl%internal_config_repo",
-            "attributes": {
-              "transition_setting_generators": {},
-              "transition_settings": []
-            }
-          },
-          "pypi__build": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/e2/03/f3c8ba0a6b6e30d7d18c40faab90807c9bb5e9a1e3b2fe2008af624a9c97/build-1.2.1-py3-none-any.whl",
-              "sha256": "75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__click": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl",
-              "sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__colorama": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl",
-              "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__importlib_metadata": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl",
-              "sha256": "30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__installer": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl",
-              "sha256": "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__more_itertools": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/50/e2/8e10e465ee3987bb7c9ab69efb91d867d93959095f4807db102d07995d94/more_itertools-10.2.0-py3-none-any.whl",
-              "sha256": "686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__packaging": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl",
-              "sha256": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__pep517": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/25/6e/ca4a5434eb0e502210f591b97537d322546e4833dcb4d470a48c375c5540/pep517-0.13.1-py3-none-any.whl",
-              "sha256": "31b206f67165b3536dd577c5c3f1518e8fbaf38cbc57efff8369a392feff1721",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__pip": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl",
-              "sha256": "ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__pip_tools": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/0d/dc/38f4ce065e92c66f058ea7a368a9c5de4e702272b479c0992059f7693941/pip_tools-7.4.1-py3-none-any.whl",
-              "sha256": "4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__pyproject_hooks": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl",
-              "sha256": "7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__setuptools": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl",
-              "sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__tomli": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl",
-              "sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__wheel": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/7d/cd/d7460c9a869b16c3dd4e1e403cce337df165368c71d6af229a74699622ce/wheel-0.43.0-py3-none-any.whl",
-              "sha256": "55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          },
-          "pypi__zipp": {
-            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
-            "attributes": {
-              "url": "https://files.pythonhosted.org/packages/da/55/a03fd7240714916507e1fcf7ae355bd9d9ed2e6db492595f1a67f61681be/zipp-3.18.2-py3-none-any.whl",
-              "sha256": "dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e",
-              "type": "zip",
-              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
-            }
-          }
-        },
-        "recordedRepoMappingEntries": [
-          [
-            "rules_python+",
-            "bazel_tools",
-            "bazel_tools"
-          ],
-          [
-            "rules_python+",
-            "pypi__build",
-            "rules_python++config+pypi__build"
-          ],
-          [
-            "rules_python+",
-            "pypi__click",
-            "rules_python++config+pypi__click"
-          ],
-          [
-            "rules_python+",
-            "pypi__colorama",
-            "rules_python++config+pypi__colorama"
-          ],
-          [
-            "rules_python+",
-            "pypi__importlib_metadata",
-            "rules_python++config+pypi__importlib_metadata"
-          ],
-          [
-            "rules_python+",
-            "pypi__installer",
-            "rules_python++config+pypi__installer"
-          ],
-          [
-            "rules_python+",
-            "pypi__more_itertools",
-            "rules_python++config+pypi__more_itertools"
-          ],
-          [
-            "rules_python+",
-            "pypi__packaging",
-            "rules_python++config+pypi__packaging"
-          ],
-          [
-            "rules_python+",
-            "pypi__pep517",
-            "rules_python++config+pypi__pep517"
-          ],
-          [
-            "rules_python+",
-            "pypi__pip",
-            "rules_python++config+pypi__pip"
-          ],
-          [
-            "rules_python+",
-            "pypi__pip_tools",
-            "rules_python++config+pypi__pip_tools"
-          ],
-          [
-            "rules_python+",
-            "pypi__pyproject_hooks",
-            "rules_python++config+pypi__pyproject_hooks"
-          ],
-          [
-            "rules_python+",
-            "pypi__setuptools",
-            "rules_python++config+pypi__setuptools"
-          ],
-          [
-            "rules_python+",
-            "pypi__tomli",
-            "rules_python++config+pypi__tomli"
-          ],
-          [
-            "rules_python+",
-            "pypi__wheel",
-            "rules_python++config+pypi__wheel"
-          ],
-          [
-            "rules_python+",
-            "pypi__zipp",
-            "rules_python++config+pypi__zipp"
-          ]
-        ]
+        }
       }
     },
     "@@rules_python+//python/uv:uv.bzl%uv": {
       "general": {
-        "bzlTransitiveDigest": "ijW9KS7qsIY+yBVvJ+Nr1mzwQox09j13DnE3iIwaeTM=",
-        "usagesDigest": "s63+dBGiTSbvuV/QBtGNrbYox+e7K5QXThW1NgBreis=",
-        "recordedFileInputs": {},
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "bzlTransitiveDigest": "I8FPZMevE2oI/peSpMBRVIN++WOtfjtJVjbPsBZQ87A=",
+        "usagesDigest": "R0pij/Kri0Z/7GcM2UBot6XmOqgduje0j8y9wNMWjLA=",
+        "recordedInputs": [
+          "REPO_MAPPING:rules_python+,bazel_tools bazel_tools",
+          "REPO_MAPPING:rules_python+,platforms platforms"
+        ],
         "generatedRepoSpecs": {
           "uv": {
             "repoRuleId": "@@rules_python+//python/uv/private:uv_toolchains_repo.bzl%uv_toolchains_repo",
@@ -1162,83 +895,437 @@
               "toolchain_target_settings": {}
             }
           }
+        }
+      }
+    },
+    "@@rules_rust+//crate_universe:extension.bzl%crate": {
+      "general": {
+        "bzlTransitiveDigest": "4ZxdDHM5dGVbcpGNyPxSWkXiz1DTw/ldJgFC1a5SPOI=",
+        "usagesDigest": "EuFUqVKVHF263jHTWOHXs4tFACdRNVOhwpoytdk19bs=",
+        "recordedInputs": [
+          "ENV:CARGO_BAZEL_DEBUG \\0",
+          "ENV:CARGO_BAZEL_GENERATOR_SHA256 \\0",
+          "ENV:CARGO_BAZEL_GENERATOR_URL \\0",
+          "ENV:CARGO_BAZEL_ISOLATED \\0",
+          "ENV:CARGO_BAZEL_REPIN \\0",
+          "ENV:CARGO_BAZEL_REPIN_ONLY \\0",
+          "ENV:CARGO_BAZEL_TIMEOUT \\0",
+          "ENV:REPIN \\0",
+          "REPO_MAPPING:bazel_features+,bazel_features_globals bazel_features++version_extension+bazel_features_globals",
+          "REPO_MAPPING:bazel_features+,bazel_features_version bazel_features++version_extension+bazel_features_version",
+          "REPO_MAPPING:rules_cc+,bazel_skylib bazel_skylib+",
+          "REPO_MAPPING:rules_cc+,bazel_tools bazel_tools",
+          "REPO_MAPPING:rules_cc+,cc_compatibility_proxy rules_cc++compatibility_proxy+cc_compatibility_proxy",
+          "REPO_MAPPING:rules_cc+,platforms platforms",
+          "REPO_MAPPING:rules_cc+,rules_cc rules_cc+",
+          "REPO_MAPPING:rules_cc++compatibility_proxy+cc_compatibility_proxy,rules_cc rules_cc+",
+          "REPO_MAPPING:rules_rust+,bazel_features bazel_features+",
+          "REPO_MAPPING:rules_rust+,bazel_skylib bazel_skylib+",
+          "REPO_MAPPING:rules_rust+,bazel_tools bazel_tools",
+          "REPO_MAPPING:rules_rust+,rules_cc rules_cc+",
+          "REPO_MAPPING:rules_rust+,rules_rust rules_rust+"
+        ],
+        "generatedRepoSpecs": {
+          "crates": {
+            "repoRuleId": "@@rules_rust+//crate_universe:extensions.bzl%_generate_repo",
+            "attributes": {
+              "contents": {
+                "BUILD.bazel": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files(\n    [\n        \"cargo-bazel.json\",\n        \"crates.bzl\",\n        \"defs.bzl\",\n    ] + glob(\n        allow_empty = True,\n        include = [\"*.bazel\"],\n    ),\n)\n\nfilegroup(\n    name = \"srcs\",\n    srcs = glob(\n        allow_empty = True,\n        include = [\n            \"*.bazel\",\n            \"*.bzl\",\n        ],\n    ),\n)\n\n# Workspace Member Dependencies\nalias(\n    name = \"googletest-0.14.2\",\n    actual = \"@crates__googletest-0.14.2//:googletest\",\n    tags = [\"manual\"],\n)\n\nalias(\n    name = \"googletest\",\n    actual = \"@crates__googletest-0.14.2//:googletest\",\n    tags = [\"manual\"],\n)\n\nalias(\n    name = \"linkme-0.3.36\",\n    actual = \"@crates__linkme-0.3.36//:linkme\",\n    tags = [\"manual\"],\n)\n\nalias(\n    name = \"linkme\",\n    actual = \"@crates__linkme-0.3.36//:linkme\",\n    tags = [\"manual\"],\n)\n\nalias(\n    name = \"paste-1.0.15\",\n    actual = \"@crates__paste-1.0.15//:paste\",\n    tags = [\"manual\"],\n)\n\nalias(\n    name = \"paste\",\n    actual = \"@crates__paste-1.0.15//:paste\",\n    tags = [\"manual\"],\n)\n\nalias(\n    name = \"quote-1.0.45\",\n    actual = \"@crates__quote-1.0.45//:quote\",\n    tags = [\"manual\"],\n)\n\nalias(\n    name = \"quote\",\n    actual = \"@crates__quote-1.0.45//:quote\",\n    tags = [\"manual\"],\n)\n\nalias(\n    name = \"syn-2.0.117\",\n    actual = \"@crates__syn-2.0.117//:syn\",\n    tags = [\"manual\"],\n)\n\nalias(\n    name = \"syn\",\n    actual = \"@crates__syn-2.0.117//:syn\",\n    tags = [\"manual\"],\n)\n",
+                "alias_rules.bzl": "\"\"\"Alias that transitions its target to `compilation_mode=opt`.  Use `transition_alias=\"opt\"` to enable.\"\"\"\n\nload(\"@rules_cc//cc:defs.bzl\", \"CcInfo\")\nload(\"@rules_rust//rust:rust_common.bzl\", \"COMMON_PROVIDERS\")\n\ndef _transition_alias_impl(ctx):\n    # `ctx.attr.actual` is a list of 1 item due to the transition\n    providers = [ctx.attr.actual[0][provider] for provider in COMMON_PROVIDERS]\n    if CcInfo in ctx.attr.actual[0]:\n        providers.append(ctx.attr.actual[0][CcInfo])\n    return providers\n\ndef _change_compilation_mode(compilation_mode):\n    def _change_compilation_mode_impl(_settings, _attr):\n        return {\n            \"//command_line_option:compilation_mode\": compilation_mode,\n        }\n\n    return transition(\n        implementation = _change_compilation_mode_impl,\n        inputs = [],\n        outputs = [\n            \"//command_line_option:compilation_mode\",\n        ],\n    )\n\ndef _transition_alias_rule(compilation_mode):\n    return rule(\n        implementation = _transition_alias_impl,\n        provides = COMMON_PROVIDERS,\n        attrs = {\n            \"actual\": attr.label(\n                mandatory = True,\n                doc = \"`rust_library()` target to transition to `compilation_mode=opt`.\",\n                providers = COMMON_PROVIDERS,\n                cfg = _change_compilation_mode(compilation_mode),\n            ),\n            \"_allowlist_function_transition\": attr.label(\n                default = \"@bazel_tools//tools/allowlists/function_transition_allowlist\",\n            ),\n        },\n        doc = \"Transitions a Rust library crate to the `compilation_mode=opt`.\",\n    )\n\ntransition_alias_dbg = _transition_alias_rule(\"dbg\")\ntransition_alias_fastbuild = _transition_alias_rule(\"fastbuild\")\ntransition_alias_opt = _transition_alias_rule(\"opt\")\n",
+                "defs.bzl": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\"\"\"\n# `crates_repository` API\n\n- [aliases](#aliases)\n- [crate_deps](#crate_deps)\n- [all_crate_deps](#all_crate_deps)\n- [crate_repositories](#crate_repositories)\n\n\"\"\"\n\nload(\"@bazel_tools//tools/build_defs/repo:git.bzl\", \"new_git_repository\")\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\nload(\"@bazel_tools//tools/build_defs/repo:utils.bzl\", \"maybe\")\nload(\"@bazel_skylib//lib:selects.bzl\", \"selects\")\nload(\"@rules_rust//crate_universe/private:local_crate_mirror.bzl\", \"local_crate_mirror\")\n\n###############################################################################\n# MACROS API\n###############################################################################\n\n# An identifier that represent common dependencies (unconditional).\n_COMMON_CONDITION = \"\"\n\ndef _flatten_dependency_maps(all_dependency_maps):\n    \"\"\"Flatten a list of dependency maps into one dictionary.\n\n    Dependency maps have the following structure:\n\n    ```python\n    DEPENDENCIES_MAP = {\n        # The first key in the map is a Bazel package\n        # name of the workspace this file is defined in.\n        \"workspace_member_package\": {\n\n            # Not all dependencies are supported for all platforms.\n            # the condition key is the condition required to be true\n            # on the host platform.\n            \"condition\": {\n\n                # An alias to a crate target.     # The label of the crate target the\n                # Aliases are only crate names.   # package name refers to.\n                \"package_name\":                   \"@full//:label\",\n            }\n        }\n    }\n    ```\n\n    Args:\n        all_dependency_maps (list): A list of dicts as described above\n\n    Returns:\n        dict: A dictionary as described above\n    \"\"\"\n    dependencies = {}\n\n    for workspace_deps_map in all_dependency_maps:\n        for pkg_name, conditional_deps_map in workspace_deps_map.items():\n            if pkg_name not in dependencies:\n                non_frozen_map = dict()\n                for key, values in conditional_deps_map.items():\n                    non_frozen_map.update({key: dict(values.items())})\n                dependencies.setdefault(pkg_name, non_frozen_map)\n                continue\n\n            for condition, deps_map in conditional_deps_map.items():\n                # If the condition has not been recorded, do so and continue\n                if condition not in dependencies[pkg_name]:\n                    dependencies[pkg_name].setdefault(condition, dict(deps_map.items()))\n                    continue\n\n                # Alert on any miss-matched dependencies\n                inconsistent_entries = []\n                for crate_name, crate_label in deps_map.items():\n                    existing = dependencies[pkg_name][condition].get(crate_name)\n                    if existing and existing != crate_label:\n                        inconsistent_entries.append((crate_name, existing, crate_label))\n                    dependencies[pkg_name][condition].update({crate_name: crate_label})\n\n    return dependencies\n\ndef crate_deps(deps, package_name = None):\n    \"\"\"Finds the fully qualified label of the requested crates for the package where this macro is called.\n\n    Args:\n        deps (list): The desired list of crate targets.\n        package_name (str, optional): The package name of the set of dependencies to look up.\n            Defaults to `native.package_name()`.\n\n    Returns:\n        list: A list of labels to generated rust targets (str)\n    \"\"\"\n\n    if not deps:\n        return []\n\n    if package_name == None:\n        package_name = native.package_name()\n\n    # Join both sets of dependencies\n    dependencies = _flatten_dependency_maps([\n        _NORMAL_DEPENDENCIES,\n        _NORMAL_DEV_DEPENDENCIES,\n        _PROC_MACRO_DEPENDENCIES,\n        _PROC_MACRO_DEV_DEPENDENCIES,\n        _BUILD_DEPENDENCIES,\n        _BUILD_PROC_MACRO_DEPENDENCIES,\n    ]).pop(package_name, {})\n\n    # Combine all conditional packages so we can easily index over a flat list\n    # TODO: Perhaps this should actually return select statements and maintain\n    # the conditionals of the dependencies\n    flat_deps = {}\n    for deps_set in dependencies.values():\n        for crate_name, crate_label in deps_set.items():\n            flat_deps.update({crate_name: crate_label})\n\n    missing_crates = []\n    crate_targets = []\n    for crate_target in deps:\n        if crate_target not in flat_deps:\n            missing_crates.append(crate_target)\n        else:\n            crate_targets.append(flat_deps[crate_target])\n\n    if missing_crates:\n        fail(\"Could not find crates `{}` among dependencies of `{}`. Available dependencies were `{}`\".format(\n            missing_crates,\n            package_name,\n            dependencies,\n        ))\n\n    return crate_targets\n\ndef all_crate_deps(\n        normal = False, \n        normal_dev = False, \n        proc_macro = False, \n        proc_macro_dev = False,\n        build = False,\n        build_proc_macro = False,\n        package_name = None):\n    \"\"\"Finds the fully qualified label of all requested direct crate dependencies \\\n    for the package where this macro is called.\n\n    If no parameters are set, all normal dependencies are returned. Setting any one flag will\n    otherwise impact the contents of the returned list.\n\n    Args:\n        normal (bool, optional): If True, normal dependencies are included in the\n            output list.\n        normal_dev (bool, optional): If True, normal dev dependencies will be\n            included in the output list.\n        proc_macro (bool, optional): If True, proc_macro dependencies are included\n            in the output list.\n        proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are\n            included in the output list.\n        build (bool, optional): If True, build dependencies are included\n            in the output list.\n        build_proc_macro (bool, optional): If True, build proc_macro dependencies are\n            included in the output list.\n        package_name (str, optional): The package name of the set of dependencies to look up.\n            Defaults to `native.package_name()` when unset.\n\n    Returns:\n        list: A list of labels to generated rust targets (str)\n    \"\"\"\n\n    if package_name == None:\n        package_name = native.package_name()\n\n    # Determine the relevant maps to use\n    all_dependency_maps = []\n    if normal:\n        all_dependency_maps.append(_NORMAL_DEPENDENCIES)\n    if normal_dev:\n        all_dependency_maps.append(_NORMAL_DEV_DEPENDENCIES)\n    if proc_macro:\n        all_dependency_maps.append(_PROC_MACRO_DEPENDENCIES)\n    if proc_macro_dev:\n        all_dependency_maps.append(_PROC_MACRO_DEV_DEPENDENCIES)\n    if build:\n        all_dependency_maps.append(_BUILD_DEPENDENCIES)\n    if build_proc_macro:\n        all_dependency_maps.append(_BUILD_PROC_MACRO_DEPENDENCIES)\n\n    # Default to always using normal dependencies\n    if not all_dependency_maps:\n        all_dependency_maps.append(_NORMAL_DEPENDENCIES)\n\n    dependencies = _flatten_dependency_maps(all_dependency_maps).pop(package_name, None)\n\n    if not dependencies:\n        if dependencies == None:\n            fail(\"Tried to get all_crate_deps for package \" + package_name + \" but that package had no Cargo.toml file\")\n        else:\n            return []\n\n    crate_deps = list(dependencies.pop(_COMMON_CONDITION, {}).values())\n    for condition, deps in dependencies.items():\n        crate_deps += selects.with_or({\n            tuple(_CONDITIONS[condition]): deps.values(),\n            \"//conditions:default\": [],\n        })\n\n    return crate_deps\n\ndef aliases(\n        normal = False,\n        normal_dev = False,\n        proc_macro = False,\n        proc_macro_dev = False,\n        build = False,\n        build_proc_macro = False,\n        package_name = None):\n    \"\"\"Produces a map of Crate alias names to their original label\n\n    If no dependency kinds are specified, `normal` and `proc_macro` are used by default.\n    Setting any one flag will otherwise determine the contents of the returned dict.\n\n    Args:\n        normal (bool, optional): If True, normal dependencies are included in the\n            output list.\n        normal_dev (bool, optional): If True, normal dev dependencies will be\n            included in the output list..\n        proc_macro (bool, optional): If True, proc_macro dependencies are included\n            in the output list.\n        proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are\n            included in the output list.\n        build (bool, optional): If True, build dependencies are included\n            in the output list.\n        build_proc_macro (bool, optional): If True, build proc_macro dependencies are\n            included in the output list.\n        package_name (str, optional): The package name of the set of dependencies to look up.\n            Defaults to `native.package_name()` when unset.\n\n    Returns:\n        dict: The aliases of all associated packages\n    \"\"\"\n    if package_name == None:\n        package_name = native.package_name()\n\n    # Determine the relevant maps to use\n    all_aliases_maps = []\n    if normal:\n        all_aliases_maps.append(_NORMAL_ALIASES)\n    if normal_dev:\n        all_aliases_maps.append(_NORMAL_DEV_ALIASES)\n    if proc_macro:\n        all_aliases_maps.append(_PROC_MACRO_ALIASES)\n    if proc_macro_dev:\n        all_aliases_maps.append(_PROC_MACRO_DEV_ALIASES)\n    if build:\n        all_aliases_maps.append(_BUILD_ALIASES)\n    if build_proc_macro:\n        all_aliases_maps.append(_BUILD_PROC_MACRO_ALIASES)\n\n    # Default to always using normal aliases\n    if not all_aliases_maps:\n        all_aliases_maps.append(_NORMAL_ALIASES)\n        all_aliases_maps.append(_PROC_MACRO_ALIASES)\n\n    aliases = _flatten_dependency_maps(all_aliases_maps).pop(package_name, None)\n\n    if not aliases:\n        return dict()\n\n    common_items = aliases.pop(_COMMON_CONDITION, {}).items()\n\n    # If there are only common items in the dictionary, immediately return them\n    if not len(aliases.keys()) == 1:\n        return dict(common_items)\n\n    # Build a single select statement where each conditional has accounted for the\n    # common set of aliases.\n    crate_aliases = {\"//conditions:default\": dict(common_items)}\n    for condition, deps in aliases.items():\n        condition_triples = _CONDITIONS[condition]\n        for triple in condition_triples:\n            if triple in crate_aliases:\n                crate_aliases[triple].update(deps)\n            else:\n                crate_aliases.update({triple: dict(deps.items() + common_items)})\n\n    return select(crate_aliases)\n\n###############################################################################\n# WORKSPACE MEMBER DEPS AND ALIASES\n###############################################################################\n\n_NORMAL_DEPENDENCIES = {\n    \"\": {\n        _COMMON_CONDITION: {\n            \"googletest\": Label(\"@crates//:googletest-0.14.2\"),\n            \"linkme\": Label(\"@crates//:linkme-0.3.36\"),\n            \"quote\": Label(\"@crates//:quote-1.0.45\"),\n            \"syn\": Label(\"@crates//:syn-2.0.117\"),\n        },\n    },\n}\n\n\n_NORMAL_ALIASES = {\n    \"\": {\n        _COMMON_CONDITION: {\n        },\n    },\n}\n\n\n_NORMAL_DEV_DEPENDENCIES = {\n    \"\": {\n    },\n}\n\n\n_NORMAL_DEV_ALIASES = {\n    \"\": {\n    },\n}\n\n\n_PROC_MACRO_DEPENDENCIES = {\n    \"\": {\n        _COMMON_CONDITION: {\n            \"paste\": Label(\"@crates//:paste-1.0.15\"),\n        },\n    },\n}\n\n\n_PROC_MACRO_ALIASES = {\n    \"\": {\n    },\n}\n\n\n_PROC_MACRO_DEV_DEPENDENCIES = {\n    \"\": {\n    },\n}\n\n\n_PROC_MACRO_DEV_ALIASES = {\n    \"\": {\n    },\n}\n\n\n_BUILD_DEPENDENCIES = {\n    \"\": {\n    },\n}\n\n\n_BUILD_ALIASES = {\n    \"\": {\n    },\n}\n\n\n_BUILD_PROC_MACRO_DEPENDENCIES = {\n    \"\": {\n    },\n}\n\n\n_BUILD_PROC_MACRO_ALIASES = {\n    \"\": {\n    },\n}\n\n\n_CONDITIONS = {\n    \"aarch64-apple-darwin\": [\"@rules_rust//rust/platform:aarch64-apple-darwin\"],\n    \"aarch64-unknown-linux-gnu\": [\"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\"],\n    \"wasm32-unknown-unknown\": [\"@rules_rust//rust/platform:wasm32-unknown-unknown\"],\n    \"wasm32-wasip1\": [\"@rules_rust//rust/platform:wasm32-wasip1\"],\n    \"x86_64-pc-windows-msvc\": [\"@rules_rust//rust/platform:x86_64-pc-windows-msvc\"],\n    \"x86_64-unknown-linux-gnu\": [\"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\"],\n    \"x86_64-unknown-nixos-gnu\": [\"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\"],\n}\n\n###############################################################################\n\ndef crate_repositories():\n    \"\"\"A macro for defining repositories for all generated crates.\n\n    Returns:\n      A list of repos visible to the module through the module extension.\n    \"\"\"\n    maybe(\n        http_archive,\n        name = \"crates__aho-corasick-1.1.4\",\n        sha256 = \"ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/aho-corasick/1.1.4/download\"],\n        strip_prefix = \"aho-corasick-1.1.4\",\n        build_file = Label(\"@crates//crates:BUILD.aho-corasick-1.1.4.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__autocfg-1.5.1\",\n        sha256 = \"f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/autocfg/1.5.1/download\"],\n        strip_prefix = \"autocfg-1.5.1\",\n        build_file = Label(\"@crates//crates:BUILD.autocfg-1.5.1.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__googletest-0.14.2\",\n        sha256 = \"06597b7d02ee58b9a37f522785ac15b9e18c6b178747c4439a6c03fbb35ea753\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/googletest/0.14.2/download\"],\n        strip_prefix = \"googletest-0.14.2\",\n        build_file = Label(\"@crates//crates:BUILD.googletest-0.14.2.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__googletest_macro-0.14.2\",\n        sha256 = \"c31d9f07c9c19b855faebf71637be3b43f8e13a518aece5d61a3beee7710b4ef\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/googletest_macro/0.14.2/download\"],\n        strip_prefix = \"googletest_macro-0.14.2\",\n        build_file = Label(\"@crates//crates:BUILD.googletest_macro-0.14.2.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__linkme-0.3.36\",\n        sha256 = \"e83272d46373fb8decca684579ac3e7c8f3d71d4cc3aa693df8759e260ae41cf\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/linkme/0.3.36/download\"],\n        strip_prefix = \"linkme-0.3.36\",\n        build_file = Label(\"@crates//crates:BUILD.linkme-0.3.36.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__linkme-impl-0.3.36\",\n        sha256 = \"32d59e20403c7d08fe62b4376edfe5c7fb2ef1e6b1465379686d0f21c8df444b\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/linkme-impl/0.3.36/download\"],\n        strip_prefix = \"linkme-impl-0.3.36\",\n        build_file = Label(\"@crates//crates:BUILD.linkme-impl-0.3.36.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__memchr-2.8.1\",\n        sha256 = \"6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/memchr/2.8.1/download\"],\n        strip_prefix = \"memchr-2.8.1\",\n        build_file = Label(\"@crates//crates:BUILD.memchr-2.8.1.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__num-traits-0.2.19\",\n        sha256 = \"071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/num-traits/0.2.19/download\"],\n        strip_prefix = \"num-traits-0.2.19\",\n        build_file = Label(\"@crates//crates:BUILD.num-traits-0.2.19.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__paste-1.0.15\",\n        sha256 = \"57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/paste/1.0.15/download\"],\n        strip_prefix = \"paste-1.0.15\",\n        build_file = Label(\"@crates//crates:BUILD.paste-1.0.15.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__proc-macro2-1.0.106\",\n        sha256 = \"8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/proc-macro2/1.0.106/download\"],\n        strip_prefix = \"proc-macro2-1.0.106\",\n        build_file = Label(\"@crates//crates:BUILD.proc-macro2-1.0.106.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__quote-1.0.45\",\n        sha256 = \"41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/quote/1.0.45/download\"],\n        strip_prefix = \"quote-1.0.45\",\n        build_file = Label(\"@crates//crates:BUILD.quote-1.0.45.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__regex-1.12.3\",\n        sha256 = \"e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/regex/1.12.3/download\"],\n        strip_prefix = \"regex-1.12.3\",\n        build_file = Label(\"@crates//crates:BUILD.regex-1.12.3.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__regex-automata-0.4.14\",\n        sha256 = \"6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/regex-automata/0.4.14/download\"],\n        strip_prefix = \"regex-automata-0.4.14\",\n        build_file = Label(\"@crates//crates:BUILD.regex-automata-0.4.14.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__regex-syntax-0.8.10\",\n        sha256 = \"dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/regex-syntax/0.8.10/download\"],\n        strip_prefix = \"regex-syntax-0.8.10\",\n        build_file = Label(\"@crates//crates:BUILD.regex-syntax-0.8.10.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__rustversion-1.0.22\",\n        sha256 = \"b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/rustversion/1.0.22/download\"],\n        strip_prefix = \"rustversion-1.0.22\",\n        build_file = Label(\"@crates//crates:BUILD.rustversion-1.0.22.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__syn-2.0.117\",\n        sha256 = \"e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/syn/2.0.117/download\"],\n        strip_prefix = \"syn-2.0.117\",\n        build_file = Label(\"@crates//crates:BUILD.syn-2.0.117.bazel\"),\n    )\n\n    maybe(\n        http_archive,\n        name = \"crates__unicode-ident-1.0.24\",\n        sha256 = \"e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75\",\n        type = \"tar.gz\",\n        urls = [\"https://static.crates.io/crates/unicode-ident/1.0.24/download\"],\n        strip_prefix = \"unicode-ident-1.0.24\",\n        build_file = Label(\"@crates//crates:BUILD.unicode-ident-1.0.24.bazel\"),\n    )\n\n    return [\n       struct(repo=\"crates__googletest-0.14.2\", is_dev_dep = False),\n       struct(repo=\"crates__linkme-0.3.36\", is_dev_dep = False),\n       struct(repo=\"crates__paste-1.0.15\", is_dev_dep = False),\n       struct(repo=\"crates__quote-1.0.45\", is_dev_dep = False),\n       struct(repo=\"crates__syn-2.0.117\", is_dev_dep = False),\n    ]\n"
+              }
+            }
+          },
+          "crates__aho-corasick-1.1.4": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/aho-corasick/1.1.4/download"
+              ],
+              "strip_prefix": "aho-corasick-1.1.4",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"aho_corasick\",\n    deps = [\n        \"@crates__memchr-2.8.1//:memchr\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"perf-literal\",\n        \"std\",\n    ],\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=aho-corasick\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"1.1.4\",\n)\n"
+            }
+          },
+          "crates__autocfg-1.5.1": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/autocfg/1.5.1/download"
+              ],
+              "strip_prefix": "autocfg-1.5.1",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"autocfg\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_root = \"src/lib.rs\",\n    edition = \"2015\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=autocfg\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"1.5.1\",\n)\n"
+            }
+          },
+          "crates__googletest-0.14.2": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "06597b7d02ee58b9a37f522785ac15b9e18c6b178747c4439a6c03fbb35ea753",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/googletest/0.14.2/download"
+              ],
+              "strip_prefix": "googletest-0.14.2",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"googletest\",\n    deps = [\n        \"@crates__num-traits-0.2.19//:num_traits\",\n        \"@crates__regex-1.12.3//:regex\",\n    ],\n    proc_macro_deps = [\n        \"@crates__googletest_macro-0.14.2//:googletest_macro\",\n        \"@crates__rustversion-1.0.22//:rustversion\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=googletest\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"0.14.2\",\n)\n"
+            }
+          },
+          "crates__googletest_macro-0.14.2": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "c31d9f07c9c19b855faebf71637be3b43f8e13a518aece5d61a3beee7710b4ef",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/googletest_macro/0.14.2/download"
+              ],
+              "strip_prefix": "googletest_macro-0.14.2",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_proc_macro\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_proc_macro(\n    name = \"googletest_macro\",\n    deps = [\n        \"@crates__proc-macro2-1.0.106//:proc_macro2\",\n        \"@crates__quote-1.0.45//:quote\",\n        \"@crates__syn-2.0.117//:syn\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=googletest_macro\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"0.14.2\",\n)\n"
+            }
+          },
+          "crates__linkme-0.3.36": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "e83272d46373fb8decca684579ac3e7c8f3d71d4cc3aa693df8759e260ae41cf",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/linkme/0.3.36/download"
+              ],
+              "strip_prefix": "linkme-0.3.36",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\n    \"@rules_rust//cargo:defs.bzl\",\n    \"cargo_build_script\",\n    \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"linkme\",\n    deps = [\n        \"@crates__linkme-0.3.36//:build_script_build\",\n    ],\n    proc_macro_deps = [\n        \"@crates__linkme-impl-0.3.36//:linkme_impl\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=linkme\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"0.3.36\",\n)\n\ncargo_build_script(\n    name = \"_bs\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \"**/*.rs\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_name = \"build_script_build\",\n    crate_root = \"build.rs\",\n    data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    edition = \"2021\",\n    pkg_name = \"linkme\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=linkme\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    version = \"0.3.36\",\n    visibility = [\"//visibility:private\"],\n)\n\nalias(\n    name = \"build_script_build\",\n    actual = \":_bs\",\n    tags = [\"manual\"],\n)\n"
+            }
+          },
+          "crates__linkme-impl-0.3.36": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "32d59e20403c7d08fe62b4376edfe5c7fb2ef1e6b1465379686d0f21c8df444b",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/linkme-impl/0.3.36/download"
+              ],
+              "strip_prefix": "linkme-impl-0.3.36",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\n    \"@rules_rust//cargo:defs.bzl\",\n    \"cargo_build_script\",\n    \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_proc_macro\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_proc_macro(\n    name = \"linkme_impl\",\n    deps = [\n        \"@crates__linkme-impl-0.3.36//:build_script_build\",\n        \"@crates__proc-macro2-1.0.106//:proc_macro2\",\n        \"@crates__quote-1.0.45//:quote\",\n        \"@crates__syn-2.0.117//:syn\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=linkme-impl\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"0.3.36\",\n)\n\ncargo_build_script(\n    name = \"_bs\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \"**/*.rs\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_name = \"build_script_build\",\n    crate_root = \"build.rs\",\n    data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    edition = \"2021\",\n    pkg_name = \"linkme-impl\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=linkme-impl\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    version = \"0.3.36\",\n    visibility = [\"//visibility:private\"],\n)\n\nalias(\n    name = \"build_script_build\",\n    actual = \":_bs\",\n    tags = [\"manual\"],\n)\n"
+            }
+          },
+          "crates__memchr-2.8.1": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/memchr/2.8.1/download"
+              ],
+              "strip_prefix": "memchr-2.8.1",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"memchr\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"alloc\",\n        \"std\",\n    ],\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=memchr\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"2.8.1\",\n)\n"
+            }
+          },
+          "crates__num-traits-0.2.19": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/num-traits/0.2.19/download"
+              ],
+              "strip_prefix": "num-traits-0.2.19",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\n    \"@rules_rust//cargo:defs.bzl\",\n    \"cargo_build_script\",\n    \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"num_traits\",\n    deps = [\n        \"@crates__num-traits-0.2.19//:build_script_build\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"default\",\n        \"std\",\n    ],\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=num-traits\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"0.2.19\",\n)\n\ncargo_build_script(\n    name = \"_bs\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \"**/*.rs\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"default\",\n        \"std\",\n    ],\n    crate_name = \"build_script_build\",\n    crate_root = \"build.rs\",\n    data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    deps = [\n        \"@crates__autocfg-1.5.1//:autocfg\",\n    ],\n    edition = \"2021\",\n    pkg_name = \"num-traits\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=num-traits\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    version = \"0.2.19\",\n    visibility = [\"//visibility:private\"],\n)\n\nalias(\n    name = \"build_script_build\",\n    actual = \":_bs\",\n    tags = [\"manual\"],\n)\n"
+            }
+          },
+          "crates__paste-1.0.15": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/paste/1.0.15/download"
+              ],
+              "strip_prefix": "paste-1.0.15",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\n    \"@rules_rust//cargo:defs.bzl\",\n    \"cargo_build_script\",\n    \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_proc_macro\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_proc_macro(\n    name = \"paste\",\n    deps = [\n        \"@crates__paste-1.0.15//:build_script_build\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_root = \"src/lib.rs\",\n    edition = \"2018\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=paste\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"1.0.15\",\n)\n\ncargo_build_script(\n    name = \"_bs\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \"**/*.rs\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_name = \"build_script_build\",\n    crate_root = \"build.rs\",\n    data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    edition = \"2018\",\n    pkg_name = \"paste\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=paste\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    version = \"1.0.15\",\n    visibility = [\"//visibility:private\"],\n)\n\nalias(\n    name = \"build_script_build\",\n    actual = \":_bs\",\n    tags = [\"manual\"],\n)\n"
+            }
+          },
+          "crates__proc-macro2-1.0.106": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/proc-macro2/1.0.106/download"
+              ],
+              "strip_prefix": "proc-macro2-1.0.106",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\n    \"@rules_rust//cargo:defs.bzl\",\n    \"cargo_build_script\",\n    \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"proc_macro2\",\n    deps = [\n        \"@crates__proc-macro2-1.0.106//:build_script_build\",\n        \"@crates__unicode-ident-1.0.24//:unicode_ident\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"proc-macro\",\n    ] + select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [\n            \"default\",  # aarch64-apple-darwin\n        ],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [\n            \"default\",  # aarch64-unknown-linux-gnu\n        ],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n            \"default\",  # x86_64-pc-windows-msvc\n        ],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [\n            \"default\",  # x86_64-unknown-linux-gnu\n        ],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [\n            \"default\",  # x86_64-unknown-nixos-gnu\n        ],\n        \"//conditions:default\": [],\n    }),\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=proc-macro2\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"1.0.106\",\n)\n\ncargo_build_script(\n    name = \"_bs\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \"**/*.rs\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"proc-macro\",\n    ] + select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [\n            \"default\",  # aarch64-apple-darwin\n        ],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [\n            \"default\",  # aarch64-unknown-linux-gnu\n        ],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n            \"default\",  # x86_64-pc-windows-msvc\n        ],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [\n            \"default\",  # x86_64-unknown-linux-gnu\n        ],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [\n            \"default\",  # x86_64-unknown-nixos-gnu\n        ],\n        \"//conditions:default\": [],\n    }),\n    crate_name = \"build_script_build\",\n    crate_root = \"build.rs\",\n    data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    edition = \"2021\",\n    pkg_name = \"proc-macro2\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=proc-macro2\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    version = \"1.0.106\",\n    visibility = [\"//visibility:private\"],\n)\n\nalias(\n    name = \"build_script_build\",\n    actual = \":_bs\",\n    tags = [\"manual\"],\n)\n"
+            }
+          },
+          "crates__quote-1.0.45": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/quote/1.0.45/download"
+              ],
+              "strip_prefix": "quote-1.0.45",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\n    \"@rules_rust//cargo:defs.bzl\",\n    \"cargo_build_script\",\n    \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"quote\",\n    deps = [\n        \"@crates__proc-macro2-1.0.106//:proc_macro2\",\n        \"@crates__quote-1.0.45//:build_script_build\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"default\",\n        \"proc-macro\",\n    ],\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=quote\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"1.0.45\",\n)\n\ncargo_build_script(\n    name = \"_bs\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \"**/*.rs\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"default\",\n        \"proc-macro\",\n    ],\n    crate_name = \"build_script_build\",\n    crate_root = \"build.rs\",\n    data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    edition = \"2021\",\n    pkg_name = \"quote\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=quote\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    version = \"1.0.45\",\n    visibility = [\"//visibility:private\"],\n)\n\nalias(\n    name = \"build_script_build\",\n    actual = \":_bs\",\n    tags = [\"manual\"],\n)\n"
+            }
+          },
+          "crates__regex-1.12.3": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/regex/1.12.3/download"
+              ],
+              "strip_prefix": "regex-1.12.3",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"regex\",\n    deps = [\n        \"@crates__aho-corasick-1.1.4//:aho_corasick\",\n        \"@crates__memchr-2.8.1//:memchr\",\n        \"@crates__regex-automata-0.4.14//:regex_automata\",\n        \"@crates__regex-syntax-0.8.10//:regex_syntax\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"default\",\n        \"perf\",\n        \"perf-backtrack\",\n        \"perf-cache\",\n        \"perf-dfa\",\n        \"perf-inline\",\n        \"perf-literal\",\n        \"perf-onepass\",\n        \"std\",\n        \"unicode\",\n        \"unicode-age\",\n        \"unicode-bool\",\n        \"unicode-case\",\n        \"unicode-gencat\",\n        \"unicode-perl\",\n        \"unicode-script\",\n        \"unicode-segment\",\n    ],\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=regex\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"1.12.3\",\n)\n"
+            }
+          },
+          "crates__regex-automata-0.4.14": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/regex-automata/0.4.14/download"
+              ],
+              "strip_prefix": "regex-automata-0.4.14",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"regex_automata\",\n    deps = [\n        \"@crates__aho-corasick-1.1.4//:aho_corasick\",\n        \"@crates__memchr-2.8.1//:memchr\",\n        \"@crates__regex-syntax-0.8.10//:regex_syntax\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"alloc\",\n        \"dfa-onepass\",\n        \"hybrid\",\n        \"meta\",\n        \"nfa-backtrack\",\n        \"nfa-pikevm\",\n        \"nfa-thompson\",\n        \"perf-inline\",\n        \"perf-literal\",\n        \"perf-literal-multisubstring\",\n        \"perf-literal-substring\",\n        \"std\",\n        \"syntax\",\n        \"unicode\",\n        \"unicode-age\",\n        \"unicode-bool\",\n        \"unicode-case\",\n        \"unicode-gencat\",\n        \"unicode-perl\",\n        \"unicode-script\",\n        \"unicode-segment\",\n        \"unicode-word-boundary\",\n    ],\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=regex-automata\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"0.4.14\",\n)\n"
+            }
+          },
+          "crates__regex-syntax-0.8.10": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/regex-syntax/0.8.10/download"
+              ],
+              "strip_prefix": "regex-syntax-0.8.10",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"regex_syntax\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"default\",\n        \"std\",\n        \"unicode\",\n        \"unicode-age\",\n        \"unicode-bool\",\n        \"unicode-case\",\n        \"unicode-gencat\",\n        \"unicode-perl\",\n        \"unicode-script\",\n        \"unicode-segment\",\n    ],\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=regex-syntax\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"0.8.10\",\n)\n"
+            }
+          },
+          "crates__rustversion-1.0.22": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/rustversion/1.0.22/download"
+              ],
+              "strip_prefix": "rustversion-1.0.22",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\n    \"@rules_rust//cargo:defs.bzl\",\n    \"cargo_build_script\",\n    \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_proc_macro\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_proc_macro(\n    name = \"rustversion\",\n    deps = [\n        \"@crates__rustversion-1.0.22//:build_script_build\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_root = \"src/lib.rs\",\n    edition = \"2018\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=rustversion\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"1.0.22\",\n)\n\ncargo_build_script(\n    name = \"_bs\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \"**/*.rs\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_name = \"build_script_build\",\n    crate_root = \"build/build.rs\",\n    data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    edition = \"2018\",\n    pkg_name = \"rustversion\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=rustversion\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    version = \"1.0.22\",\n    visibility = [\"//visibility:private\"],\n)\n\nalias(\n    name = \"build_script_build\",\n    actual = \":_bs\",\n    tags = [\"manual\"],\n)\n"
+            }
+          },
+          "crates__syn-2.0.117": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/syn/2.0.117/download"
+              ],
+              "strip_prefix": "syn-2.0.117",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"syn\",\n    deps = [\n        \"@crates__proc-macro2-1.0.106//:proc_macro2\",\n        \"@crates__quote-1.0.45//:quote\",\n        \"@crates__unicode-ident-1.0.24//:unicode_ident\",\n    ],\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_features = [\n        \"clone-impls\",\n        \"default\",\n        \"derive\",\n        \"parsing\",\n        \"printing\",\n        \"proc-macro\",\n    ] + select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [\n            \"extra-traits\",  # aarch64-apple-darwin\n            \"full\",  # aarch64-apple-darwin\n        ],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [\n            \"extra-traits\",  # aarch64-unknown-linux-gnu\n            \"full\",  # aarch64-unknown-linux-gnu\n        ],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n            \"extra-traits\",  # x86_64-pc-windows-msvc\n            \"full\",  # x86_64-pc-windows-msvc\n        ],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [\n            \"extra-traits\",  # x86_64-unknown-linux-gnu\n            \"full\",  # x86_64-unknown-linux-gnu\n        ],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [\n            \"extra-traits\",  # x86_64-unknown-nixos-gnu\n            \"full\",  # x86_64-unknown-nixos-gnu\n        ],\n        \"//conditions:default\": [],\n    }),\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=syn\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"2.0.117\",\n)\n"
+            }
+          },
+          "crates__unicode-ident-1.0.24": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "remote_patch_strip": 1,
+              "sha256": "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75",
+              "type": "tar.gz",
+              "urls": [
+                "https://static.crates.io/crates/unicode-ident/1.0.24/download"
+              ],
+              "strip_prefix": "unicode-ident-1.0.24",
+              "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n#     bazel mod show_repo 'protobuf'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n    name = \"cargo_toml_env_vars\",\n    src = \"Cargo.toml\",\n)\n\nrust_library(\n    name = \"unicode_ident\",\n    compile_data = glob(\n        allow_empty = True,\n        include = [\"**\"],\n        exclude = [\n            \"**/* *\",\n            \".tmp_git_root/**/*\",\n            \"BUILD\",\n            \"BUILD.bazel\",\n            \"WORKSPACE\",\n            \"WORKSPACE.bazel\",\n        ],\n    ),\n    crate_root = \"src/lib.rs\",\n    edition = \"2021\",\n    rustc_env_files = [\n        \":cargo_toml_env_vars\",\n    ],\n    rustc_flags = [\n        \"--cap-lints=allow\",\n    ],\n    srcs = glob(\n        allow_empty = True,\n        include = [\"**/*.rs\"],\n    ),\n    tags = [\n        \"cargo-bazel\",\n        \"crate-name=unicode-ident\",\n        \"manual\",\n        \"noclippy\",\n        \"norustfmt\",\n    ],\n    target_compatible_with = select({\n        \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n        \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n        \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n        \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n        \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n        \"//conditions:default\": [\"@platforms//:incompatible\"],\n    }),\n    version = \"1.0.24\",\n)\n"
+            }
+          }
+        }
+      }
+    },
+    "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": {
+      "general": {
+        "bzlTransitiveDigest": "2RrHa9GqLSgJmFPwKD1wONow/2x/PGTGakiYDTBQJ88=",
+        "usagesDigest": "tG3p3Nb5XxC7vWY/bcKdb//g0HoAxpxxH3F5/jBVlk4=",
+        "recordedInputs": [
+          "REPO_MAPPING:bazel_features+,bazel_features_globals bazel_features++version_extension+bazel_features_globals",
+          "REPO_MAPPING:bazel_features+,bazel_features_version bazel_features++version_extension+bazel_features_version",
+          "REPO_MAPPING:rules_cc+,bazel_skylib bazel_skylib+",
+          "REPO_MAPPING:rules_cc+,bazel_tools bazel_tools",
+          "REPO_MAPPING:rules_cc+,cc_compatibility_proxy rules_cc++compatibility_proxy+cc_compatibility_proxy",
+          "REPO_MAPPING:rules_cc+,platforms platforms",
+          "REPO_MAPPING:rules_cc+,rules_cc rules_cc+",
+          "REPO_MAPPING:rules_cc++compatibility_proxy+cc_compatibility_proxy,rules_cc rules_cc+",
+          "REPO_MAPPING:rules_rust+,bazel_features bazel_features+",
+          "REPO_MAPPING:rules_rust+,bazel_skylib bazel_skylib+",
+          "REPO_MAPPING:rules_rust+,bazel_tools bazel_tools",
+          "REPO_MAPPING:rules_rust+,cui rules_rust++cu+cui",
+          "REPO_MAPPING:rules_rust+,rrc rules_rust++i2+rrc",
+          "REPO_MAPPING:rules_rust+,rules_cc rules_cc+",
+          "REPO_MAPPING:rules_rust+,rules_rust rules_rust+"
+        ],
+        "generatedRepoSpecs": {
+          "cargo_bazel_bootstrap": {
+            "repoRuleId": "@@rules_rust+//cargo/private:cargo_bootstrap.bzl%cargo_bootstrap_repository",
+            "attributes": {
+              "srcs": [
+                "@@rules_rust+//crate_universe:src/api.rs",
+                "@@rules_rust+//crate_universe:src/api/lockfile.rs",
+                "@@rules_rust+//crate_universe:src/cli.rs",
+                "@@rules_rust+//crate_universe:src/cli/generate.rs",
+                "@@rules_rust+//crate_universe:src/cli/query.rs",
+                "@@rules_rust+//crate_universe:src/cli/render.rs",
+                "@@rules_rust+//crate_universe:src/cli/splice.rs",
+                "@@rules_rust+//crate_universe:src/cli/vendor.rs",
+                "@@rules_rust+//crate_universe:src/config.rs",
+                "@@rules_rust+//crate_universe:src/context.rs",
+                "@@rules_rust+//crate_universe:src/context/crate_context.rs",
+                "@@rules_rust+//crate_universe:src/context/platforms.rs",
+                "@@rules_rust+//crate_universe:src/lib.rs",
+                "@@rules_rust+//crate_universe:src/lockfile.rs",
+                "@@rules_rust+//crate_universe:src/main.rs",
+                "@@rules_rust+//crate_universe:src/metadata.rs",
+                "@@rules_rust+//crate_universe:src/metadata/cargo_bin.rs",
+                "@@rules_rust+//crate_universe:src/metadata/cargo_tree_resolver.rs",
+                "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.bat",
+                "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.sh",
+                "@@rules_rust+//crate_universe:src/metadata/dependency.rs",
+                "@@rules_rust+//crate_universe:src/metadata/metadata_annotation.rs",
+                "@@rules_rust+//crate_universe:src/rendering.rs",
+                "@@rules_rust+//crate_universe:src/rendering/template_engine.rs",
+                "@@rules_rust+//crate_universe:src/rendering/templates/module_bzl.j2",
+                "@@rules_rust+//crate_universe:src/rendering/templates/partials/header.j2",
+                "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/aliases_map.j2",
+                "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/deps_map.j2",
+                "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_git.j2",
+                "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_http.j2",
+                "@@rules_rust+//crate_universe:src/rendering/templates/vendor_module.j2",
+                "@@rules_rust+//crate_universe:src/rendering/verbatim/alias_rules.bzl",
+                "@@rules_rust+//crate_universe:src/select.rs",
+                "@@rules_rust+//crate_universe:src/splicing.rs",
+                "@@rules_rust+//crate_universe:src/splicing/cargo_config.rs",
+                "@@rules_rust+//crate_universe:src/splicing/crate_index_lookup.rs",
+                "@@rules_rust+//crate_universe:src/splicing/splicer.rs",
+                "@@rules_rust+//crate_universe:src/test.rs",
+                "@@rules_rust+//crate_universe:src/utils.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark/glob.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark/label.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark/select.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark/select_dict.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark/select_list.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark/select_scalar.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark/select_set.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark/serialize.rs",
+                "@@rules_rust+//crate_universe:src/utils/starlark/target_compatible_with.rs",
+                "@@rules_rust+//crate_universe:src/utils/symlink.rs",
+                "@@rules_rust+//crate_universe:src/utils/target_triple.rs"
+              ],
+              "binary": "cargo-bazel",
+              "cargo_lockfile": "@@rules_rust+//crate_universe:Cargo.lock",
+              "cargo_toml": "@@rules_rust+//crate_universe:Cargo.toml",
+              "version": "1.93.1",
+              "timeout": 900,
+              "rust_toolchain_cargo_template": "@rust_host_tools//:bin/{tool}",
+              "rust_toolchain_rustc_template": "@rust_host_tools//:bin/{tool}",
+              "compressed_windows_toolchain_names": false
+            }
+          }
         },
-        "recordedRepoMappingEntries": [
-          [
-            "rules_python+",
-            "bazel_tools",
-            "bazel_tools"
+        "moduleExtensionMetadata": {
+          "explicitRootModuleDirectDeps": [
+            "cargo_bazel_bootstrap"
           ],
-          [
-            "rules_python+",
-            "platforms",
-            "platforms"
-          ]
-        ]
+          "explicitRootModuleDirectDevDeps": [],
+          "useAllRepos": "NO",
+          "reproducible": false
+        }
       }
     },
     "@@yq.bzl+//yq:extensions.bzl%yq": {
       "general": {
-        "bzlTransitiveDigest": "61Uz+o5PnlY0jJfPZEUNqsKxnM/UCLeWsn5VVCc8u5Y=",
-        "usagesDigest": "d6LYc0jBWHeivnXQugRBrSwkM83GSDwge0l8buSqero=",
-        "recordedFileInputs": {},
-        "recordedDirentsInputs": {},
-        "envVariables": {},
+        "bzlTransitiveDigest": "tDqk+ntWTdxNAWPDjRY1uITgHbti2jcXR5ZdinltBs0=",
+        "usagesDigest": "Pz33iK5QtkPFivaljfReN4KzpAqM2DgY4vwgyZSNoAM=",
+        "recordedInputs": [],
         "generatedRepoSpecs": {
           "yq_darwin_amd64": {
             "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo",
             "attributes": {
               "platform": "darwin_amd64",
-              "version": "4.45.1"
+              "version": "4.45.2"
             }
           },
           "yq_darwin_arm64": {
             "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo",
             "attributes": {
               "platform": "darwin_arm64",
-              "version": "4.45.1"
+              "version": "4.45.2"
             }
           },
           "yq_linux_amd64": {
             "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo",
             "attributes": {
               "platform": "linux_amd64",
-              "version": "4.45.1"
+              "version": "4.45.2"
             }
           },
           "yq_linux_arm64": {
             "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo",
             "attributes": {
               "platform": "linux_arm64",
-              "version": "4.45.1"
+              "version": "4.45.2"
             }
           },
           "yq_linux_s390x": {
             "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo",
             "attributes": {
               "platform": "linux_s390x",
-              "version": "4.45.1"
+              "version": "4.45.2"
             }
           },
           "yq_linux_riscv64": {
             "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo",
             "attributes": {
               "platform": "linux_riscv64",
-              "version": "4.45.1"
+              "version": "4.45.2"
             }
           },
           "yq_linux_ppc64le": {
             "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo",
             "attributes": {
               "platform": "linux_ppc64le",
-              "version": "4.45.1"
+              "version": "4.45.2"
             }
           },
           "yq_windows_amd64": {
             "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo",
             "attributes": {
               "platform": "windows_amd64",
-              "version": "4.45.1"
+              "version": "4.45.2"
+            }
+          },
+          "yq_windows_arm64": {
+            "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo",
+            "attributes": {
+              "platform": "windows_arm64",
+              "version": "4.45.2"
             }
           },
           "yq_toolchains": {
@@ -1247,10 +1334,727 @@
               "user_repository_name": "yq"
             }
           }
-        },
-        "recordedRepoMappingEntries": []
+        }
       }
     }
   },
-  "facts": {}
+  "facts": {
+    "@@rules_go+//go:extensions.bzl%go_sdk": {
+      "1.22.4": {
+        "aix_ppc64": [
+          "go1.22.4.aix-ppc64.tar.gz",
+          "b9647fa9fc83a0cc5d4f092a19eaeaecf45f063a5aa7d4962fde65aeb7ae6ce1"
+        ],
+        "darwin_amd64": [
+          "go1.22.4.darwin-amd64.tar.gz",
+          "c95967f50aa4ace34af0c236cbdb49a9a3e80ee2ad09d85775cb4462a5c19ed3"
+        ],
+        "darwin_arm64": [
+          "go1.22.4.darwin-arm64.tar.gz",
+          "242b78dc4c8f3d5435d28a0d2cec9b4c1aa999b601fb8aa59fb4e5a1364bf827"
+        ],
+        "dragonfly_amd64": [
+          "go1.22.4.dragonfly-amd64.tar.gz",
+          "f2fbb51af4719d3616efb482d6ed2b96579b474156f85a7ddc6f126764feec4b"
+        ],
+        "freebsd_386": [
+          "go1.22.4.freebsd-386.tar.gz",
+          "7c54884bb9f274884651d41e61d1bc12738863ad1497e97ea19ad0e9aa6bf7b5"
+        ],
+        "freebsd_amd64": [
+          "go1.22.4.freebsd-amd64.tar.gz",
+          "88d44500e1701dd35797619774d6dd51bf60f45a8338b0a82ddc018e4e63fb78"
+        ],
+        "freebsd_arm64": [
+          "go1.22.4.freebsd-arm64.tar.gz",
+          "726dc093cf020277be45debf03c3b02b43c2efb3e2a5d4fba8f52579d65327dc"
+        ],
+        "freebsd_armv6l": [
+          "go1.22.4.freebsd-arm.tar.gz",
+          "3d9efe47db142a22679aba46b1772e3900b0d87ae13bd2b3bc80dbf2ac0b2cd6"
+        ],
+        "freebsd_riscv64": [
+          "go1.22.4.freebsd-riscv64.tar.gz",
+          "5f6b67e5e32f1d6ccb2d4dcb44934a5e2e870a877ba7443d86ec43cfc28afa71"
+        ],
+        "illumos_amd64": [
+          "go1.22.4.illumos-amd64.tar.gz",
+          "d56ecc2f85b6418a21ef83879594d0c42ab4f65391a676bb12254870e6690d63"
+        ],
+        "linux_386": [
+          "go1.22.4.linux-386.tar.gz",
+          "47a2a8d249a91eb8605c33bceec63aedda0441a43eac47b4721e3975ff916cec"
+        ],
+        "linux_amd64": [
+          "go1.22.4.linux-amd64.tar.gz",
+          "ba79d4526102575196273416239cca418a651e049c2b099f3159db85e7bade7d"
+        ],
+        "linux_arm64": [
+          "go1.22.4.linux-arm64.tar.gz",
+          "a8e177c354d2e4a1b61020aca3562e27ea3e8f8247eca3170e3fa1e0c2f9e771"
+        ],
+        "linux_armv6l": [
+          "go1.22.4.linux-armv6l.tar.gz",
+          "e2b143fbacbc9cbd448e9ef41ac3981f0488ce849af1cf37e2341d09670661de"
+        ],
+        "linux_loong64": [
+          "go1.22.4.linux-loong64.tar.gz",
+          "e2ff9436e4b34bf6926b06d97916e26d67a909a2effec17967245900f0816f1d"
+        ],
+        "linux_mips": [
+          "go1.22.4.linux-mips.tar.gz",
+          "73f0dcc60458c4770593b05a7bc01cc0d31fc98f948c0c2334812c7a1f2fc3f1"
+        ],
+        "linux_mips64": [
+          "go1.22.4.linux-mips64.tar.gz",
+          "417af97fc2630a647052375768be4c38adcc5af946352ea5b28613ea81ca5d45"
+        ],
+        "linux_mips64le": [
+          "go1.22.4.linux-mips64le.tar.gz",
+          "7486e2d7dd8c98eb44df815ace35a7fe7f30b7c02326e3741bd934077508139b"
+        ],
+        "linux_mipsle": [
+          "go1.22.4.linux-mipsle.tar.gz",
+          "69479c8aad301e459a8365b40cad1074a0dbba5defb9291669f94809c4c4be6e"
+        ],
+        "linux_ppc64": [
+          "go1.22.4.linux-ppc64.tar.gz",
+          "dd238847e65bc3e2745caca475a5db6522a2fcf85cf6c38fc36a06642b19efd7"
+        ],
+        "linux_ppc64le": [
+          "go1.22.4.linux-ppc64le.tar.gz",
+          "a3e5834657ef92523f570f798fed42f1f87bc18222a16815ec76b84169649ec4"
+        ],
+        "linux_riscv64": [
+          "go1.22.4.linux-riscv64.tar.gz",
+          "56a827ff7dc6245bcd7a1e9288dffaa1d8b0fd7468562264c1523daf3b4f1b4a"
+        ],
+        "linux_s390x": [
+          "go1.22.4.linux-s390x.tar.gz",
+          "7590c3e278e2dc6040aae0a39da3ca1eb2e3921673a7304cc34d588c45889eec"
+        ],
+        "netbsd_386": [
+          "go1.22.4.netbsd-386.tar.gz",
+          "ddd2eebe34471a2502de6c5dad04ab27c9fc80cbde7a9ad5b3c66ecec4504e1d"
+        ],
+        "netbsd_amd64": [
+          "go1.22.4.netbsd-amd64.tar.gz",
+          "33af79f6f935f6fbacc5d23876450b3567b79348fc065beef8e64081127dd234"
+        ],
+        "netbsd_arm64": [
+          "go1.22.4.netbsd-arm64.tar.gz",
+          "c9a2971dec9f6d320c6f2b049b2353c6d0a2d35e87b8a4b2d78a2f0d62545f8e"
+        ],
+        "netbsd_armv6l": [
+          "go1.22.4.netbsd-arm.tar.gz",
+          "fa3550ebd5375a70b3bcd342b5a71f4bd271dcbbfaf4eabefa2144ab5d8924b6"
+        ],
+        "openbsd_386": [
+          "go1.22.4.openbsd-386.tar.gz",
+          "d21af022331bfdc2b5b161d616c3a1a4573d33cf7a30416ee509a8f3641deb47"
+        ],
+        "openbsd_amd64": [
+          "go1.22.4.openbsd-amd64.tar.gz",
+          "72c0094c43f7e5722ec49c2a3e9dfa7a1123ac43a5f3a63eecf3e3795d3ff0ae"
+        ],
+        "openbsd_arm64": [
+          "go1.22.4.openbsd-arm64.tar.gz",
+          "a7ab8d4e0b02bf06ed144ba42c61c0e93ee00f2b433415dfd4ad4b6e79f31650"
+        ],
+        "openbsd_armv6l": [
+          "go1.22.4.openbsd-arm.tar.gz",
+          "1096831ea3c5ea3ca57d14251d9eda3786889531eb40d7d6775dcaa324d4b065"
+        ],
+        "openbsd_ppc64": [
+          "go1.22.4.openbsd-ppc64.tar.gz",
+          "9716327c8a628358798898dc5148c49dbbeb5196bf2cbf088e550721a6e4f60b"
+        ],
+        "plan9_386": [
+          "go1.22.4.plan9-386.tar.gz",
+          "a8dd4503c95c32a502a616ab78870a19889c9325fe9bd31eb16dd69346e4bfa8"
+        ],
+        "plan9_amd64": [
+          "go1.22.4.plan9-amd64.tar.gz",
+          "5423a25808d76fe5aca8607a2e5ac5673abf45446b168cb5e9d8519ee9fe39a1"
+        ],
+        "plan9_armv6l": [
+          "go1.22.4.plan9-arm.tar.gz",
+          "6af939ad583f5c85c09c53728ab7d38c3cc2b39167562d6c18a07c5c6608b370"
+        ],
+        "solaris_amd64": [
+          "go1.22.4.solaris-amd64.tar.gz",
+          "e8cabe69c03085725afdb32a6f9998191a3e55a747b270d835fd05000d56abba"
+        ],
+        "windows_386": [
+          "go1.22.4.windows-386.zip",
+          "aca4e2c37278a10f1c70dd0df142f7d66b50334fcee48978d409202d308d6d25"
+        ],
+        "windows_amd64": [
+          "go1.22.4.windows-amd64.zip",
+          "26321c4d945a0035d8a5bc4a1965b0df401ff8ceac66ce2daadabf9030419a98"
+        ],
+        "windows_arm64": [
+          "go1.22.4.windows-arm64.zip",
+          "8a2daa9ea28cbdafddc6171aefed384f4e5b6e714fb52116fe9ed25a132f37ed"
+        ],
+        "windows_armv6l": [
+          "go1.22.4.windows-arm.zip",
+          "5fcd0671a49cecf39b41021621ee1b6e7aa1370f37122b72e80d4fd4185833b6"
+        ]
+      },
+      "1.25.0": {
+        "aix_ppc64": [
+          "go1.25.0.aix-ppc64.tar.gz",
+          "e5234a7dac67bc86c528fe9752fc9d63557918627707a733ab4cac1a6faed2d4"
+        ],
+        "darwin_amd64": [
+          "go1.25.0.darwin-amd64.tar.gz",
+          "5bd60e823037062c2307c71e8111809865116714d6f6b410597cf5075dfd80ef"
+        ],
+        "darwin_arm64": [
+          "go1.25.0.darwin-arm64.tar.gz",
+          "544932844156d8172f7a28f77f2ac9c15a23046698b6243f633b0a0b00c0749c"
+        ],
+        "dragonfly_amd64": [
+          "go1.25.0.dragonfly-amd64.tar.gz",
+          "5ed3cf9a810a1483822538674f1336c06b51aa1b94d6d545a1a0319a48177120"
+        ],
+        "freebsd_386": [
+          "go1.25.0.freebsd-386.tar.gz",
+          "abea5d5c6697e6b5c224731f2158fe87c602996a2a233ac0c4730cd57bf8374e"
+        ],
+        "freebsd_amd64": [
+          "go1.25.0.freebsd-amd64.tar.gz",
+          "86e6fe0a29698d7601c4442052dac48bd58d532c51cccb8f1917df648138730b"
+        ],
+        "freebsd_arm": [
+          "go1.25.0.freebsd-arm.tar.gz",
+          "d90b78e41921f72f30e8bbc81d9dec2cff7ff384a33d8d8debb24053e4336bfe"
+        ],
+        "freebsd_arm64": [
+          "go1.25.0.freebsd-arm64.tar.gz",
+          "451d0da1affd886bfb291b7c63a6018527b269505db21ce6e14724f22ab0662e"
+        ],
+        "freebsd_riscv64": [
+          "go1.25.0.freebsd-riscv64.tar.gz",
+          "7b565f76bd8bda46549eeaaefe0e53b251e644c230577290c0f66b1ecdb3cdbe"
+        ],
+        "illumos_amd64": [
+          "go1.25.0.illumos-amd64.tar.gz",
+          "b1e1fdaab1ad25aa1c08d7a36c97d45d74b98b89c3f78c6d2145f77face54a2c"
+        ],
+        "linux_386": [
+          "go1.25.0.linux-386.tar.gz",
+          "8c602dd9d99bc9453b3995d20ce4baf382cc50855900a0ece5de9929df4a993a"
+        ],
+        "linux_amd64": [
+          "go1.25.0.linux-amd64.tar.gz",
+          "2852af0cb20a13139b3448992e69b868e50ed0f8a1e5940ee1de9e19a123b613"
+        ],
+        "linux_arm64": [
+          "go1.25.0.linux-arm64.tar.gz",
+          "05de75d6994a2783699815ee553bd5a9327d8b79991de36e38b66862782f54ae"
+        ],
+        "linux_armv6l": [
+          "go1.25.0.linux-armv6l.tar.gz",
+          "a5a8f8198fcf00e1e485b8ecef9ee020778bf32a408a4e8873371bfce458cd09"
+        ],
+        "linux_loong64": [
+          "go1.25.0.linux-loong64.tar.gz",
+          "cab86b1cf761b1cb3bac86a8877cfc92e7b036fc0d3084123d77013d61432afc"
+        ],
+        "linux_mips": [
+          "go1.25.0.linux-mips.tar.gz",
+          "d66b6fb74c3d91b9829dc95ec10ca1f047ef5e89332152f92e136cf0e2da5be1"
+        ],
+        "linux_mips64": [
+          "go1.25.0.linux-mips64.tar.gz",
+          "4082e4381a8661bc2a839ff94ba3daf4f6cde20f8fb771b5b3d4762dc84198a2"
+        ],
+        "linux_mips64le": [
+          "go1.25.0.linux-mips64le.tar.gz",
+          "70002c299ec7f7175ac2ef673b1b347eecfa54ae11f34416a6053c17f855afcc"
+        ],
+        "linux_mipsle": [
+          "go1.25.0.linux-mipsle.tar.gz",
+          "b00a3a39eff099f6df9f1c7355bf28e4589d0586f42d7d4a394efb763d145a73"
+        ],
+        "linux_ppc64": [
+          "go1.25.0.linux-ppc64.tar.gz",
+          "df166f33bd98160662560a72ff0b4ba731f969a80f088922bddcf566a88c1ec1"
+        ],
+        "linux_ppc64le": [
+          "go1.25.0.linux-ppc64le.tar.gz",
+          "0f18a89e7576cf2c5fa0b487a1635d9bcbf843df5f110e9982c64df52a983ad0"
+        ],
+        "linux_riscv64": [
+          "go1.25.0.linux-riscv64.tar.gz",
+          "c018ff74a2c48d55c8ca9b07c8e24163558ffec8bea08b326d6336905d956b67"
+        ],
+        "linux_s390x": [
+          "go1.25.0.linux-s390x.tar.gz",
+          "34e5a2e19f2292fbaf8783e3a241e6e49689276aef6510a8060ea5ef54eee408"
+        ],
+        "netbsd_386": [
+          "go1.25.0.netbsd-386.tar.gz",
+          "f8586cdb7aa855657609a5c5f6dbf523efa00c2bbd7c76d3936bec80aa6c0aba"
+        ],
+        "netbsd_amd64": [
+          "go1.25.0.netbsd-amd64.tar.gz",
+          "ae8dc1469385b86a157a423bb56304ba45730de8a897615874f57dd096db2c2a"
+        ],
+        "netbsd_arm": [
+          "go1.25.0.netbsd-arm.tar.gz",
+          "1ff7e4cc764425fc9dd6825eaee79d02b3c7cafffbb3691687c8d672ade76cb7"
+        ],
+        "netbsd_arm64": [
+          "go1.25.0.netbsd-arm64.tar.gz",
+          "e1b310739f26724216aa6d7d7208c4031f9ff54c9b5b9a796ddc8bebcb4a5f16"
+        ],
+        "openbsd_386": [
+          "go1.25.0.openbsd-386.tar.gz",
+          "4802a9b20e533da91adb84aab42e94aa56cfe3e5475d0550bed3385b182e69d8"
+        ],
+        "openbsd_amd64": [
+          "go1.25.0.openbsd-amd64.tar.gz",
+          "c016cd984bebe317b19a4f297c4f50def120dc9788490540c89f28e42f1dabe1"
+        ],
+        "openbsd_arm": [
+          "go1.25.0.openbsd-arm.tar.gz",
+          "a1e31d0bf22172ddde42edf5ec811ef81be43433df0948ece52fecb247ccfd8d"
+        ],
+        "openbsd_arm64": [
+          "go1.25.0.openbsd-arm64.tar.gz",
+          "343ea8edd8c218196e15a859c6072d0dd3246fbbb168481ab665eb4c4140458d"
+        ],
+        "openbsd_ppc64": [
+          "go1.25.0.openbsd-ppc64.tar.gz",
+          "694c14da1bcaeb5e3332d49bdc2b6d155067648f8fe1540c5de8f3cf8e157154"
+        ],
+        "openbsd_riscv64": [
+          "go1.25.0.openbsd-riscv64.tar.gz",
+          "aa510ad25cf54c06cd9c70b6d80ded69cb20188ac6e1735655eef29ff7e7885f"
+        ],
+        "plan9_386": [
+          "go1.25.0.plan9-386.tar.gz",
+          "46f8cef02086cf04bf186c5912776b56535178d4cb319cd19c9fdbdd29231986"
+        ],
+        "plan9_amd64": [
+          "go1.25.0.plan9-amd64.tar.gz",
+          "29b34391d84095e44608a228f63f2f88113a37b74a79781353ec043dfbcb427b"
+        ],
+        "plan9_arm": [
+          "go1.25.0.plan9-arm.tar.gz",
+          "0a047107d13ebe7943aaa6d54b1d7bbd2e45e68ce449b52915a818da715799c2"
+        ],
+        "solaris_amd64": [
+          "go1.25.0.solaris-amd64.tar.gz",
+          "9977f9e4351984364a3b2b78f8b88bfd1d339812356d5237678514594b7d3611"
+        ],
+        "windows_386": [
+          "go1.25.0.windows-386.zip",
+          "df9f39db82a803af0db639e3613a36681ab7a42866b1384b3f3a1045663961a7"
+        ],
+        "windows_amd64": [
+          "go1.25.0.windows-amd64.zip",
+          "89efb4f9b30812eee083cc1770fdd2913c14d301064f6454851428f9707d190b"
+        ],
+        "windows_arm64": [
+          "go1.25.0.windows-arm64.zip",
+          "27bab004c72b3d7bd05a69b6ec0fc54a309b4b78cc569dd963d8b3ec28bfdb8c"
+        ]
+      }
+    },
+    "@@rules_python+//python/extensions:pip.bzl%pip": {
+      "dist_hashes": {
+        "https://pypi.org/simple": {
+          "backports-tarfile": {
+            "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz": "d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991",
+            "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl": "77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"
+          },
+          "certifi": {
+            "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz": "47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43",
+            "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl": "0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"
+          },
+          "cffi": {
+            "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0",
+            "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl": "3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94",
+            "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl": "5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743",
+            "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl": "b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe",
+            "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165",
+            "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63",
+            "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl": "cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495",
+            "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl": "7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512",
+            "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl": "737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205",
+            "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl": "c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5",
+            "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl": "89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322",
+            "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl": "7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d",
+            "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl": "cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f",
+            "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl": "b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739",
+            "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl": "6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b",
+            "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl": "19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75",
+            "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl": "81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037",
+            "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl": "de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7",
+            "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl": "9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9",
+            "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl": "087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f",
+            "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl": "a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9",
+            "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775",
+            "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl": "8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb",
+            "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl": "45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca",
+            "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl": "00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb",
+            "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb",
+            "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl": "2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c",
+            "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c",
+            "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl": "d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c",
+            "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl": "b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9",
+            "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl": "c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13",
+            "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl": "e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5",
+            "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba",
+            "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl": "da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18",
+            "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl": "9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664",
+            "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl": "fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5",
+            "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl": "0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44",
+            "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl": "4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6",
+            "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl": "c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d",
+            "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453",
+            "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26",
+            "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c",
+            "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl": "f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49",
+            "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl": "dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad",
+            "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl": "9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4",
+            "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl": "1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e",
+            "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534",
+            "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b",
+            "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl": "2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a",
+            "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl": "94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414",
+            "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl": "0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9",
+            "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl": "66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5",
+            "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b",
+            "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92",
+            "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c",
+            "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl": "2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187",
+            "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93",
+            "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl": "203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25",
+            "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl": "d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef",
+            "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c",
+            "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl": "d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6",
+            "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl": "fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf",
+            "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e",
+            "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl": "38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1",
+            "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl": "256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91",
+            "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl": "dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3",
+            "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592",
+            "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062",
+            "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b",
+            "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26",
+            "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl": "b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc",
+            "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5",
+            "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl": "8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c",
+            "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8",
+            "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl": "6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4",
+            "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl": "1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a",
+            "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl": "0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2",
+            "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl": "6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d",
+            "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz": "44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529",
+            "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl": "74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27",
+            "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2",
+            "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl": "1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65",
+            "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl": "da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5",
+            "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"
+          },
+          "charset-normalizer": {
+            "https://files.pythonhosted.org/packages/00/bd/ef9c88464b126fa176f4ef4a317ad9b6f4d30b2cffbc43386062367c3e2c/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9",
+            "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl": "ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049",
+            "https://files.pythonhosted.org/packages/04/9a/914d294daa4809c57667b77470533e65def9c0be1ef8b4c1183a99170e9d/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7",
+            "https://files.pythonhosted.org/packages/05/35/bb59b1cd012d7196fc81c2f5879113971efc226a63812c9cf7f89fe97c40/charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl": "5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40",
+            "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl": "42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491",
+            "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl": "30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92",
+            "https://files.pythonhosted.org/packages/0c/52/8b0c6c3e53f7e546a5e49b9edb876f379725914e1130297f3b423c7b71c5/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b",
+            "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f",
+            "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d",
+            "https://files.pythonhosted.org/packages/1a/79/ae516e678d6e32df2e7e740a7be51dc80b700e2697cb70054a0f1ac2c955/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b",
+            "https://files.pythonhosted.org/packages/20/30/5f64fe3981677fe63fa987b80e6c01042eb5ff653ff7cec1b7bd9268e54e/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl": "2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312",
+            "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe",
+            "https://files.pythonhosted.org/packages/22/82/63a45bfc36f73efe46731a3a71cb84e2112f7e0b049507025ce477f0f052/charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl": "0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c",
+            "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl": "d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f",
+            "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl": "1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae",
+            "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl": "cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93",
+            "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl": "78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a",
+            "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601",
+            "https://files.pythonhosted.org/packages/39/c6/99271dc37243a4f925b09090493fb96c9333d7992c6187f5cfe5312008d2/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e",
+            "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl": "86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc",
+            "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91",
+            "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl": "4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0",
+            "https://files.pythonhosted.org/packages/45/8c/dcef87cfc2b3f002a6478f38906f9040302c68aebe21468090e39cde1445/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl": "88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34",
+            "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl": "939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14",
+            "https://files.pythonhosted.org/packages/50/10/c117806094d2c956ba88958dab680574019abc0c02bcf57b32287afca544/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl": "a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b",
+            "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl": "fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37",
+            "https://files.pythonhosted.org/packages/59/c0/a74f3bd167d311365e7973990243f32c35e7a94e45103125275b9e6c479f/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4",
+            "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9",
+            "https://files.pythonhosted.org/packages/61/c5/dc3ba772489c453621ffc27e8978a98fe7e41a93e787e5e5bde797f1dddb/charset_normalizer-3.4.3-cp38-cp38-win32.whl": "ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557",
+            "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl": "96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30",
+            "https://files.pythonhosted.org/packages/63/86/9cbd533bd37883d467fcd1bd491b3547a3532d0fbb46de2b99feeebf185e/charset_normalizer-3.4.3-cp39-cp39-win32.whl": "16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432",
+            "https://files.pythonhosted.org/packages/64/d1/f9d141c893ef5d4243bc75c130e95af8fd4bc355beff06e9b1e941daad6e/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl": "5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a",
+            "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl": "6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849",
+            "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2",
+            "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl": "14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe",
+            "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl": "18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc",
+            "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8",
+            "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl": "d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16",
+            "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl": "fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9",
+            "https://files.pythonhosted.org/packages/7a/03/cbb6fac9d3e57f7e07ce062712ee80d80a5ab46614684078461917426279/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl": "d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb",
+            "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884",
+            "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl": "53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0",
+            "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f",
+            "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl": "b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b",
+            "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392",
+            "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz": "6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14",
+            "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c",
+            "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31",
+            "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07",
+            "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl": "ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a",
+            "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl": "3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15",
+            "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl": "fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c",
+            "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl": "cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef",
+            "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa",
+            "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl": "6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce",
+            "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl": "02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0",
+            "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl": "027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154",
+            "https://files.pythonhosted.org/packages/b0/a8/6f5bcf1bcf63cb45625f7c5cadca026121ff8a6c8a3256d8d8cd59302663/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7",
+            "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl": "c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce",
+            "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl": "320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f",
+            "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl": "6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927",
+            "https://files.pythonhosted.org/packages/c2/ca/9a0983dd5c8e9733565cf3db4df2b0a2e9a82659fd8aa2a868ac6e4a991f/charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl": "70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05",
+            "https://files.pythonhosted.org/packages/c4/72/d3d0e9592f4e504f9dea08b8db270821c909558c353dc3b457ed2509f2fb/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl": "1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19",
+            "https://files.pythonhosted.org/packages/c5/35/9c99739250742375167bc1b1319cd1cec2bf67438a70d84b2e1ec4c9daa3/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl": "b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942",
+            "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64",
+            "https://files.pythonhosted.org/packages/ce/d6/7e805c8e5c46ff9729c49950acc4ee0aeb55efb8b3a56687658ad10c3216/charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl": "d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca",
+            "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db",
+            "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl": "fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72",
+            "https://files.pythonhosted.org/packages/e1/ef/dd08b2cac9284fd59e70f7d97382c33a3d0a926e45b15fc21b3308324ffd/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl": "511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc",
+            "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl": "c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669",
+            "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f",
+            "https://files.pythonhosted.org/packages/e4/69/132eab043356bba06eb333cc2cc60c6340857d0a2e4ca6dc2b51312886b3/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99",
+            "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl": "e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1",
+            "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl": "73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c",
+            "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl": "c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0",
+            "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096",
+            "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl": "31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c",
+            "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl": "bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5",
+            "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"
+          },
+          "cryptography": {
+            "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl": "2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275",
+            "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl": "760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c",
+            "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl": "02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70",
+            "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl": "8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d",
+            "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl": "7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c",
+            "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl": "3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361",
+            "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl": "69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a",
+            "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl": "aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707",
+            "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a",
+            "https://files.pythonhosted.org/packages/1a/89/843b53614b47f97fe1abc13f9a86efa5ec9e275292c457af1d4a60dc80e0/cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl": "6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e",
+            "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl": "79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f",
+            "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl": "2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c",
+            "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl": "ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4",
+            "https://files.pythonhosted.org/packages/2e/84/7ccff00ced5bac74b775ce0beb7d1be4e8637536b522b5df9b73ada42da2/cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead",
+            "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl": "8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290",
+            "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl": "22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97",
+            "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl": "6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72",
+            "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl": "64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8",
+            "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8",
+            "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl": "d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463",
+            "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl": "b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736",
+            "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30",
+            "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl": "380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f",
+            "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl": "b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410",
+            "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl": "341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58",
+            "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl": "c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013",
+            "https://files.pythonhosted.org/packages/9e/c5/e1594c4eec66a567c3ac4400008108a415808be2ce13dcb9a9045c92f1a0/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl": "90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a",
+            "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz": "27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759",
+            "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b",
+            "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl": "bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2",
+            "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl": "4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b",
+            "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl": "8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca",
+            "https://files.pythonhosted.org/packages/bc/1f/4c926f50df7749f000f20eede0c896769509895e2648db5da0ed55db711d/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl": "a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8",
+            "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl": "6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124",
+            "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl": "12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a",
+            "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl": "7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d",
+            "https://files.pythonhosted.org/packages/c6/65/707be3ffbd5f786028665c3223e86e11c4cda86023adbc56bd72b1b6bab5/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl": "12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0",
+            "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl": "c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4",
+            "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl": "97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed",
+            "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl": "67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175",
+            "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl": "3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507",
+            "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl": "456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa",
+            "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl": "cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19",
+            "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl": "9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb",
+            "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77",
+            "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl": "d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738",
+            "https://files.pythonhosted.org/packages/f3/6d/73557ed0ef7d73d04d9aba745d2c8e95218213687ee5e76b7d236a5030fc/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl": "50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b",
+            "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4",
+            "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl": "063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d"
+          },
+          "docutils": {
+            "https://files.pythonhosted.org/packages/4a/c0/89fe6215b443b919cb98a5002e107cb5026854ed1ccb6b5833e0768419d1/docutils-0.22.2.tar.gz": "9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d",
+            "https://files.pythonhosted.org/packages/66/dd/f95350e853a4468ec37478414fc04ae2d61dad7a947b3015c3dcc51a09b9/docutils-0.22.2-py3-none-any.whl": "b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8"
+          },
+          "idna": {
+            "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3",
+            "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz": "12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"
+          },
+          "importlib-metadata": {
+            "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl": "e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd",
+            "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz": "d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"
+          },
+          "jaraco-classes": {
+            "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz": "47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd",
+            "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl": "f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"
+          },
+          "jaraco-context": {
+            "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz": "9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3",
+            "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl": "f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"
+          },
+          "jaraco-functools": {
+            "https://files.pythonhosted.org/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl": "227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8",
+            "https://files.pythonhosted.org/packages/f7/ed/1aa2d585304ec07262e1a83a9889880701079dde796ac7b1d1826f40c63d/jaraco_functools-4.3.0.tar.gz": "cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294"
+          },
+          "jeepney": {
+            "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz": "cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732",
+            "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl": "97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"
+          },
+          "keyring": {
+            "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz": "0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66",
+            "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl": "552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"
+          },
+          "markdown-it-py": {
+            "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz": "cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3",
+            "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl": "87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"
+          },
+          "mdurl": {
+            "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl": "84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8",
+            "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz": "bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"
+          },
+          "more-itertools": {
+            "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl": "52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b",
+            "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz": "f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd"
+          },
+          "nh3": {
+            "https://files.pythonhosted.org/packages/0c/e0/cf1543e798ba86d838952e8be4cb8d18e22999be2a24b112a671f1c04fd6/nh3-0.3.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl": "ec6cfdd2e0399cb79ba4dcffb2332b94d9696c52272ff9d48a630c5dca5e325a",
+            "https://files.pythonhosted.org/packages/10/71/2fb1834c10fab6d9291d62c95192ea2f4c7518bd32ad6c46aab5d095cb87/nh3-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl": "0649464ac8eee018644aacbc103874ccbfac80e3035643c3acaab4287e36e7f5",
+            "https://files.pythonhosted.org/packages/23/1e/80a8c517655dd40bb13363fc4d9e66b2f13245763faab1a20f1df67165a7/nh3-0.3.0-cp313-cp313t-win_amd64.whl": "423201bbdf3164a9e09aa01e540adbb94c9962cc177d5b1cbb385f5e1e79216e",
+            "https://files.pythonhosted.org/packages/2f/d6/f1c6e091cbe8700401c736c2bc3980c46dca770a2cf6a3b48a175114058e/nh3-0.3.0-cp313-cp313t-win32.whl": "7275fdffaab10cc5801bf026e3c089d8de40a997afc9e41b981f7ac48c5aa7d5",
+            "https://files.pythonhosted.org/packages/33/c1/8f8ccc2492a000b6156dce68a43253fcff8b4ce70ab4216d08f90a2ac998/nh3-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl": "1adeb1062a1c2974bc75b8d1ecb014c5fd4daf2df646bbe2831f7c23659793f9",
+            "https://files.pythonhosted.org/packages/39/2c/6394301428b2017a9d5644af25f487fa557d06bc8a491769accec7524d9a/nh3-0.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "f416c35efee3e6a6c9ab7716d9e57aa0a49981be915963a82697952cba1353e1",
+            "https://files.pythonhosted.org/packages/4c/3c/cba7b26ccc0ef150c81646478aa32f9c9535234f54845603c838a1dc955c/nh3-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl": "80fe20171c6da69c7978ecba33b638e951b85fb92059259edd285ff108b82a6d",
+            "https://files.pythonhosted.org/packages/4e/9a/344b9f9c4bd1c2413a397f38ee6a3d5db30f1a507d4976e046226f12b297/nh3-0.3.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl": "37d3003d98dedca6cd762bf88f2e70b67f05100f6b949ffe540e189cc06887f9",
+            "https://files.pythonhosted.org/packages/5b/76/3165e84e5266d146d967a6cc784ff2fbf6ddd00985a55ec006b72bc39d5d/nh3-0.3.0-cp38-abi3-win_arm64.whl": "d97d3efd61404af7e5721a0e74d81cdbfc6e5f97e11e731bb6d090e30a7b62b2",
+            "https://files.pythonhosted.org/packages/5c/86/a96b1453c107b815f9ab8fac5412407c33cc5c7580a4daf57aabeb41b774/nh3-0.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "ce5e7185599f89b0e391e2f29cc12dc2e206167380cea49b33beda4891be2fe1",
+            "https://files.pythonhosted.org/packages/63/da/c5fd472b700ba37d2df630a9e0d8cc156033551ceb8b4c49cc8a5f606b68/nh3-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl": "ba0caa8aa184196daa6e574d997a33867d6d10234018012d35f86d46024a2a95",
+            "https://files.pythonhosted.org/packages/66/3f/cd37f76c8ca277b02a84aa20d7bd60fbac85b4e2cbdae77cb759b22de58b/nh3-0.3.0-cp38-abi3-musllinux_1_2_aarch64.whl": "634e34e6162e0408e14fb61d5e69dbaea32f59e847cfcfa41b66100a6b796f62",
+            "https://files.pythonhosted.org/packages/6a/1b/b15bd1ce201a1a610aeb44afd478d55ac018b4475920a3118ffd806e2483/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl": "e9e6a7e4d38f7e8dda9edd1433af5170c597336c1a74b4693c5cb75ab2b30f2a",
+            "https://files.pythonhosted.org/packages/8c/ae/324b165d904dc1672eee5f5661c0a68d4bab5b59fbb07afb6d8d19a30b45/nh3-0.3.0-cp38-abi3-win_amd64.whl": "bae63772408fd63ad836ec569a7c8f444dd32863d0c67f6e0b25ebbd606afa95",
+            "https://files.pythonhosted.org/packages/8f/14/079670fb2e848c4ba2476c5a7a2d1319826053f4f0368f61fca9bb4227ae/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "7852f038a054e0096dac12b8141191e02e93e0b4608c4b993ec7d4ffafea4e49",
+            "https://files.pythonhosted.org/packages/97/03/03f79f7e5178eb1ad5083af84faff471e866801beb980cc72943a4397368/nh3-0.3.0-cp38-abi3-musllinux_1_2_i686.whl": "c7a32a7f0d89f7d30cb8f4a84bdbd56d1eb88b78a2434534f62c71dac538c450",
+            "https://files.pythonhosted.org/packages/97/33/11e7273b663839626f714cb68f6eb49899da5a0d9b6bc47b41fe870259c2/nh3-0.3.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "389d93d59b8214d51c400fb5b07866c2a4f79e4e14b071ad66c92184fec3a392",
+            "https://files.pythonhosted.org/packages/9a/e0/af86d2a974c87a4ba7f19bc3b44a8eaa3da480de264138fec82fe17b340b/nh3-0.3.0-cp313-cp313t-win_arm64.whl": "16f8670201f7e8e0e05ed1a590eb84bfa51b01a69dd5caf1d3ea57733de6a52f",
+            "https://files.pythonhosted.org/packages/a3/e5/ac7fc565f5d8bce7f979d1afd68e8cb415020d62fa6507133281c7d49f91/nh3-0.3.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl": "af5aa8127f62bbf03d68f67a956627b1bd0469703a35b3dad28d0c1195e6c7fb",
+            "https://files.pythonhosted.org/packages/ad/7f/7c6b8358cf1222921747844ab0eef81129e9970b952fcb814df417159fb9/nh3-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "7c915060a2c8131bef6a29f78debc29ba40859b6dbe2362ef9e5fd44f11487c2",
+            "https://files.pythonhosted.org/packages/b4/11/340b7a551916a4b2b68c54799d710f86cf3838a4abaad8e74d35360343bb/nh3-0.3.0-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl": "a537ece1bf513e5a88d8cff8a872e12fe8d0f42ef71dd15a5e7520fecd191bbb",
+            "https://files.pythonhosted.org/packages/c3/a4/96cff0977357f60f06ec4368c4c7a7a26cccfe7c9fcd54f5378bf0428fd3/nh3-0.3.0.tar.gz": "d8ba24cb31525492ea71b6aac11a4adac91d828aadeff7c4586541bf5dc34d2f",
+            "https://files.pythonhosted.org/packages/c9/50/76936ec021fe1f3270c03278b8af5f2079038116b5d0bfe8538ffe699d69/nh3-0.3.0-cp38-abi3-win32.whl": "6d68fa277b4a3cf04e5c4b84dd0c6149ff7d56c12b3e3fab304c525b850f613d",
+            "https://files.pythonhosted.org/packages/ce/55/1974bcc16884a397ee699cebd3914e1f59be64ab305533347ca2d983756f/nh3-0.3.0-cp38-abi3-musllinux_1_2_x86_64.whl": "3f1b4f8a264a0c86ea01da0d0c390fe295ea0bcacc52c2103aca286f6884f518",
+            "https://files.pythonhosted.org/packages/ee/db/7aa11b44bae4e7474feb1201d8dee04fabe5651c7cb51409ebda94a4ed67/nh3-0.3.0-cp38-abi3-musllinux_1_2_armv7l.whl": "b0612ccf5de8a480cf08f047b08f9d3fecc12e63d2ee91769cb19d7290614c23",
+            "https://files.pythonhosted.org/packages/f3/ba/59e204d90727c25b253856e456ea61265ca810cda8ee802c35f3fadaab00/nh3-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl": "e90883f9f85288f423c77b3f5a6f4486375636f25f793165112679a7b6363b35"
+          },
+          "pkginfo": {
+            "https://files.pythonhosted.org/packages/24/03/e26bf3d6453b7fda5bd2b84029a426553bb373d6277ef6b5ac8863421f87/pkginfo-1.12.1.2.tar.gz": "5cd957824ac36f140260964eba3c6be6442a8359b8c48f4adf90210f33a04b7b",
+            "https://files.pythonhosted.org/packages/fa/3d/f4f2ba829efb54b6cd2d91349c7463316a9cc55a43fc980447416c88540f/pkginfo-1.12.1.2-py3-none-any.whl": "c783ac885519cab2c34927ccfa6bf64b5a704d7c69afaea583dd9b7afe969343"
+          },
+          "pycparser": {
+            "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl": "e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934",
+            "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz": "78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"
+          },
+          "pygments": {
+            "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz": "636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887",
+            "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl": "86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"
+          },
+          "pywin32-ctypes": {
+            "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz": "d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755",
+            "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl": "8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"
+          },
+          "readme-renderer": {
+            "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz": "8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1",
+            "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl": "2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151"
+          },
+          "requests": {
+            "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz": "c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652",
+            "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl": "3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b"
+          },
+          "requests-toolbelt": {
+            "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl": "cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06",
+            "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz": "7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"
+          },
+          "rfc3986": {
+            "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz": "97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c",
+            "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl": "50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"
+          },
+          "rich": {
+            "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl": "536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f",
+            "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz": "e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8"
+          },
+          "secretstorage": {
+            "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz": "2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77",
+            "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl": "f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"
+          },
+          "twine": {
+            "https://files.pythonhosted.org/packages/5d/ec/00f9d5fd040ae29867355e559a94e9a8429225a0284a3f5f091a3878bfc0/twine-5.1.1-py3-none-any.whl": "215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997",
+            "https://files.pythonhosted.org/packages/77/68/bd982e5e949ef8334e6f7dcf76ae40922a8750aa2e347291ae1477a4782b/twine-5.1.1.tar.gz": "9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db"
+          },
+          "urllib3": {
+            "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl": "bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4",
+            "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz": "1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"
+          },
+          "zipp": {
+            "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl": "071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e",
+            "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz": "a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"
+          }
+        }
+      },
+      "fact_version": "v1",
+      "index_urls": {
+        "https://pypi.org/simple/": {
+          "backports_tarfile": "/simple/backports-tarfile/",
+          "certifi": "/simple/certifi/",
+          "cffi": "/simple/cffi/",
+          "charset_normalizer": "/simple/charset-normalizer/",
+          "cryptography": "/simple/cryptography/",
+          "docutils": "/simple/docutils/",
+          "idna": "/simple/idna/",
+          "importlib_metadata": "/simple/importlib-metadata/",
+          "jaraco_classes": "/simple/jaraco-classes/",
+          "jaraco_context": "/simple/jaraco-context/",
+          "jaraco_functools": "/simple/jaraco-functools/",
+          "jeepney": "/simple/jeepney/",
+          "keyring": "/simple/keyring/",
+          "markdown_it_py": "/simple/markdown-it-py/",
+          "mdurl": "/simple/mdurl/",
+          "more_itertools": "/simple/more-itertools/",
+          "nh3": "/simple/nh3/",
+          "pkginfo": "/simple/pkginfo/",
+          "pycparser": "/simple/pycparser/",
+          "pygments": "/simple/pygments/",
+          "pywin32_ctypes": "/simple/pywin32-ctypes/",
+          "readme_renderer": "/simple/readme-renderer/",
+          "requests": "/simple/requests/",
+          "requests_toolbelt": "/simple/requests-toolbelt/",
+          "rfc3986": "/simple/rfc3986/",
+          "rich": "/simple/rich/",
+          "secretstorage": "/simple/secretstorage/",
+          "twine": "/simple/twine/",
+          "urllib3": "/simple/urllib3/",
+          "zipp": "/simple/zipp/"
+        }
+      }
+    }
+  }
 }
diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod
deleted file mode 100644
index 91f491b..0000000
--- a/WORKSPACE.bzlmod
+++ /dev/null
@@ -1,7 +0,0 @@
-workspace(
-    name = "gerrit",
-)
-
-load("//plugins:external_plugin_deps.bzl", "external_plugin_deps")
-
-external_plugin_deps()
diff --git a/configs/agent_configs.textproto b/configs/agent_configs.textproto
index 704943a..c16bf16b 100644
--- a/configs/agent_configs.textproto
+++ b/configs/agent_configs.textproto
@@ -35,3 +35,30 @@
   }
   automatic: true
 }
+
+# Commit Message Reviewer Agent
+configs: {
+  id: "gerrit-commit-message-review"
+  display_name: "Gerrit Commit Message Reviewer"
+  description: "Reviews the CL commit message for correctness, grammar, formatting compliance, and clarity."
+  skills: "gerrit_commit_message_review"
+  include_filters: {
+    project: "gerrit"
+    path_regex: "^/?COMMIT_MSG$"
+  }
+}
+
+# TypeScript Style Review Agent
+configs: {
+  id: "gerrit-typescript-style-review"
+  display_name: "TypeScript Style Review"
+  description: "Checks TypeScript files against style guide, focusing on property visibility and bypasses."
+  skills: "typescript_style_review"
+  include_filters: {
+    project: "gerrit"
+    project: "TestRepo"
+    path_regex: ".*\\.ts$"
+  }
+  automatic: true
+}
+
diff --git a/configs/skills/gerrit_commit_message_review/SKILL.md b/configs/skills/gerrit_commit_message_review/SKILL.md
new file mode 100644
index 0000000..46448f8
--- /dev/null
+++ b/configs/skills/gerrit_commit_message_review/SKILL.md
@@ -0,0 +1,502 @@
+---
+name: gerrit-commit-message-review
+description: Proofreads and suggests structural improvements for Git commit messages to ensure style guide compliance, completeness, and accuracy.
+---
+
+# Git Commit Message & Metadata Standards
+
+## Executive Summary
+
+Welcome to the definitive engineering reference for formatting, structuring, and
+preserving metadata inside Git commit messages. This document encapsulates the
+core conventions for commit hygiene, which are essential to ensuring commit log
+histories remain clean, readable, and highly traceable across development
+lifecycles.
+
+Adhering to these standards ensures metadata traceability, and provides clear,
+long-term context to future developers.
+
+## Summary
+
+| Chapter Theme / Title | Scope & Objective |
+| :--- | :--- |
+| **Commit Title Conventions** | Defines stylistic and length requirements for the first line of the commit message to optimize history navigation. |
+| **Commit Body Structure & Formatting** | Outlines instructions to clearly explain the "what" and "why" of the patchset with pragmatic conciseness and precise wrapping. |
+| **Metadata Footers & Preservations** | Enforces the strict preservation of system-critical integration footers (such as Change-Id and issue tracking IDs). |
+| **Review Feedback & Suggested Commit Message** | Instructs the reviewer to provide a complete, fully-compliant, copy-pasteable revised commit message. |
+
+
+--------------------------------------------------------------------------------
+
+## Chapter: Commit Title Conventions
+
+**Context:** The title line of a Git commit message is the first line of visual feedback for engineers navigating repository logs. To ensure standard sizing, clarity, and readability, title structures are subject to rigid constraints.
+
+### Summary
+
+| Rule ID | Principle / Constraint | Priority | Primary Symptom / Trap |
+| :--- | :--- | :--- | :--- |
+| **T1-01** | Concise & Imperative Commit Titles | High | Writing titles exceeding 60 characters, or utilizing past-tense/progressive verbs (e.g. "Fixed...", "Fixing..."). |
+
+### Rules
+
+#### T1-01: Concise & Imperative Commit Titles
+
+> **Rule:** Commit titles must be 60 characters or less, start with an imperative verb (e.g., "Add", "Fix", "Update", "Remove"), and use sentence case without trailing punctuation.
+>
+> **What:** The commit title line must be a concise, imperative sentence summary strictly 60 characters or less.
+>
+> **Applies To:** Git commit message first line.
+>
+> **Why:** The codebase's core validation rules programmatically block and flag commits with subjects exceeding 60 characters. Keeping the title under this strict limit avoids repository presubmit upload blockages and ensures neat display in CLI tools.
+
+**Trap 1: Writing passive, overly long, or descriptive titles using progressive or past tense.**
+
+**Don't:**
+```text
+Fixing the loading spinner bug in gr-reply-dialog.ts and adding tests
+```
+
+**Do:**
+```text
+Fix loading spinner and add test coverage
+```
+
+--------------------------------------------------------------------------------
+
+## Chapter: Commit Body Structure & Formatting
+
+**Context:** The body of a commit is a vital repository asset storing the architectural intent behind a change. It must explain engineering decisions with pragmatic conciseness, provide targeted context, and be wrapped strictly for terminal compatibility.
+
+### Summary
+
+| Rule ID | Principle / Constraint | Priority | Primary Symptom / Trap |
+| :--- | :--- | :--- | :--- |
+| **T2-01** | Explaining the Context: What and Why | High | Omitting commit bodies entirely, repeating the title, describing "how" instead of "why", or leaving critical design/bug links without context. |
+| **T2-02** | Strict Line Wrapping at 72 Characters | High | Writing continuous, single-line paragraphs that span past 72 characters, causing awkward wrapping in console windows. |
+| **T2-03** | Pragmatic Tone, Conciseness, and Anti-Filler | High | Writing verbose, flowery prose, introducing generic engineering philosophy/boilerplate, or using redundant Q&A layouts on simple changes. |
+
+--------------------------------------------------------------------------------
+
+### Rules
+
+#### T2-01: Explaining the Context: What and Why
+
+> **Rule:** The commit message body must clearly explain *what* changes were made and *why* they were necessary, focusing on context and architectural intent, while maintaining a concise, non-redundant, and pragmatic tone. The body must jump directly into the technical context or problem and must never repeat, rephrase, or start with a high-level introductory summary of the title. For complex, security-sensitive, or high-risk changes, the explanation should explicitly ground the "why" by referencing the relevant issue, bug tracking ID, or design/RFC document. Do not include empty, low-value, or vacuous bug references (e.g. 'To address b/XXXXX, ...') in the body text unless accompanied by descriptive context explaining what the bug or issue represents. If the specific bug context is not known or cannot be verified, the bug reference must be omitted from the body paragraphs entirely, relying solely on the metadata footer for tracking.
+>
+> **What:** Explanations in the body must detail the problem and the rationale for the solution, leaving the mechanical "how" to be read from the code diff, and omitting generic value propositions of development practices, non-technical fluff, or introductory meta-sentences. The opening paragraph of the body must begin directly with the context or problem being solved and not restate or paraphrase the commit title. If the commit relates to a complex problem or implements an approved design specification, the body should draw from and cite these linked resources to clarify the reasoning in a direct, straightforward manner. If citing a bug tracker ID in the body paragraphs, ensure it adds concrete value by describing what problem or feedback is tracked there; otherwise, leave the bug reference out of the narrative body and let the footer handle the link.
+>
+> **Applies To:** Commit message lines following the spacer blank line (line 3 and onward).
+>
+> **Why:** Obvious code listings are redundant. Context is key: for complex or sensitive engineering changes, subsequent maintainers must understand the origin of a requirement or design constraint (e.g., a specific bug, CVE, or design specification) without having to guess, establishing clear auditability.
+
+**Trap 1: Repeating or rephrasing the title, starting the body with a high-level introductory summary sentence, or omitting concrete context.**
+
+The commit title is already the high-level summary of the change. Starting the body of the commit message with a rephrasing, restatement, or introductory "thesis statement" (e.g. "Add the agent and define its corresponding guidelines") is highly redundant and wastes reader time. Do not write introductory meta-sentences; begin the body paragraphs by jumping directly into the technical context or the problem being solved.
+
+**Don't:**
+```text
+Fix loading spinner and add test coverage
+
+This change fixes the loading spinner and adds test coverage to gr-reply-dialog.ts.
+```
+*(Problem: Repeats the title almost verbatim in the body opening.)*
+
+**Do:**
+```text
+Fix loading spinner and add test coverage
+
+The loading spinner in gr-reply-dialog was experiencing visual jitter
+on rapid page transitions due to a race condition in the reactive
+lifecycle hook.
+
+This change moves property assignments out of firstUpdated to avoid
+unnecessary second-pass rendering, stabilizing the visual state.
+```
+*(Rationale: Jumps immediately into the concrete problem.)*
+
+**Trap 2: Omitting context from linked bugs or design documents in complex, critical, or security-sensitive changes.**
+
+While simple or minor bug fixes do not need to explicitly reference their issue IDs inside the body, major, high-risk, or architectural modifications that reference external specs, RFCs, or bug tracker tickets should integrate that context into the "why" explanation. Failing to do so makes the commit message look disconnected from its metadata, making review and auditability difficult.
+
+**Don't:**
+```text
+Enhance project deletion permission validation
+
+Only users with administrative privileges are allowed to delete a
+project, but the server was previously checking for owner status only.
+This change corrects the check to require system administrator scope.
+
+Bug: gerrit:40012901
+```
+*(Problem: A security-sensitive permission model change is being made under a bug, but the body only explains the mechanical change. It completely misses the security context—like the permission bypass mentioned in the bug report—making the reasoning for this risk-heavy change unclear without looking up the bug.)*
+
+**Do:**
+```text
+Enhance project deletion permission validation
+
+To resolve the permission bypass reported in gerrit:40012901, where
+project owners could bypass global security policies to delete resource
+containers, we must restrict deletion calls to administrators.
+
+As defined in the project deletion security spec (https://example.com/gerrit-delete-spec),
+only system-level administrators should have the capability to
+destroy project repositories in production environments.
+
+Bug: gerrit:40012901
+```
+
+**Trap 3: Adding vacuous or low-value bug/issue references to the commit body.**
+
+Do not introduce generic, filler sentences or phrases (like "To address b/12345...", "In reference to b/12345...", or "As requested in b/12345...") into the narrative body of a commit message unless you are actually adding descriptive, valuable context from that bug or design document. If the bug context is not known, or if you cannot visit/verify the bug contents, **omit** the reference from the body text entirely. The metadata footer at the bottom (e.g., `Bug: b/12345` or `Google-Bug-ID: b/12345`) is the correct place to handle automated tracking; adding a vacuous mention in the body adds zero explanatory value and only increases noise.
+
+**Don't (Vacuous reference in body):**
+```text
+Suggest full commit message and avoid pedantic reviews
+
+To address b/505405738, this change updates the review guidelines to
+require that commit message reviews provide a complete, copy-pasteable
+revised message when formatting issues are found. It also instructs the
+reviewer to avoid pedantic feedback on minor casing or phrasing if the
+original message is already clear and compliant.
+
+Google-Bug-ID: b/505405738
+Release-Notes: skip
+Change-Id: I75ef56099ea36b8838a65746abb3a4771fcefd23
+```
+*(Problem: The body paragraph starts with "To address b/505405738, ...", but this phrase does not explain what the bug represents or what context it provides. It is a low-value, mechanical filler phrase.)*
+
+**Do (When bug context is unknown - omit the reference from the body):**
+```text
+Suggest full commit message and avoid pedantic reviews
+
+Automated commit message reviews can sometimes generate fragmented,
+pedantic feedback on minor phrasing nits, creating friction rather than
+saving developer time.
+
+To improve usability, SKILL.md now requires reviewers to generate a
+complete, copy-pasteable revised commit message when formatting issues
+are found, and to suppress feedback on minor nits if the original
+commit is fundamentally sound.
+
+Google-Bug-ID: b/505405738
+Release-Notes: skip
+Change-Id: I75ef56099ea36b8838a65746abb3a4771fcefd23
+```
+*(Rationale: Since the bug context is not explicitly visited or verified, the vacuous reference is completely removed from the body text, and the body jumps directly into the problem context first rather than starting with a generic "This change..." statement.)*
+
+**Do (When bug context is visited/known - integrate descriptive context):**
+```text
+Suggest full commit message and avoid pedantic reviews
+
+In b/505405738, developers reported being slowed down by fragmented,
+pedantic automated review feedback and requested ready-to-apply
+suggestions to reduce manual editing friction.
+
+To address this, this change updates the guidelines to require a
+complete, copy-pasteable revised message when formatting issues are
+found, and instructs reviewers to tolerate minor casing or phrasing
+variations when the original message is already compliant.
+
+Google-Bug-ID: b/505405738
+Release-Notes: skip
+Change-Id: I75ef56099ea36b8838a65746abb3a4771fcefd23
+```
+*(Rationale: The bug reference in the body now adds actual value by explaining exactly what issue/feedback was reported in b/505405738, providing important context to future maintainers, while explaining the problem/need before the solution.)*
+```
+*(Rationale: Since this is a high-risk change (a security bypass), the body explicitly connects to the bug report gerrit:40012901 and links the authoritative security spec (e.g., https://example.com/gerrit-delete-spec). This establishes bulletproof reasoning and traceability for a critical modification.)*
+
+--------------------------------------------------------------------------------
+
+#### T2-02: Strict Line Wrapping at 72 Characters
+
+> **Rule:** Wrap the body of all commit messages strictly at 72 characters per line, except for unwrappable URLs, file paths, or commands.
+>
+> **What:** Lines in the commit body must have explicit carriage returns at or before 72 columns.
+>
+> **Applies To:** Git commit message bodies.
+>
+> **Why:** Terminal output screens wrap at standard columns. Explicitly wrapping to 72 characters ensures clean reading in simple text editors, CLI viewers, and patch viewers.
+
+**Trap 1: Appending full paragraphs without manual word wrapping.**
+
+**Don't:**
+```text
+This change refactors the core caching helper and resolves a race condition that occurs when the same component gets disconnected rapidly from the DOM during teardown, which historically resulted in an uncaught exception.
+```
+
+**Do:**
+```text
+This change refactors the core caching helper and resolves a race
+condition that occurs when the same component gets disconnected
+rapidly from the DOM during teardown, which historically resulted
+in an uncaught exception.
+```
+
+--------------------------------------------------------------------------------
+
+#### T2-03: Pragmatic Tone, Conciseness, and Anti-Filler
+
+> **Rule:** Commit messages must be concise, direct, and free of conversational filler, obvious generalities, marketing/PR speak, or excessive boilerplate structure. Every sentence must serve to communicate technical context.
+>
+> **What:** Avoid long-winded introductions (e.g. "To ensure standard high-quality..."), platitudes (e.g. "Commit messages are vital repositories of engineering intent..."), and rigid Q&A-style templates (like "What is changing / Why this is necessary" headers) unless the change is highly complex and structurally demands them. Keep explanations simple, direct, and focused on the technical problem and its solution.
+>
+> **Applies To:** Git commit message bodies.
+>
+> **Why:** Verbose, flowery, or highly templated commit messages clutter repository logs and increase cognitive load for engineers searching history. A staff engineer's time is valuable; the commit message must deliver maximum signal-to-noise ratio.
+
+**Trap 1: Including generic software engineering justifications or explaining why code review, testing, or good practices are important in general.**
+Do not write essays on general design philosophy in the commit message. Stick strictly to the specific change's technical facts.
+
+**Don't:**
+```text
+Register gerrit-commit-message-review agent and skill
+
+To enforce rigorous commit log hygiene across this repository, this
+change registers the new 'gerrit-commit-message-review' AI reviewer
+agent and establishes its associated quality guidelines.
+
+Commit messages are permanent repositories of engineering intent, but
+manual review is prone to human oversight. Under the feature request
+in Issue 505405738, this system automates audit checks to provide
+instant, high-fidelity feedback. By deploying a specialized agent that
+evaluates formatting style, structural completeness, and footer
+integrity, developers receive automated proofreading findings directly
+in the Gerrit Checks UI (with ready-to-apply autofixes where
+applicable).
+```
+*(Problem: Highly verbose, generic text explaining general AI reviewer value propositions and repository hygiene, filled with fluff sentences like "Commit messages are permanent repositories...")*
+
+**Do:**
+```text
+Register gerrit-commit-message-review agent and skill
+
+To automate commit log hygiene audits across this repository, this
+change registers the new agent to trigger automatically on COMMIT_MSG
+changes. The accompanying skill definition outlines rules for
+subject-line format, strict 72-character line wrapping, context and
+intent explanation, and strict preservation of integration footers.
+
+Bug: Issue 505405738
+```
+*(Rationale: High signal-to-noise ratio. Completely avoids vacuous issue references in the body and jumps directly into the technical mechanism, leaving the issue linkage to the metadata footer.)*
+
+**Trap 2: Forcing complex multi-section layouts (like bullet points or Q&A headers) for straightforward, medium-sized, or simple changes.**
+Use simple, direct paragraphs instead of lists and structural headings whenever possible. Only use numbered lists when listing a sequence of highly distinct architectural changes.
+
+**Don't:**
+```text
+Fix loading spinner and add test coverage
+
+What is changing:
+1. The loading spinner in gr-reply-dialog.ts is fixed.
+2. Property assignments are moved out of firstUpdated to avoid dual rendering.
+3. Tests are added.
+
+Why this is necessary:
+The loading spinner was experiencing visual jitter on rapid page transitions
+due to a race condition in the reactive lifecycle hook, which is bad for UX.
+```
+*(Problem: Trivial bug fix is forced into a multi-headed structure with redundant wording.)*
+
+**Do:**
+```text
+Fix loading spinner and add test coverage
+
+The loading spinner in gr-reply-dialog was experiencing visual jitter
+on rapid page transitions due to a race condition in the reactive
+lifecycle hook.
+
+This change moves property assignments out of firstUpdated to avoid
+unnecessary second-pass rendering, stabilizing the visual state.
+```
+*(Rationale: Short, elegant, two paragraphs of narrative flow. Fully explains the problem and high-level solution without rigid, repetitive structural overhead.)*
+
+**Trap 3: Over-explaining straightforward changes, using sequential narrations ("First", "Second"), or explaining obvious developer-experience benefits.**
+Avoid writing multiple paragraphs or sequential lists to explain simple, singular enhancements. Never dedicate sentences to explaining *why* a change is beneficial in general (e.g. explaining that "this saves developer effort" or "improves developer experience"). Ground the explanation purely in the technical delta and keep simple changes within a single, dense paragraph of 2 to 3 sentences max.
+
+**Don't (Too Verbose):**
+```text
+Suggest full commit message and avoid pedantic reviews
+
+To address b/505405738 and improve the developer experience when
+addressing commit message feedback, this change updates the review
+guidelines in SKILL.md with a new chapter outlining response standards.
+
+First, if any formatting or hygiene issues are found, the reviewer must
+provide a complete, fully-compliant, and copy-pasteable revised commit
+message. This saves developer effort and streamlines the edit workflow.
+
+Second, to prevent automated review noise, the reviewer must adopt a
+pragmatic approach and tolerate minor casing or subjective phrasing
+differences if the message is already clear and compliant.
+```
+*(Problem: Highly verbose, uses sequential enumeration ("First", "Second"), and explains obvious general benefits like "This saves developer effort and streamlines the edit workflow".)*
+
+**Do (Ultra-Concise & High Density):**
+```text
+Suggest full commit message and avoid pedantic reviews
+
+Automated commit message reviews can generate fragmented, pedantic
+feedback on minor phrasing nits, creating friction rather than saving
+developer time. This change updates SKILL.md to require reviewers to
+provide a complete, copy-pasteable revised commit message when
+violations are found, and to tolerate minor variations if the original
+message is fundamentally compliant.
+
+Google-Bug-ID: b/505405738
+Release-Notes: skip
+```
+*(Rationale: Highly concise and high signal-to-noise ratio. Explains the problem/why context first, and avoids any vacuous issue reference prefixes or "This change..." introductory meta-sentences, while utilizing the metadata footer for bug linkage.)*
+
+
+--------------------------------------------------------------------------------
+
+## Chapter: Metadata Footers & Preservations
+
+**Context:** Dynamic metadata footers serve as vital integration links connecting code changes to issue tracking systems, code review platforms, and automated release auditing pipelines.
+
+### Summary
+
+| Rule ID | Principle / Constraint | Priority | Primary Symptom / Trap |
+| :--- | :--- | :--- | :--- |
+| **T3-01** | Mandatory Integration Footer Preservation | Critical | Modifying, corrupting, or dropping structured footers (Change-Id, Bug, or issue tracking keys) during edits. |
+
+--------------------------------------------------------------------------------
+
+### Rules
+
+#### T3-01: Mandatory Integration Footer Preservation
+
+> **Rule:** Always preserve all structured git metadata footers at the bottom of the commit message, matching the appropriate tracker style based on environment context.
+>
+> **What:** Do not modify, corrupt, or drop system-critical footers such as Change-Id, Bug, or tracker reference keys during edits.
+>
+> **Applies To:** Commit message footer block at the bottom.
+>
+> **Why:** Code review platforms (such as Gerrit) track revisions strictly using the `Change-Id` footer. Deleting it detaches revision history or breaks integration webhooks. Similarly, issue tracking systems rely on matching keys (e.g., `Bug: <ID>`, `Closes #<ID>`) to link code commits with project tickets.
+
+**Trap 1: Amending or rewriting the commit message and dropping the original metadata footers.**
+
+**Don't:**
+```text
+Update system cache configs
+
+Refactored memory size and cache duration parameters.
+```
+
+**Do (Standard issue tracker format):**
+```text
+Update system cache configs
+
+Refactored memory size and cache duration parameters.
+
+Bug: Issue 12345
+Release-Notes: skip
+Change-Id: Iab12cd34ef560078009000120034005600780090
+```
+
+**Do (GitHub/GitLab tracker format):**
+```text
+Update system cache configs
+
+Refactored memory size and cache duration parameters.
+
+Closes #1234567
+Release-Notes: skip
+Change-Id: Iab12cd34ef560078009000120034005600780090
+```
+
+--------------------------------------------------------------------------------
+
+## Chapter: Review Feedback & Suggested Commit Message
+
+**Context:** To provide maximum value and minimize friction, the reviewer must not only point out formatting/hygiene violations but also provide a complete, corrected, and fully-compliant commit message that the developer can copy and paste directly into Gerrit's commit message editor.
+
+### Summary
+
+| Rule ID | Principle / Constraint | Priority | Primary Symptom / Trap |
+| :--- | :--- | :--- | :--- |
+| **T4-01** | Provide a Complete Suggested Commit Message | High | Providing only high-level feedback or listing line-by-line suggestions without providing a single, copy-pasteable revised commit message. |
+| **T4-02** | Pragmatic Tolerance & Anti-Noise | High | Suggesting rewrites for minor stylistic differences or trivial casing when the original commit message is already highly informative and readable. |
+
+--------------------------------------------------------------------------------
+
+### Rules
+
+#### T4-01: Provide a Complete Suggested Commit Message
+
+> **Rule:** Whenever any formatting, structure, or hygiene issues are identified in the commit message, the review feedback must include a dedicated section containing the complete, fully-compliant, and improved version of the commit message wrapped inside a markdown code block (e.g., using `text` or `git` syntax highlighting). If the original commit message is already satisfactory, fully compliant, and informative, no revised version or suggestions should be provided.
+>
+> **What:** The review feedback must provide the complete revised commit message in a single code block as a drop-in replacement ONLY when violations or potential improvements are found. This suggested message must meticulously apply all the guidelines defined in this skill (e.g., title length under 60 chars, imperative verbs, strict 72-character line wrapping in the body, pragmatic and concise explanation). It **must** preserve all existing metadata footers (like `Change-Id`, `Bug`, `Closes`, and `Release-Notes`) exactly as they appeared in the original message. If the commit message is already fully compliant, the reviewer should state that no improvements are necessary and omit the suggestion block.
+>
+> **Applies To:** Review feedback reports and summary comments on COMMIT_MSG.
+>
+> **Why:** Developers want to resolve formatting issues as quickly as possible. Providing a complete, copy-pasteable improved message eliminates the need for the developer to manually re-wrap lines or rewrite sentences, significantly improving the developer experience. However, forcing a rewrite when the message is already of high quality causes unnecessary noise and friction.
+
+**Trap 1: Providing feedback comments on individual lines but omitting a single, unified revised commit message.**
+
+**Don't:**
+```text
+Line 1: The title has 65 characters, which is over the 60-character limit. Please shorten it.
+Line 3: This line is 85 characters long. Please wrap it at 72 characters.
+```
+
+**Do:**
+````text
+### Commit Message Review
+
+I found a few formatting issues with your commit message:
+1. The title is too long (65 characters).
+2. The body paragraphs are not wrapped at 72 characters.
+
+Here is an improved, fully-compliant version of your commit message that you can copy and paste directly into the Gerrit edit dialog:
+
+```text
+Fix loading spinner and add test coverage
+
+The loading spinner in gr-reply-dialog was experiencing visual jitter
+on rapid page transitions due to a race condition in the reactive
+lifecycle hook.
+
+This change moves property assignments out of firstUpdated to avoid
+unnecessary second-pass rendering, stabilizing the visual state.
+
+Bug: Issue 12345
+Release-Notes: skip
+Change-Id: Iab12cd34ef560078009000120034005600780090
+```
+````
+*(Rationale: Provides a ready-to-use solution that saves developer effort.)*
+
+--------------------------------------------------------------------------------
+
+#### T4-02: Pragmatic Tolerance & Anti-Noise
+
+> **Rule:** The reviewer must adopt a pragmatic, non-pedantic approach to evaluating commit messages. Do NOT suggest rewrites for minor stylistic differences, subjective phrasing preferences, or trivial casing choices if the original message is already clear, informative, correctly wrapped, and compliant with core limits.
+>
+> **What:** Apply a high threshold of value before flagging a commit message or suggesting an alternative. Trivial stylistic points (such as starting a component prefix colon with a lowercase verb, e.g., `GrepServlet: add ...` vs `GrepServlet: Add ...`, or slightly differing sentence structures that express the same context) are considered acceptable. The reviewer must **never** post comments or generate a suggestion block for these minor variations. Suggest revisions **only** when there are clear, objective violations (e.g., title > 60 chars, body lines > 72 chars, missing essential context, or corrupted/missing metadata).
+>
+> **Applies To:** All review feedback reports and comments.
+>
+> **Why:** Superfluous or nitpicky reviews (often referred to as "pedantic noise") irritate authors, waste review cycles, and erode trust in automated tooling. AI reviews must focus strictly on high-value correctness, safety, and critical readability standards.
+
+**Trap 1: Flagging a well-written, informative, and correctly-wrapped commit message over minor sentence formatting or prefix casing.**
+
+**Don't (Pedantic Noise):**
+```text
+### Commit Message Review
+
+The title uses a lowercase verb after the prefix ("add" instead of "Add"). Also, we can improve the body phrasing to be slightly more descriptive.
+
+Suggested Commit Message:
+GrepServlet: Add JSON content search endpoint
+
+To support content search in Gitiles (Issue 376381593)...
+```
+*(Problem: The original commit message was already outstanding. Suggesting a rewrite for trivial casing and wording differences adds no structural value.)*
+
+**Do:**
+State that the commit message is fully compliant and satisfactory, and do not post any individual comments or suggested rewrite.
+
+
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/configs/skills/typescript_style_review/SKILL.md b/configs/skills/typescript_style_review/SKILL.md
new file mode 100644
index 0000000..d1e79f2
--- /dev/null
+++ b/configs/skills/typescript_style_review/SKILL.md
@@ -0,0 +1,42 @@
+---
+name: typescript-style-review
+description: Reviews TypeScript code against the Google TS Style Guide.
+---
+
+# TypeScript Style Review Guide
+
+## Executive Summary
+
+This guide serves as the instruction set for reviewing TypeScript code. The
+goal is to ensure that all TypeScript code adheres to the official Google
+TypeScript Style Guide.
+
+## Guidelines
+
+You must review the TypeScript files in the changelist and ensure they comply
+with the rules and best practices outlined in the official Google TypeScript
+Style Guide.
+
+### Reference
+*   **Google TypeScript Style Guide**:
+    https://google.github.io/styleguide/tsguide.html
+
+## Review Instructions
+
+When performing the review:
+1.  Identify any violations of the Google TypeScript Style Guide.
+2.  For each violation, provide a clear explanation of the problem, a
+    suggestion for how to fix it (with a code snippet if appropriate), and a
+    reference to the relevant section of the style guide.
+3.  Format your comments using the following template:
+
+### Problem
+Clearly and succinctly describe the style guide violation.
+
+### Suggestion
+Provide a suggestion for improvement, including a code snippet.
+
+### Reference
+Provide the link to the relevant section in the Google TypeScript Style Guide
+(e.g.,
+`https://google.github.io/styleguide/tsguide.html#visibility-of-properties-accessed-from-templates`).
diff --git a/contrib/maintenance/gerrit/site.py b/contrib/maintenance/gerrit/site.py
index faf6c02..450acaa 100644
--- a/contrib/maintenance/gerrit/site.py
+++ b/contrib/maintenance/gerrit/site.py
@@ -37,11 +37,11 @@
             ) as cfg:
                 config_base_path = cfg.get("gerrit", None, "basePath", "git")
                 if os.path.isabs(config_base_path):
-                    self.basePath = config_base_path
+                    self.base_path = config_base_path
                 else:
-                    self.basePath = os.path.join(self.path, config_base_path)
+                    self.base_path = os.path.join(self.path, config_base_path)
 
-        return self.basePath
+        return self.base_path
 
     def get_projects(self, excludes=None):
         for current, dirs, _ in os.walk(self.get_base_path(), topdown=True):
diff --git a/external_deps.lock.json b/external_deps.lock.json
index a3ce899..141bbfc 100644
--- a/external_deps.lock.json
+++ b/external_deps.lock.json
@@ -8,10 +8,10 @@
     "com.github.ben-manes.caffeine:caffeine": 1735183231,
     "com.github.ben-manes.caffeine:guava": -2131703186,
     "com.github.rholder:guava-retrying": 1309513165,
-    "com.google.auto.factory:auto-factory": -1233360965,
-    "com.google.auto.service:auto-service-annotations": 953830824,
-    "com.google.auto.value:auto-value": 1146221104,
-    "com.google.auto.value:auto-value-annotations": -1101276935,
+    "com.google.auto.factory:auto-factory": -1222857861,
+    "com.google.auto.service:auto-service-annotations": 964333928,
+    "com.google.auto.value:auto-value": -661233359,
+    "com.google.auto.value:auto-value-annotations": 1386235898,
     "com.google.auto:auto-common": -832702775,
     "com.google.code.findbugs:jsr305": -1992157670,
     "com.google.code.gson:gson": 2063056812,
@@ -28,13 +28,13 @@
     "com.google.guava:failureaccess": -2032498474,
     "com.google.guava:guava": -1756621521,
     "com.google.guava:guava-testlib": -203887467,
-    "com.google.inject.extensions:guice-assistedinject": -1667539622,
-    "com.google.inject.extensions:guice-servlet": 569202692,
-    "com.google.inject:guice": -1660789120,
+    "com.google.inject.extensions:guice-assistedinject": -1742632647,
+    "com.google.inject.extensions:guice-servlet": -1284557873,
+    "com.google.inject:guice": 2106361664,
     "com.google.j2objc:j2objc-annotations": -727464895,
     "com.google.jimfs:jimfs": -1004381565,
-    "com.google.protobuf:protobuf-java": 1237026331,
-    "com.google.template:soy": -1478719887,
+    "com.google.protobuf:protobuf-java": 1247529435,
+    "com.google.template:soy": -843524660,
     "com.google.truth.extensions:truth-java8-extension": -129319374,
     "com.google.truth.extensions:truth-liteproto-extension": 1463279446,
     "com.google.truth.extensions:truth-proto-extension": 1270333764,
@@ -109,7 +109,7 @@
     "commons-dbcp:commons-dbcp": -873877417,
     "commons-digester:commons-digester": 1688456634,
     "commons-io:commons-io": 1305681826,
-    "commons-logging:commons-logging": 243315756,
+    "commons-logging:commons-logging": -264381874,
     "commons-net:commons-net": 1227155931,
     "commons-pool:commons-pool": -2015226625,
     "commons-validator:commons-validator": -89255997,
@@ -119,16 +119,17 @@
     "io.github.java-diff-utils:java-diff-utils": 1296992014,
     "io.sweers.autotransient:autotransient": 1591678970,
     "jakarta.inject:jakarta.inject-api": 190564362,
+    "jakarta.servlet:jakarta.servlet-api": -3550902,
     "javax.activation:activation": -940616209,
     "javax.annotation:jsr250-api": -593155132,
     "javax.inject:javax.inject": -297932879,
     "javax.servlet:javax.servlet-api": 669233360,
     "junit:junit": -744267592,
     "log4j:log4j": 182326902,
-    "net.bytebuddy:byte-buddy": 731630558,
-    "net.bytebuddy:byte-buddy-agent": 294689510,
-    "net.java.dev.jna:jna": 929040997,
-    "net.java.dev.jna:jna-platform": 1235639073,
+    "net.bytebuddy:byte-buddy": -1875367778,
+    "net.bytebuddy:byte-buddy-agent": 1759328918,
+    "net.java.dev.jna:jna": -867910362,
+    "net.java.dev.jna:jna-platform": -561312286,
     "net.minidev:json-smart": -1043043954,
     "net.sf.jopt-simple:jopt-simple": 906822697,
     "net.sourceforge.nekohtml:nekohtml": 1723624706,
@@ -152,12 +153,12 @@
     "org.apache.lucene:lucene-queries": 1341801569,
     "org.apache.lucene:lucene-queryparser": 2090205297,
     "org.apache.lucene:lucene-sandbox": 1803509280,
-    "org.apache.mina:mina-core": -955969490,
+    "org.apache.mina:mina-core": -1403307213,
     "org.apache.sshd:sshd-common": 1844247612,
     "org.apache.sshd:sshd-core": 602454664,
-    "org.apache.sshd:sshd-mina": -1925771193,
-    "org.apache.sshd:sshd-osgi": 1671034928,
-    "org.apache.sshd:sshd-sftp": 2079258759,
+    "org.apache.sshd:sshd-mina": 582747848,
+    "org.apache.sshd:sshd-osgi": -115413327,
+    "org.apache.sshd:sshd-sftp": 292810504,
     "org.asciidoctor:asciidoctorj": -457860213,
     "org.assertj:assertj-core": -1145412507,
     "org.bouncycastle:bcpg-jdk18on": -1572213535,
@@ -168,18 +169,18 @@
     "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.ee11:jetty-ee11-servlet": -562048764,
+    "org.eclipse.jetty.ee8:jetty-ee8-nested": 860016002,
+    "org.eclipse.jetty.ee8:jetty-ee8-security": -175183477,
+    "org.eclipse.jetty.ee8:jetty-ee8-servlet": 1148815122,
+    "org.eclipse.jetty:jetty-http": -2043364460,
+    "org.eclipse.jetty:jetty-io": -1852333390,
+    "org.eclipse.jetty:jetty-jmx": -26410899,
+    "org.eclipse.jetty:jetty-security": 668339532,
+    "org.eclipse.jetty:jetty-server": -1937626673,
+    "org.eclipse.jetty:jetty-session": 1109126766,
+    "org.eclipse.jetty:jetty-util": 2042889998,
+    "org.eclipse.jetty:jetty-util-ajax": 1865427299,
     "org.hamcrest:hamcrest": 1547523135,
     "org.jruby:jruby-complete": -2103568068,
     "org.json:json": -811907600,
@@ -187,7 +188,7 @@
     "org.mockito:mockito-core": 1330163800,
     "org.nibor.autolink:autolink": -342487050,
     "org.objenesis:objenesis": 748376655,
-    "org.openid4java:openid4java": -842286787,
+    "org.openid4java:openid4java": -1889179110,
     "org.openjdk.jmh:jmh-core": 983716932,
     "org.openjdk.jmh:jmh-generator-annprocess": -1162360421,
     "org.ow2.asm:asm": 1206815935,
@@ -202,309 +203,311 @@
     "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,
     "xml-apis:xml-apis": -113825062
   },
   "__RESOLVED_ARTIFACTS_HASH": {
-    "antlr:antlr": 2120497295,
-    "aopalliance:aopalliance": -1268465981,
-    "aopalliance:aopalliance:jar:sources": 442956464,
-    "args4j:args4j": 1165073517,
-    "args4j:args4j:jar:sources": -1061197748,
-    "ch.qos.reload4j:reload4j": 1742597253,
-    "ch.qos.reload4j:reload4j:jar:sources": -505557081,
-    "com.beust:jcommander": -86812675,
-    "com.beust:jcommander:jar:sources": -377940076,
-    "com.github.ben-manes.caffeine:caffeine": 386266583,
-    "com.github.ben-manes.caffeine:caffeine:jar:sources": 824791053,
-    "com.github.ben-manes.caffeine:guava": -586447867,
-    "com.github.ben-manes.caffeine:guava:jar:sources": -2102940169,
-    "com.github.rholder:guava-retrying": -193656863,
-    "com.github.rholder:guava-retrying:jar:sources": 402259526,
-    "com.google.auto.factory:auto-factory": 117391549,
-    "com.google.auto.factory:auto-factory:jar:sources": 1626334411,
-    "com.google.auto.service:auto-service-annotations": -2030804522,
-    "com.google.auto.service:auto-service-annotations:jar:sources": 1880995980,
-    "com.google.auto.value:auto-value": 1610847974,
-    "com.google.auto.value:auto-value-annotations": 818774630,
-    "com.google.auto.value:auto-value-annotations:jar:sources": -1313058273,
-    "com.google.auto.value:auto-value:jar:sources": -1087092342,
-    "com.google.auto:auto-common": 1232278285,
-    "com.google.auto:auto-common:jar:sources": 1238804620,
-    "com.google.code.findbugs:jsr305": 1028218835,
-    "com.google.code.findbugs:jsr305:jar:sources": 1130389911,
-    "com.google.code.gson:gson": 1676184452,
-    "com.google.code.gson:gson:jar:sources": -1194250318,
-    "com.google.common.html.types:types": -1195124169,
-    "com.google.common.html.types:types:jar:sources": 1541167117,
-    "com.google.errorprone:error_prone_annotations": -2118374750,
-    "com.google.errorprone:error_prone_annotations:jar:sources": -88858373,
-    "com.google.flogger:flogger": 2071094150,
-    "com.google.flogger:flogger-log4j-backend": -1752942291,
-    "com.google.flogger:flogger-log4j-backend:jar:sources": 167088335,
-    "com.google.flogger:flogger-system-backend": 170636970,
-    "com.google.flogger:flogger-system-backend:jar:sources": 267353177,
-    "com.google.flogger:flogger:jar:sources": -399970922,
-    "com.google.flogger:google-extensions": 683610995,
-    "com.google.flogger:google-extensions:jar:sources": -362108063,
-    "com.google.gitiles:blame-cache": -1735353948,
-    "com.google.gitiles:blame-cache:jar:sources": -370259038,
-    "com.google.gitiles:gitiles-servlet": -1411302355,
-    "com.google.gitiles:gitiles-servlet:jar:sources": 192620509,
-    "com.google.guava:failureaccess": -121989663,
-    "com.google.guava:failureaccess:jar:sources": 2092951686,
-    "com.google.guava:guava": -1983533712,
-    "com.google.guava:guava-testlib": 869030181,
-    "com.google.guava:guava-testlib:jar:sources": -1116521441,
-    "com.google.guava:guava:jar:sources": 1163674882,
-    "com.google.guava:listenablefuture": -181371066,
-    "com.google.inject.extensions:guice-assistedinject": -191835468,
-    "com.google.inject.extensions:guice-assistedinject:jar:sources": 1682505664,
-    "com.google.inject.extensions:guice-servlet": -1455734849,
-    "com.google.inject.extensions:guice-servlet:jar:sources": 1055060429,
-    "com.google.inject:guice": -1714385564,
-    "com.google.inject:guice:jar:sources": 1581524423,
-    "com.google.j2objc:j2objc-annotations": -1833492981,
-    "com.google.j2objc:j2objc-annotations:jar:sources": 1596689722,
-    "com.google.jimfs:jimfs": -542987600,
-    "com.google.jimfs:jimfs:jar:sources": 1692501412,
-    "com.google.jsinterop:jsinterop-annotations": 1916195800,
-    "com.google.jsinterop:jsinterop-annotations:jar:sources": -1738684177,
-    "com.google.protobuf:protobuf-java": -1979841029,
-    "com.google.protobuf:protobuf-java:jar:sources": 1539671335,
-    "com.google.template:soy": 1725524315,
-    "com.google.template:soy:jar:sources": 934091432,
-    "com.google.truth.extensions:truth-java8-extension": 2072975728,
-    "com.google.truth.extensions:truth-java8-extension:jar:sources": -404559504,
-    "com.google.truth.extensions:truth-liteproto-extension": -47385409,
-    "com.google.truth.extensions:truth-liteproto-extension:jar:sources": -96134878,
-    "com.google.truth.extensions:truth-proto-extension": 1308006125,
-    "com.google.truth.extensions:truth-proto-extension:jar:sources": -1961892523,
-    "com.google.truth:truth": 1710077790,
-    "com.google.truth:truth:jar:sources": -1194452779,
-    "com.googlecode.javaewah:JavaEWAH": 157530325,
-    "com.googlecode.javaewah:JavaEWAH:jar:sources": 728434282,
-    "com.googlecode.prolog-cafe:prolog-cafeteria": 1166081954,
-    "com.googlecode.prolog-cafe:prolog-cafeteria:jar:sources": 186391213,
-    "com.googlecode.prolog-cafe:prolog-compiler": -741003406,
-    "com.googlecode.prolog-cafe:prolog-compiler:jar:sources": 126666107,
-    "com.googlecode.prolog-cafe:prolog-io": -900599131,
-    "com.googlecode.prolog-cafe:prolog-io:jar:sources": -1440088888,
-    "com.googlecode.prolog-cafe:prolog-runtime": 828309397,
-    "com.googlecode.prolog-cafe:prolog-runtime:jar:sources": -1157091735,
-    "com.h2database:h2": 682770038,
-    "com.h2database:h2:jar:sources": -675004040,
-    "com.ibm.icu:icu4j": -1964869803,
-    "com.ibm.icu:icu4j:jar:sources": 864352766,
-    "com.icegreen:greenmail": 537606032,
-    "com.icegreen:greenmail:jar:sources": -1978817971,
-    "com.jcraft:jsch": 1986987929,
-    "com.jcraft:jsch:jar:sources": 707308247,
-    "com.jcraft:jzlib": 1581025040,
-    "com.jcraft:jzlib:jar:sources": 807243431,
-    "com.ryanharter.auto.value:auto-value-gson-extension": 1639397257,
-    "com.ryanharter.auto.value:auto-value-gson-extension:jar:sources": 1925708692,
-    "com.ryanharter.auto.value:auto-value-gson-factory": 1817252669,
-    "com.ryanharter.auto.value:auto-value-gson-factory:jar:sources": -768025607,
-    "com.ryanharter.auto.value:auto-value-gson-runtime": 1015116695,
-    "com.ryanharter.auto.value:auto-value-gson-runtime:jar:sources": -831897407,
-    "com.squareup:javapoet": 1313128977,
-    "com.squareup:javapoet:jar:sources": -1200205912,
-    "com.sun.mail:javax.mail": -382507283,
-    "com.sun.mail:javax.mail:jar:sources": 1073816789,
-    "com.vladsch.flexmark:flexmark-all:jar:lib": 347351331,
-    "commons-codec:commons-codec": 1048744614,
-    "commons-codec:commons-codec:jar:sources": -1637317619,
-    "commons-dbcp:commons-dbcp": -1946826524,
-    "commons-dbcp:commons-dbcp:jar:sources": -1238094905,
-    "commons-io:commons-io": 1463238719,
-    "commons-io:commons-io:jar:sources": -120466886,
-    "commons-net:commons-net": 29132340,
-    "commons-net:commons-net:jar:sources": 1898783167,
-    "commons-pool:commons-pool": -3173032,
-    "commons-pool:commons-pool:jar:sources": 1763080609,
-    "commons-validator:commons-validator": -1615658806,
-    "commons-validator:commons-validator:jar:sources": 1267489728,
-    "dk.brics:automaton": 1622735787,
-    "dk.brics:automaton:jar:sources": 1946522418,
-    "eu.medsea.mimeutil:mime-util": 1812001452,
-    "io.dropwizard.metrics:metrics-core": -1856132969,
-    "io.dropwizard.metrics:metrics-core:jar:sources": 159424675,
-    "io.github.java-diff-utils:java-diff-utils": 457660153,
-    "io.github.java-diff-utils:java-diff-utils:jar:sources": -619235043,
-    "io.sweers.autotransient:autotransient": 1340991802,
-    "io.sweers.autotransient:autotransient:jar:sources": 80080981,
-    "jakarta.inject:jakarta.inject-api": 851002214,
-    "jakarta.inject:jakarta.inject-api:jar:sources": -1570274750,
-    "javax.activation:activation": -1092171588,
-    "javax.activation:activation:jar:sources": 1310123688,
-    "javax.inject:javax.inject": -1960241368,
-    "javax.inject:javax.inject:jar:sources": -34689928,
-    "javax.servlet:javax.servlet-api": 1796323811,
-    "javax.servlet:javax.servlet-api:jar:sources": 137081253,
-    "junit:junit": 238187285,
-    "junit:junit:jar:sources": 1084731434,
-    "net.bytebuddy:byte-buddy": 1931414768,
-    "net.bytebuddy:byte-buddy-agent": 1328010878,
-    "net.bytebuddy:byte-buddy-agent:jar:sources": -899388612,
-    "net.bytebuddy:byte-buddy:jar:sources": 1062390101,
-    "net.java.dev.jna:jna": -1916526385,
-    "net.java.dev.jna:jna-platform": 459618853,
-    "net.java.dev.jna:jna-platform:jar:sources": -2077304647,
-    "net.java.dev.jna:jna:jar:sources": 1518406952,
-    "net.minidev:json-smart": -1913865264,
-    "net.minidev:json-smart:jar:sources": 475976688,
-    "net.sf.jopt-simple:jopt-simple": 1531230776,
-    "net.sf.jopt-simple:jopt-simple:jar:sources": -1087190884,
-    "net.sourceforge.nekohtml:nekohtml": 589300296,
-    "net.sourceforge.nekohtml:nekohtml:jar:sources": 1455941061,
-    "org.antlr:ST4": 943007212,
-    "org.antlr:ST4:jar:sources": 1076411807,
-    "org.antlr:antlr": -10615566,
-    "org.antlr:antlr-runtime": -20800628,
-    "org.antlr:antlr-runtime:jar:sources": 1652502717,
-    "org.antlr:antlr:jar:sources": -1207538557,
-    "org.antlr:stringtemplate": -1826141279,
-    "org.antlr:stringtemplate:jar:sources": 831821310,
-    "org.apache.commons:commons-compress": -2083070990,
-    "org.apache.commons:commons-compress:jar:sources": 128824058,
-    "org.apache.commons:commons-lang3": 1593572986,
-    "org.apache.commons:commons-lang3:jar:sources": 1589181154,
-    "org.apache.commons:commons-math3": 1532637713,
-    "org.apache.commons:commons-math3:jar:sources": 1655744467,
-    "org.apache.commons:commons-text": -1205775100,
-    "org.apache.commons:commons-text:jar:sources": 1247506591,
-    "org.apache.httpcomponents:fluent-hc": 1857459163,
-    "org.apache.httpcomponents:fluent-hc:jar:sources": 1555727994,
-    "org.apache.httpcomponents:httpclient": -940371367,
-    "org.apache.httpcomponents:httpclient:jar:sources": 1069741198,
-    "org.apache.httpcomponents:httpcore": -279989236,
-    "org.apache.httpcomponents:httpcore:jar:sources": 1796128621,
-    "org.apache.james:apache-mime4j-core": 596731567,
-    "org.apache.james:apache-mime4j-core:jar:sources": 1221134342,
-    "org.apache.james:apache-mime4j-dom": 2052814931,
-    "org.apache.james:apache-mime4j-dom:jar:sources": 2056952874,
-    "org.apache.lucene:lucene-analysis-common": 1674710283,
-    "org.apache.lucene:lucene-analysis-common:jar:sources": 334029180,
-    "org.apache.lucene:lucene-backward-codecs": -508942990,
-    "org.apache.lucene:lucene-backward-codecs:jar:sources": 1999324390,
-    "org.apache.lucene:lucene-core": -1994692613,
-    "org.apache.lucene:lucene-core:jar:sources": -164995544,
-    "org.apache.lucene:lucene-misc": -739909715,
-    "org.apache.lucene:lucene-misc:jar:sources": -128984682,
-    "org.apache.lucene:lucene-queryparser": -1416736164,
-    "org.apache.lucene:lucene-queryparser:jar:sources": 1960943881,
-    "org.apache.mina:mina-core": 603384434,
-    "org.apache.mina:mina-core:jar:sources": 1763715353,
-    "org.apache.sshd:sshd-mina": 707703392,
-    "org.apache.sshd:sshd-mina:jar:sources": 458343698,
-    "org.apache.sshd:sshd-osgi": 1463287175,
-    "org.apache.sshd:sshd-osgi:jar:sources": -1934894876,
-    "org.apache.sshd:sshd-sftp": -1455178358,
-    "org.apache.sshd:sshd-sftp:jar:sources": 1450030982,
-    "org.asciidoctor:asciidoctorj": -943976727,
-    "org.asciidoctor:asciidoctorj:jar:sources": -1022891917,
-    "org.assertj:assertj-core": 1465577983,
-    "org.assertj:assertj-core:jar:sources": 1906472612,
-    "org.bouncycastle:bcpg-jdk18on": 726067526,
-    "org.bouncycastle:bcpg-jdk18on:jar:sources": 812126316,
-    "org.bouncycastle:bcpkix-jdk18on": 2070253037,
-    "org.bouncycastle:bcpkix-jdk18on:jar:sources": -1497475064,
-    "org.bouncycastle:bcprov-jdk18on": -1778261676,
-    "org.bouncycastle:bcprov-jdk18on:jar:sources": -1245673492,
-    "org.bouncycastle:bcutil-jdk18on": 86705488,
-    "org.bouncycastle:bcutil-jdk18on:jar:sources": -784396673,
-    "org.checkerframework:checker-compat-qual": -1678975214,
-    "org.checkerframework:checker-compat-qual:jar:sources": -673395382,
-    "org.checkerframework:checker-qual": -1657280421,
-    "org.checkerframework:checker-qual:jar:sources": 324669507,
-    "org.commonmark:commonmark": -1467575831,
-    "org.commonmark:commonmark-ext-autolink": -1808977749,
-    "org.commonmark:commonmark-ext-autolink:jar:sources": -1324197024,
-    "org.commonmark:commonmark-ext-gfm-strikethrough": 1872267513,
-    "org.commonmark:commonmark-ext-gfm-strikethrough:jar:sources": 1099767676,
-    "org.commonmark:commonmark-ext-gfm-tables": -435057552,
-    "org.commonmark:commonmark-ext-gfm-tables:jar:sources": -1247551792,
-    "org.commonmark:commonmark:jar:sources": -1275158082,
-    "org.eclipse.jetty.ee8:jetty-ee8-nested": -937999467,
-    "org.eclipse.jetty.ee8:jetty-ee8-nested:jar:sources": 1857707413,
-    "org.eclipse.jetty.ee8:jetty-ee8-security": -1357485120,
-    "org.eclipse.jetty.ee8:jetty-ee8-security:jar:sources": -946021443,
-    "org.eclipse.jetty.ee8:jetty-ee8-servlet": -1729254347,
-    "org.eclipse.jetty.ee8:jetty-ee8-servlet:jar:sources": 450880365,
-    "org.eclipse.jetty.toolchain:jetty-servlet-api": 380579650,
-    "org.eclipse.jetty.toolchain:jetty-servlet-api:jar:sources": 212656908,
-    "org.eclipse.jetty:jetty-http": -2051325092,
-    "org.eclipse.jetty:jetty-http:jar:sources": -882391521,
-    "org.eclipse.jetty:jetty-io": 1774579468,
-    "org.eclipse.jetty:jetty-io:jar:sources": -231665913,
-    "org.eclipse.jetty:jetty-jmx": -385762105,
-    "org.eclipse.jetty:jetty-jmx:jar:sources": -1468671741,
-    "org.eclipse.jetty:jetty-security": 1698636651,
-    "org.eclipse.jetty:jetty-security:jar:sources": 118665539,
-    "org.eclipse.jetty:jetty-server": -477112374,
-    "org.eclipse.jetty:jetty-server:jar:sources": -710016839,
-    "org.eclipse.jetty:jetty-servlet": -974397932,
-    "org.eclipse.jetty:jetty-servlet:jar:sources": 1281350506,
-    "org.eclipse.jetty:jetty-session": -891670874,
-    "org.eclipse.jetty:jetty-session:jar:sources": -351578476,
-    "org.eclipse.jetty:jetty-util": 2086696316,
-    "org.eclipse.jetty:jetty-util-ajax": -451213174,
-    "org.eclipse.jetty:jetty-util-ajax:jar:sources": 1686109257,
-    "org.eclipse.jetty:jetty-util:jar:sources": 958996619,
-    "org.hamcrest:hamcrest": -1550813651,
-    "org.hamcrest:hamcrest-core": -1198150244,
-    "org.hamcrest:hamcrest-core:jar:sources": -1576492927,
-    "org.hamcrest:hamcrest:jar:sources": -1807813747,
-    "org.jruby:jruby-complete": 407729900,
-    "org.jruby:jruby-complete:jar:sources": 554963637,
-    "org.jsoup:jsoup": 789840847,
-    "org.jsoup:jsoup:jar:sources": 1377510617,
-    "org.jspecify:jspecify": -797399878,
-    "org.jspecify:jspecify:jar:sources": 1011232509,
-    "org.mockito:mockito-core": -1882151935,
-    "org.mockito:mockito-core:jar:sources": 41691546,
-    "org.nibor.autolink:autolink": 1237374319,
-    "org.nibor.autolink:autolink:jar:sources": 1695391615,
-    "org.objenesis:objenesis": -1055367721,
-    "org.objenesis:objenesis:jar:sources": 707329220,
-    "org.openid4java:openid4java": 1656473252,
-    "org.openid4java:openid4java:jar:sources": 789962531,
-    "org.openjdk.jmh:jmh-core": -381500549,
-    "org.openjdk.jmh:jmh-core:jar:sources": -1014071512,
-    "org.openjdk.jmh:jmh-generator-annprocess": -1315975534,
-    "org.openjdk.jmh:jmh-generator-annprocess:jar:sources": 81479330,
-    "org.ow2.asm:asm": 1540593910,
-    "org.ow2.asm:asm-analysis": 1735619699,
-    "org.ow2.asm:asm-analysis:jar:sources": 609078251,
-    "org.ow2.asm:asm-commons": -231881389,
-    "org.ow2.asm:asm-commons:jar:sources": 1140590651,
-    "org.ow2.asm:asm-tree": 1288506580,
-    "org.ow2.asm:asm-tree:jar:sources": -1050091087,
-    "org.ow2.asm:asm-util": 247171895,
-    "org.ow2.asm:asm-util:jar:sources": 354115585,
-    "org.ow2.asm:asm:jar:sources": 511988412,
-    "org.roaringbitmap:RoaringBitmap": 506688526,
-    "org.roaringbitmap:RoaringBitmap:jar:sources": 1956160126,
-    "org.roaringbitmap:shims": -1352997269,
-    "org.roaringbitmap:shims:jar:sources": -981899312,
-    "org.slf4j:jcl-over-slf4j": 429539128,
-    "org.slf4j:jcl-over-slf4j:jar:sources": 619461888,
-    "org.slf4j:slf4j-api": -630423788,
-    "org.slf4j:slf4j-api:jar:sources": -1112457040,
-    "org.slf4j:slf4j-ext": -1747709512,
-    "org.slf4j:slf4j-ext:jar:sources": 2070817143,
-    "org.slf4j:slf4j-reload4j": -1999639452,
-    "org.slf4j:slf4j-reload4j:jar:sources": -1774817644,
-    "org.slf4j:slf4j-simple": -1220154160,
-    "org.slf4j:slf4j-simple:jar:sources": 334190933,
-    "org.tukaani:xz": 1514570375,
-    "org.tukaani:xz:jar:sources": -610773207,
-    "xerces:xercesImpl": -723395208,
-    "xerces:xercesImpl:jar:sources": -127516017
+    "antlr:antlr": -947608540,
+    "aopalliance:aopalliance": 1403132272,
+    "aopalliance:aopalliance:jar:sources": -1259601821,
+    "args4j:args4j": 208424838,
+    "args4j:args4j:jar:sources": -370825913,
+    "ch.qos.reload4j:reload4j": -523782034,
+    "ch.qos.reload4j:reload4j:jar:sources": -338991604,
+    "com.beust:jcommander": -1223620106,
+    "com.beust:jcommander:jar:sources": 1270399231,
+    "com.github.ben-manes.caffeine:caffeine": -1342502459,
+    "com.github.ben-manes.caffeine:caffeine:jar:sources": 1780037606,
+    "com.github.ben-manes.caffeine:guava": 1717885520,
+    "com.github.ben-manes.caffeine:guava:jar:sources": 1590068668,
+    "com.github.rholder:guava-retrying": -789983219,
+    "com.github.rholder:guava-retrying:jar:sources": 2030887181,
+    "com.google.auto.factory:auto-factory": 2025609377,
+    "com.google.auto.factory:auto-factory:jar:sources": -861333607,
+    "com.google.auto.service:auto-service-annotations": 195984873,
+    "com.google.auto.service:auto-service-annotations:jar:sources": -902556490,
+    "com.google.auto.value:auto-value": -1504415167,
+    "com.google.auto.value:auto-value-annotations": 641752776,
+    "com.google.auto.value:auto-value-annotations:jar:sources": -2083311588,
+    "com.google.auto.value:auto-value:jar:sources": -30292358,
+    "com.google.auto:auto-common": -195836273,
+    "com.google.auto:auto-common:jar:sources": -2100626169,
+    "com.google.code.findbugs:jsr305": -998441376,
+    "com.google.code.findbugs:jsr305:jar:sources": -640520676,
+    "com.google.code.gson:gson": -2014404431,
+    "com.google.code.gson:gson:jar:sources": 935710753,
+    "com.google.common.html.types:types": 1171780099,
+    "com.google.common.html.types:types:jar:sources": -1323749402,
+    "com.google.errorprone:error_prone_annotations": 804114225,
+    "com.google.errorprone:error_prone_annotations:jar:sources": -2115535816,
+    "com.google.flogger:flogger": -1346444653,
+    "com.google.flogger:flogger-log4j-backend": 661355127,
+    "com.google.flogger:flogger-log4j-backend:jar:sources": 1768481764,
+    "com.google.flogger:flogger-system-backend": 1919472289,
+    "com.google.flogger:flogger-system-backend:jar:sources": 2143617050,
+    "com.google.flogger:flogger:jar:sources": -1464119363,
+    "com.google.flogger:google-extensions": 1202868209,
+    "com.google.flogger:google-extensions:jar:sources": -1667103726,
+    "com.google.gitiles:blame-cache": 2017928431,
+    "com.google.gitiles:blame-cache:jar:sources": 467275313,
+    "com.google.gitiles:gitiles-servlet": 1918113734,
+    "com.google.gitiles:gitiles-servlet:jar:sources": -158011882,
+    "com.google.guava:failureaccess": 1715931538,
+    "com.google.guava:failureaccess:jar:sources": 1303858893,
+    "com.google.guava:guava": 555169272,
+    "com.google.guava:guava-testlib": 1085873063,
+    "com.google.guava:guava-testlib:jar:sources": 1351030420,
+    "com.google.guava:guava:jar:sources": 1246910673,
+    "com.google.guava:listenablefuture": 1079558157,
+    "com.google.inject.extensions:guice-assistedinject": -205980308,
+    "com.google.inject.extensions:guice-assistedinject:jar:sources": 550961491,
+    "com.google.inject.extensions:guice-servlet": -325370690,
+    "com.google.inject.extensions:guice-servlet:jar:sources": -828654042,
+    "com.google.inject:guice:jar:classes": 1808670374,
+    "com.google.inject:guice:jar:sources": -2060215316,
+    "com.google.j2objc:j2objc-annotations": 1702790440,
+    "com.google.j2objc:j2objc-annotations:jar:sources": -1254484583,
+    "com.google.jimfs:jimfs": 375012684,
+    "com.google.jimfs:jimfs:jar:sources": -555304721,
+    "com.google.jsinterop:jsinterop-annotations": 1636460091,
+    "com.google.jsinterop:jsinterop-annotations:jar:sources": 694679492,
+    "com.google.protobuf:protobuf-java": 1331070778,
+    "com.google.protobuf:protobuf-java:jar:sources": 133270104,
+    "com.google.template:soy": 1170788493,
+    "com.google.template:soy:jar:sources": 895044971,
+    "com.google.truth.extensions:truth-java8-extension": 766384514,
+    "com.google.truth.extensions:truth-java8-extension:jar:sources": 1257445795,
+    "com.google.truth.extensions:truth-liteproto-extension": -1774954418,
+    "com.google.truth.extensions:truth-liteproto-extension:jar:sources": -754757455,
+    "com.google.truth.extensions:truth-proto-extension": -180673672,
+    "com.google.truth.extensions:truth-proto-extension:jar:sources": -171856482,
+    "com.google.truth:truth": 2133252626,
+    "com.google.truth:truth:jar:sources": 494258718,
+    "com.googlecode.javaewah:JavaEWAH": 1782327838,
+    "com.googlecode.javaewah:JavaEWAH:jar:sources": -77762967,
+    "com.googlecode.prolog-cafe:prolog-cafeteria": 1676859953,
+    "com.googlecode.prolog-cafe:prolog-cafeteria:jar:sources": -2075023034,
+    "com.googlecode.prolog-cafe:prolog-compiler": 796735585,
+    "com.googlecode.prolog-cafe:prolog-compiler:jar:sources": 2069727160,
+    "com.googlecode.prolog-cafe:prolog-io": 1604599374,
+    "com.googlecode.prolog-cafe:prolog-io:jar:sources": 2063038283,
+    "com.googlecode.prolog-cafe:prolog-runtime": -776672418,
+    "com.googlecode.prolog-cafe:prolog-runtime:jar:sources": 1445213194,
+    "com.h2database:h2": 1660949213,
+    "com.h2database:h2:jar:sources": 446387355,
+    "com.ibm.icu:icu4j": -1725310050,
+    "com.ibm.icu:icu4j:jar:sources": 163265109,
+    "com.icegreen:greenmail": 945965666,
+    "com.icegreen:greenmail:jar:sources": -245613146,
+    "com.jcraft:jsch": 1084501210,
+    "com.jcraft:jsch:jar:sources": -586408740,
+    "com.jcraft:jzlib": -1423321085,
+    "com.jcraft:jzlib:jar:sources": 1440827148,
+    "com.ryanharter.auto.value:auto-value-gson-extension": -1071085858,
+    "com.ryanharter.auto.value:auto-value-gson-extension:jar:sources": -670606081,
+    "com.ryanharter.auto.value:auto-value-gson-factory": -586162268,
+    "com.ryanharter.auto.value:auto-value-gson-factory:jar:sources": -543638406,
+    "com.ryanharter.auto.value:auto-value-gson-runtime": 1330169783,
+    "com.ryanharter.auto.value:auto-value-gson-runtime:jar:sources": 706425522,
+    "com.squareup:javapoet": -2135371934,
+    "com.squareup:javapoet:jar:sources": -54658965,
+    "com.sun.mail:javax.mail": -1925012432,
+    "com.sun.mail:javax.mail:jar:sources": 608124446,
+    "com.vladsch.flexmark:flexmark-all:jar:lib": 1454723856,
+    "commons-codec:commons-codec": -28659539,
+    "commons-codec:commons-codec:jar:sources": -499809306,
+    "commons-dbcp:commons-dbcp": 1878088686,
+    "commons-dbcp:commons-dbcp:jar:sources": 699520492,
+    "commons-io:commons-io": 1072619124,
+    "commons-io:commons-io:jar:sources": -238571367,
+    "commons-net:commons-net": 2103840351,
+    "commons-net:commons-net:jar:sources": -729248524,
+    "commons-pool:commons-pool": -212519749,
+    "commons-pool:commons-pool:jar:sources": 1134824914,
+    "commons-validator:commons-validator": 298719241,
+    "commons-validator:commons-validator:jar:sources": 1666065747,
+    "dk.brics:automaton": -509279352,
+    "dk.brics:automaton:jar:sources": 1035320481,
+    "eu.medsea.mimeutil:mime-util": -188093271,
+    "io.dropwizard.metrics:metrics-core": 740461012,
+    "io.dropwizard.metrics:metrics-core:jar:sources": -1988318320,
+    "io.github.java-diff-utils:java-diff-utils": -1180802694,
+    "io.github.java-diff-utils:java-diff-utils:jar:sources": 1551226070,
+    "io.sweers.autotransient:autotransient": 1808939417,
+    "io.sweers.autotransient:autotransient:jar:sources": -703216994,
+    "jakarta.inject:jakarta.inject-api": 188991469,
+    "jakarta.inject:jakarta.inject-api:jar:sources": -1584622703,
+    "jakarta.servlet:jakarta.servlet-api": -1421863120,
+    "jakarta.servlet:jakarta.servlet-api:jar:sources": -2084357823,
+    "javax.activation:activation": 1998212823,
+    "javax.activation:activation:jar:sources": 2062811499,
+    "javax.inject:javax.inject": 698155243,
+    "javax.inject:javax.inject:jar:sources": 1222576539,
+    "javax.servlet:javax.servlet-api": 735808080,
+    "javax.servlet:javax.servlet-api:jar:sources": -2015355058,
+    "junit:junit": -1256429642,
+    "junit:junit:jar:sources": 940567721,
+    "net.bytebuddy:byte-buddy": -2144538556,
+    "net.bytebuddy:byte-buddy-agent": -1307562154,
+    "net.bytebuddy:byte-buddy-agent:jar:sources": 2038081020,
+    "net.bytebuddy:byte-buddy:jar:sources": 1602176187,
+    "net.java.dev.jna:jna": 1622514527,
+    "net.java.dev.jna:jna-platform": 1756885266,
+    "net.java.dev.jna:jna-platform:jar:sources": -507721531,
+    "net.java.dev.jna:jna:jar:sources": -902150228,
+    "net.minidev:json-smart": 1099098947,
+    "net.minidev:json-smart:jar:sources": 1458315043,
+    "net.sf.jopt-simple:jopt-simple": -1677351973,
+    "net.sf.jopt-simple:jopt-simple:jar:sources": 1072431863,
+    "net.sourceforge.nekohtml:nekohtml": 1220479218,
+    "net.sourceforge.nekohtml:nekohtml:jar:sources": -689850578,
+    "org.antlr:ST4": 1197671269,
+    "org.antlr:ST4:jar:sources": 494740756,
+    "org.antlr:antlr": -1604354866,
+    "org.antlr:antlr-runtime": -1636816377,
+    "org.antlr:antlr-runtime:jar:sources": 290715958,
+    "org.antlr:antlr:jar:sources": -1910959184,
+    "org.antlr:stringtemplate": -1632674608,
+    "org.antlr:stringtemplate:jar:sources": -562160043,
+    "org.apache.commons:commons-compress": 2043911487,
+    "org.apache.commons:commons-compress:jar:sources": -1888643111,
+    "org.apache.commons:commons-lang3": -850748327,
+    "org.apache.commons:commons-lang3:jar:sources": 1059297009,
+    "org.apache.commons:commons-math3": -1383243934,
+    "org.apache.commons:commons-math3:jar:sources": -2132756896,
+    "org.apache.commons:commons-text": -549439287,
+    "org.apache.commons:commons-text:jar:sources": 400790036,
+    "org.apache.httpcomponents:fluent-hc": -1791063366,
+    "org.apache.httpcomponents:fluent-hc:jar:sources": -1265691559,
+    "org.apache.httpcomponents:httpclient": -930110091,
+    "org.apache.httpcomponents:httpclient:jar:sources": -779528763,
+    "org.apache.httpcomponents:httpcore": 67453319,
+    "org.apache.httpcomponents:httpcore:jar:sources": 2134002822,
+    "org.apache.james:apache-mime4j-core": 1172767236,
+    "org.apache.james:apache-mime4j-core:jar:sources": 1540423501,
+    "org.apache.james:apache-mime4j-dom": -704995319,
+    "org.apache.james:apache-mime4j-dom:jar:sources": -1311932247,
+    "org.apache.lucene:lucene-analysis-common": 966070693,
+    "org.apache.lucene:lucene-analysis-common:jar:sources": -1260454377,
+    "org.apache.lucene:lucene-backward-codecs": -1819747764,
+    "org.apache.lucene:lucene-backward-codecs:jar:sources": 2116900461,
+    "org.apache.lucene:lucene-core": -633809608,
+    "org.apache.lucene:lucene-core:jar:sources": 856971755,
+    "org.apache.lucene:lucene-misc": 882591687,
+    "org.apache.lucene:lucene-misc:jar:sources": 1204085693,
+    "org.apache.lucene:lucene-queryparser": 2111539382,
+    "org.apache.lucene:lucene-queryparser:jar:sources": 1992621418,
+    "org.apache.mina:mina-core": -584591076,
+    "org.apache.mina:mina-core:jar:sources": 71382131,
+    "org.apache.sshd:sshd-mina": -36716477,
+    "org.apache.sshd:sshd-mina:jar:sources": 97528844,
+    "org.apache.sshd:sshd-osgi": 1810958617,
+    "org.apache.sshd:sshd-osgi:jar:sources": 710209222,
+    "org.apache.sshd:sshd-sftp": -320832840,
+    "org.apache.sshd:sshd-sftp:jar:sources": -1268961386,
+    "org.asciidoctor:asciidoctorj": 1685789893,
+    "org.asciidoctor:asciidoctorj:jar:sources": 2091708864,
+    "org.assertj:assertj-core": -59722598,
+    "org.assertj:assertj-core:jar:sources": -697161745,
+    "org.bouncycastle:bcpg-jdk18on": -1258294405,
+    "org.bouncycastle:bcpg-jdk18on:jar:sources": -1733408473,
+    "org.bouncycastle:bcpkix-jdk18on": -294727450,
+    "org.bouncycastle:bcpkix-jdk18on:jar:sources": 1412420619,
+    "org.bouncycastle:bcprov-jdk18on": 1743462207,
+    "org.bouncycastle:bcprov-jdk18on:jar:sources": 1198104999,
+    "org.bouncycastle:bcutil-jdk18on": 968717615,
+    "org.bouncycastle:bcutil-jdk18on:jar:sources": -1508579276,
+    "org.checkerframework:checker-compat-qual": -1467964223,
+    "org.checkerframework:checker-compat-qual:jar:sources": 187825033,
+    "org.checkerframework:checker-qual": -739034920,
+    "org.checkerframework:checker-qual:jar:sources": 1223055344,
+    "org.commonmark:commonmark": 965353610,
+    "org.commonmark:commonmark-ext-autolink": -1259954266,
+    "org.commonmark:commonmark-ext-autolink:jar:sources": 2044805555,
+    "org.commonmark:commonmark-ext-gfm-strikethrough": -523313366,
+    "org.commonmark:commonmark-ext-gfm-strikethrough:jar:sources": 992870423,
+    "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.ee11:jetty-ee11-servlet": -2443254,
+    "org.eclipse.jetty.ee11:jetty-ee11-servlet:jar:sources": -1546113901,
+    "org.eclipse.jetty.ee8:jetty-ee8-nested": -1915352225,
+    "org.eclipse.jetty.ee8:jetty-ee8-nested:jar:sources": 673934800,
+    "org.eclipse.jetty.ee8:jetty-ee8-security": -1918552702,
+    "org.eclipse.jetty.ee8:jetty-ee8-security:jar:sources": 114520187,
+    "org.eclipse.jetty.ee8:jetty-ee8-servlet": 57123647,
+    "org.eclipse.jetty.ee8:jetty-ee8-servlet:jar:sources": 500172952,
+    "org.eclipse.jetty.toolchain:jetty-servlet-api": 1364182673,
+    "org.eclipse.jetty.toolchain:jetty-servlet-api:jar:sources": 736604807,
+    "org.eclipse.jetty:jetty-http": -2005285297,
+    "org.eclipse.jetty:jetty-http:jar:sources": 1510111824,
+    "org.eclipse.jetty:jetty-io": 1963881320,
+    "org.eclipse.jetty:jetty-io:jar:sources": -219635344,
+    "org.eclipse.jetty:jetty-jmx": 974624101,
+    "org.eclipse.jetty:jetty-jmx:jar:sources": -733987429,
+    "org.eclipse.jetty:jetty-security": -628512589,
+    "org.eclipse.jetty:jetty-security:jar:sources": 730204298,
+    "org.eclipse.jetty:jetty-server": -1113673878,
+    "org.eclipse.jetty:jetty-server:jar:sources": -1325429404,
+    "org.eclipse.jetty:jetty-session": -240475595,
+    "org.eclipse.jetty:jetty-session:jar:sources": -407525695,
+    "org.eclipse.jetty:jetty-util": 1729944958,
+    "org.eclipse.jetty:jetty-util-ajax": 1312600683,
+    "org.eclipse.jetty:jetty-util-ajax:jar:sources": -2027361402,
+    "org.eclipse.jetty:jetty-util:jar:sources": -1809018264,
+    "org.hamcrest:hamcrest": 1282317766,
+    "org.hamcrest:hamcrest-core": 649657847,
+    "org.hamcrest:hamcrest-core:jar:sources": -1646511374,
+    "org.hamcrest:hamcrest:jar:sources": 1394599014,
+    "org.jruby:jruby-complete": -1216064857,
+    "org.jruby:jruby-complete:jar:sources": -308329410,
+    "org.jsoup:jsoup": 885756132,
+    "org.jsoup:jsoup:jar:sources": -2058254438,
+    "org.jspecify:jspecify": 117231129,
+    "org.jspecify:jspecify:jar:sources": -2134060298,
+    "org.mockito:mockito-core": 493796464,
+    "org.mockito:mockito-core:jar:sources": 1900207417,
+    "org.nibor.autolink:autolink": -443901116,
+    "org.nibor.autolink:autolink:jar:sources": -1863403724,
+    "org.objenesis:objenesis": 1536526812,
+    "org.objenesis:objenesis:jar:sources": 321662415,
+    "org.openid4java:openid4java": -469605553,
+    "org.openid4java:openid4java:jar:sources": -1312563952,
+    "org.openjdk.jmh:jmh-core": 262505124,
+    "org.openjdk.jmh:jmh-core:jar:sources": 814651627,
+    "org.openjdk.jmh:jmh-generator-annprocess": 554503664,
+    "org.openjdk.jmh:jmh-generator-annprocess:jar:sources": -59690191,
+    "org.ow2.asm:asm": 1614653533,
+    "org.ow2.asm:asm-analysis": 865618371,
+    "org.ow2.asm:asm-analysis:jar:sources": 860015432,
+    "org.ow2.asm:asm-commons": 33716409,
+    "org.ow2.asm:asm-commons:jar:sources": 899206392,
+    "org.ow2.asm:asm-tree": -1173653421,
+    "org.ow2.asm:asm-tree:jar:sources": 590990274,
+    "org.ow2.asm:asm-util": 1667785410,
+    "org.ow2.asm:asm-util:jar:sources": 902318962,
+    "org.ow2.asm:asm:jar:sources": 340034775,
+    "org.roaringbitmap:RoaringBitmap": -1389417978,
+    "org.roaringbitmap:RoaringBitmap:jar:sources": -144719403,
+    "org.roaringbitmap:shims": -1535336248,
+    "org.roaringbitmap:shims:jar:sources": -422658237,
+    "org.slf4j:jcl-over-slf4j": 1113513525,
+    "org.slf4j:jcl-over-slf4j:jar:sources": 494484499,
+    "org.slf4j:slf4j-api": 500377983,
+    "org.slf4j:slf4j-api:jar:sources": 1751644771,
+    "org.slf4j:slf4j-ext": -1625091915,
+    "org.slf4j:slf4j-ext:jar:sources": 194861628,
+    "org.slf4j:slf4j-reload4j": -1779685465,
+    "org.slf4j:slf4j-reload4j:jar:sources": -1558574593,
+    "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,
+    "xerces:xercesImpl:jar:sources": -1057334748
   },
   "artifacts": {
     "antlr:antlr": {
@@ -564,31 +567,31 @@
     },
     "com.google.auto.factory:auto-factory": {
       "shasums": {
-        "jar": "d59fb7ada5962a480abf0b81d4d2a14a2952f17c026732359af8b585e531c16c",
-        "sources": "c6098f8976b8833cf40edc36d1e3f7cd5cbb474c018185650b5ca4e24e713e6a"
+        "jar": "9b4505cd7a60574d59386672c1d51d6154b803c892677eb909cf4155ebee771f",
+        "sources": "4f46cc87784c2339d103c3b3d4e19bc03dd3ecca018ea931c61c4b63a4829616"
       },
-      "version": "1.0.1"
+      "version": "1.1.0"
     },
     "com.google.auto.service:auto-service-annotations": {
       "shasums": {
-        "jar": "c7bec54b7b5588b5967e870341091c5691181d954cf2039f1bf0a6eeb837473b",
-        "sources": "b013ca159b0fea3a0041d3d5fbb3b7e49a819da80a172a01fb17dd28fd98e72b"
+        "jar": "cb5667036f75e9682b493b8b47adbe391bcff72b9d3e16c52ae725f514665af2",
+        "sources": "10ca2b6c041ec8a7dc07d391ab792a55a4d6518d8067c8e63b7deda92e1de9e9"
       },
-      "version": "1.0.1"
+      "version": "1.1.0"
     },
     "com.google.auto.value:auto-value": {
       "shasums": {
-        "jar": "aaf8d637bfed3c420436b9facf1b7a88d12c8785374e4202382783005319c2c3",
-        "sources": "4bff06fe077d68f964bd5e05f020ed78fd7870730441e403a2eb306360c4890a"
+        "jar": "0c556370ca3d072ba7dec6b37751ccad0e4804b6f3390cc9acb800d821dd9c0a",
+        "sources": "a9eb12d149bfbcfc7fb670689d67dea3440b0ecaea15829e2953a03a1503b29c"
       },
-      "version": "1.11.0"
+      "version": "1.11.1"
     },
     "com.google.auto.value:auto-value-annotations": {
       "shasums": {
-        "jar": "5a055ce4255333b3346e1a8703da5bf8ff049532286fdcd31712d624abe111dd",
-        "sources": "d7941e5f19bb38afcfa85350d57e5245856c23c98c2bbe32f6d31b5577f2bc33"
+        "jar": "6c61a11420a5cddf3313888e9d335d5eedfbeb9a8da26591470fbfe61f5bf859",
+        "sources": "2bc2b5c2f8b1dbe3c05673750f16ec51db2ef9bf54be942c278e16035e5dca21"
       },
-      "version": "1.11.0"
+      "version": "1.11.1"
     },
     "com.google.auto:auto-common": {
       "shasums": {
@@ -710,7 +713,7 @@
     },
     "com.google.inject:guice": {
       "shasums": {
-        "jar": "b4d4f7ec5e8fc17b4f98dee9d3f6cf6ae3ae13e2e5ed4b2f7bbf09bc4bb675d5",
+        "classes": "ff084ab91a2f00745d2535883e4c2e15f40970f93887c2df50504897119cfc3d",
         "sources": "656b82a85535ada22d251fbc4ab3e786e66997510d03325d168bc193c2148c09"
       },
       "version": "6.0.0"
@@ -738,10 +741,10 @@
     },
     "com.google.protobuf:protobuf-java": {
       "shasums": {
-        "jar": "ae0459030b54a6f3058ad349aa01fc2edcbb2d4a84b73699675bbd125df2e96d",
-        "sources": "240db630888d78b6d67c0977416c8a48dd4bce42fb48424d054b128b72420b20"
+        "jar": "400621a8f0aac52fab7ec88f492edd356f5a4355caa8f92028f217a8b5e4ac3b",
+        "sources": "63798a62a5f62575c814b92b1b951252bfd379ebe8207610db41622e03b007dc"
       },
-      "version": "4.34.1"
+      "version": "4.35.0"
     },
     "com.google.template:soy": {
       "shasums": {
@@ -972,6 +975,13 @@
       },
       "version": "2.0.1"
     },
+    "jakarta.servlet:jakarta.servlet-api": {
+      "shasums": {
+        "jar": "8a31f465f3593bf2351531a5c952014eb839da96a605b5825b93dd54714c48c4",
+        "sources": "6eb958543e0548bb93d2519e40224d13c8003b10cc615b5652bfd9899350bfb4"
+      },
+      "version": "6.1.0"
+    },
     "javax.activation:activation": {
       "shasums": {
         "jar": "ae475120e9fcd99b4b00b38329bd61cdc5eb754eee03fe66c01f50e137724f99",
@@ -1002,31 +1012,31 @@
     },
     "net.bytebuddy:byte-buddy": {
       "shasums": {
-        "jar": "227d3e0ad51915809143f6a744ac0f4cb21f03214dab28e52ee7007c5ad7e9d9",
-        "sources": "8529b9ec93e698165dde6fddfd57e0820bb8f6d17f91ada1d8c525563636dc4d"
+        "jar": "e32f454c2c1f4aca982f9ec764ed892d9a6eee7e8a77f435cbdd180f6ffdb821",
+        "sources": "7f38a50fa7406b61d867a2c81a89610bb816de2fa28696b4a54fcaa8d52530a1"
       },
-      "version": "1.18.8"
+      "version": "1.18.11"
     },
     "net.bytebuddy:byte-buddy-agent": {
       "shasums": {
-        "jar": "e303594d597de090abcb54580aa788478fc5e58f259e6ae914a1115f207c43af",
-        "sources": "881b5f123cefa2afc4ed89f8662da3ef9975e3a6e00f8f563b0470caf34b9511"
+        "jar": "780b3601c34150ab9fb822673c472f7229c1041e8e2a12facf6a5de9ba7a2d64",
+        "sources": "30bbf5860aa58e4740b352d2c4c15fe2428a2019e81dccd25c8e3937a0a2a0a3"
       },
-      "version": "1.18.8"
+      "version": "1.18.11"
     },
     "net.java.dev.jna:jna": {
       "shasums": {
-        "jar": "260c4b1e22b1db9e110ee441c4f13ce115f841fa48c41d78750986214b395557",
-        "sources": "0b9224e215b3c6a464959e3f994ddd64c14d46fb4014facd6afa1cc18e469466"
+        "jar": "4fb141dd8ef6b0585ffceea4bc49602fbc6312fa977e2c488794ea3e6aafecae",
+        "sources": "0136f719ddd91d92d2368aa2c59a0c436810ac0b9c29024044383758b0e99786"
       },
-      "version": "5.18.1"
+      "version": "5.19.1"
     },
     "net.java.dev.jna:jna-platform": {
       "shasums": {
-        "jar": "ad14c1b1ec4f43d396231219dfa635ebf828f738eac9f890ea1bc07795892d9a",
-        "sources": "5ffcac4b35114c6539ab9485592a90153ddeefb60e675dd9e8a2ee24e54ec1bc"
+        "jar": "3b3864f5b449e9c3c24b16861524b622b086563f44e0cd8384c8efc5a6052f82",
+        "sources": "8288e781424a34b0478928be5664b01ace168e80608178d37d02464041528940"
       },
-      "version": "5.18.1"
+      "version": "5.19.1"
     },
     "net.minidev:json-smart": {
       "shasums": {
@@ -1177,31 +1187,31 @@
     },
     "org.apache.mina:mina-core": {
       "shasums": {
-        "jar": "39b2dfc8e84380bf7adab657d3d5e1625cb6592a885ebdb854ec5c6f7a3ec88d",
-        "sources": "6c7823b8ed5a8d3511b8fb7ba6166ab825a45784741cd15c9991a75e54ad0dba"
+        "jar": "09b4b5e416834e5281dd0dfccac1a10413d6f42c89f133b1c43641e34f33e840",
+        "sources": "ea8192924cc0fd86742be74097c53edbce68232e9b27099ced307912e53ef1ea"
       },
-      "version": "2.2.4"
+      "version": "2.2.9"
     },
     "org.apache.sshd:sshd-mina": {
       "shasums": {
-        "jar": "9ac956852adadcc5857e702bbc3accd0b4f22dc95b4d32a3c3e7d842478cabe6",
-        "sources": "763d0820dfc362c8ae6cfa5b10a49b349d855f60e5f792dad0a0497c13dc2f75"
+        "jar": "da1088d16ba7953444b7d9aefcfc60c9b5e000bf26542d4c3ed0573f692bf14a",
+        "sources": "a8e548e09d8744effe88d83dcd55faa87dc14c37f8a0a595b4c6af3b20e558aa"
       },
-      "version": "2.17.1"
+      "version": "2.19.0"
     },
     "org.apache.sshd:sshd-osgi": {
       "shasums": {
-        "jar": "77c7c19d86dd59c63cedc9fea79708df264ea779c30076ce5dcfc51dc941a78b",
-        "sources": "b51cb4bc79d4b63b5ba1f72bb8e71b24f85597f1d1a8de4f49b5adb452d3a6cb"
+        "jar": "0b6b9371c6a97430390b84c23c58f4470927a747e74704a0ca7d19a09a66fe37",
+        "sources": "d11b69618dba155f075571cbd9b21741953c27dfa9a1af09b4024f2183226abe"
       },
-      "version": "2.17.1"
+      "version": "2.19.0"
     },
     "org.apache.sshd:sshd-sftp": {
       "shasums": {
-        "jar": "84727e3ac45458e1efde1058269964e2526aa3d520584870ba007adb20534b3c",
-        "sources": "6282c3628c62cfcec6578611b0c106f206fc418385b06b899cb8fb79aece5200"
+        "jar": "a3ad30d2feba9fad589c7ad9117fcd9d332823697d59eb424fe2902f4d805575",
+        "sources": "e26b7df972cead5c529bca6ab3e782c27dd43b323f9a54f98d9e23d4f5d03c30"
       },
-      "version": "2.17.1"
+      "version": "2.19.0"
     },
     "org.asciidoctor:asciidoctorj": {
       "shasums": {
@@ -1287,26 +1297,33 @@
       },
       "version": "0.24.0"
     },
+    "org.eclipse.jetty.ee11:jetty-ee11-servlet": {
+      "shasums": {
+        "jar": "f88e19a29f4e46322df60fe43d5ac4d852edefad923e8c38ef17ff14dc522e84",
+        "sources": "345f50f1a2937968934f354134cf4dd7e2b94bbfec36a177bb015ba675bd345c"
+      },
+      "version": "12.1.11"
+    },
     "org.eclipse.jetty.ee8:jetty-ee8-nested": {
       "shasums": {
-        "jar": "876fd83d52002d26ecb1a145ed7ae2397742a77c5e8ea9c6d309071b41cbb59c",
-        "sources": "c4f2633f8501679b59a0156cd64a6b49f34c27abf9896b6113f45fa7f6ae7d4a"
+        "jar": "b6b4db33b8894688a08f23aba9eee1bc951c66be12a43095f2811121a54018d8",
+        "sources": "2ba3403fb5daf6eaa10e4c3e59fa859e35e4451593d0afd681c2d57b8f6056e9"
       },
-      "version": "12.1.8"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty.ee8:jetty-ee8-security": {
       "shasums": {
-        "jar": "5643ebb9781e97b4ebc5096bea9533fefdeef535b1afa8b8f2c7524901a26dbe",
-        "sources": "e29ee9d16fcbaa271c12bb43ee39b29b2c04e1af5087aabf8d1cd18754175eef"
+        "jar": "9f165768c7f7b7a02c7631b38d898f7c16a729b78307c5c3d5940f5de7ee955d",
+        "sources": "7269d79523195225aab585f0bd799914b4d6a9d4746fd71758bb509e6c9b356e"
       },
-      "version": "12.1.8"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty.ee8:jetty-ee8-servlet": {
       "shasums": {
-        "jar": "183551aaff43938c5fc0fa649b62521b279eacbd53b3e5ee692cb1a3c08a6f8e",
-        "sources": "c0bb488146c42812a19c6f8e4c9f43aa55c5cf1c700bfcd2400c0493279159c5"
+        "jar": "82bd9eed66b147f243231a164c1157d3dd72cf2d05ae9029e8b12aff9df25fec",
+        "sources": "520e666d8dc4646ccd4b274946278322a393742f54f29d9911be8f46d087c54e"
       },
-      "version": "12.1.8"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty.toolchain:jetty-servlet-api": {
       "shasums": {
@@ -1317,66 +1334,59 @@
     },
     "org.eclipse.jetty:jetty-http": {
       "shasums": {
-        "jar": "02c6514977f0051dfdecf8d0799acf7a88fd8008a5fd9320a92f2e5db45d297b",
-        "sources": "1851f55b408241a6ae692730dd9bda9d1ecf7f0be6a9ccc471affa5bf8d07b9c"
+        "jar": "0e204f4c71c5d7e138949d50bb7fb15766840ecea6ca24513fb92ee34b363f55",
+        "sources": "1410d5686da1b63ad5d290cdd09b27bd4dfb1a563619a12ef02f63ad8c174eea"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty:jetty-io": {
       "shasums": {
-        "jar": "f6246a2cf0abcee7f0971217c0ce4cd30d8ce15a91530363457113907ab38690",
-        "sources": "f21960b5fe18c1fa4281aa0ba1ce8f2f7d4d8f00e64e08d536b6d6577ca92489"
+        "jar": "0534454eb445263a8577200f7bd009153d36f3a81ef1543e79ab551846ee3b94",
+        "sources": "d7298bbee4ebde8b35a6566212c9b04c9b19ac9dff7b84eb9a9109ddde805fe9"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty:jetty-jmx": {
       "shasums": {
-        "jar": "24b24e205b7f1a7812c781e95ac6154b044e09abf0d13432777fc0e7fbd2f4ac",
-        "sources": "5627d14d6d36d68f14a2765adfafe18852099e19821fe73a68adb93ed8819eb5"
+        "jar": "1207fefdedd9f690d3ac37f1408af6e41f4d9d60a00491e71b3b7137ef95da6f",
+        "sources": "29621d7f6da9909f9214434fef0644c1b5ff20c7d34bc4084daf3de6f1700e86"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty:jetty-security": {
       "shasums": {
-        "jar": "af923d4f395a73bf8ddcb754f42d7617c6b7055e37e5a6b625ed894f73107ae9",
-        "sources": "0f8718fe938f0c8a5c3098129b67d13381c1fef0d56765627ccc2e017fc2654b"
+        "jar": "6d21a7cbfd1915735cab6638011998f881161fdeff94936ee8f71e37d3528e79",
+        "sources": "bd00bca2c71ba594de92e10215c705493cbb0fc79149069c6db643911715c9b9"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty:jetty-server": {
       "shasums": {
-        "jar": "ba957ae07da647023cfa52c923732aea1c67f5273a594cee1863365dfebb9a02",
-        "sources": "089098dce0a947401a52bd00056427e1a2e348a71340a00e496c3139c14bafcb"
+        "jar": "772f84baf043bbc34edd99de728f3c0b2de642c400dde0fefa763e40de2ea583",
+        "sources": "433739222a745d1a4e14c71b78601065caac517bf307b44f5f85b1cd83da48ae"
       },
-      "version": "9.4.57.v20241219"
-    },
-    "org.eclipse.jetty:jetty-servlet": {
-      "shasums": {
-        "jar": "c5e9517974dec9e4606b2d810f4995ea81091b1e24bd9640cb45d8b2aefd722c",
-        "sources": "d3214106ebbfa9034aa041ea26caa080a99c494ae6275f88c95bc63796bc0367"
-      },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty:jetty-session": {
       "shasums": {
-        "jar": "ebe84dd41942d7adda5f7d1304095d274e7915999451d848e887cbd409f947ea",
-        "sources": "3741c771d7204c47d8e72ae5b6e834ab4710c9fe6118d4777bfd910a17bb847f"
+        "jar": "d00db0879d2dde6bc8742c3050e995314c5d423e20ca63601cf91add2547a381",
+        "sources": "b86814b8efcff1b2a79bc3ebdd9e02851d131692f2b0e7ab7b9735bbfa86d5bf"
       },
-      "version": "12.1.8"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty:jetty-util": {
       "shasums": {
-        "jar": "6ccbf678716778e316cc097d8aada4fe2a2e16c0bbfd8a1763204d6724b423f4",
-        "sources": "77d5935c637276d08da2e1141a7fe4d9db4a2d072b6b418b625b261009d0cb4c"
+        "jar": "8a8d7e063cf312b044927f817b09ad7a4517162d747f7ff891a414e8ef476c26",
+        "sources": "a1cf0445b69a53200633bc8b264cfd88a2927ce4c041048649666ad7cddaaacf"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.11"
     },
     "org.eclipse.jetty:jetty-util-ajax": {
       "shasums": {
-        "jar": "67af50dd7714803b1bcdfabac181dd8b279d0ba6ba7fd27ea80c5b2099016542",
-        "sources": "19888b386e9d4e81c81ce94465e8263680e1abcd83f324ba1992d7ded099abad"
+        "jar": "2fc1756acdde0bdbc44c693f2c9f5801d7843ccf823b25bd65e6fc52ed77d4f5",
+        "sources": "501f94d01324e7455f365e471acf3c5a3ad6c7c11ceeacf9514f3bc60cdc59e7"
       },
-      "version": "9.4.57.v20241219"
+      "version": "12.1.11"
     },
     "org.hamcrest:hamcrest": {
       "shasums": {
@@ -1534,10 +1544,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": {
@@ -1572,8 +1582,7 @@
       "com.google.auto.value:auto-value-annotations",
       "com.google.auto:auto-common",
       "com.google.guava:guava",
-      "com.squareup:javapoet",
-      "javax.inject:javax.inject"
+      "com.squareup:javapoet"
     ],
     "com.google.auto:auto-common": [
       "com.google.guava:guava"
@@ -1618,13 +1627,9 @@
       "org.jspecify:jspecify"
     ],
     "com.google.inject.extensions:guice-assistedinject": [
-      "com.google.errorprone:error_prone_annotations",
-      "com.google.inject:guice"
+      "com.google.errorprone:error_prone_annotations"
     ],
-    "com.google.inject.extensions:guice-servlet": [
-      "com.google.inject:guice"
-    ],
-    "com.google.inject:guice": [
+    "com.google.inject:guice:jar:classes": [
       "aopalliance:aopalliance",
       "com.google.guava:guava",
       "jakarta.inject:jakarta.inject-api",
@@ -1643,7 +1648,6 @@
       "com.google.flogger:flogger-system-backend",
       "com.google.flogger:google-extensions",
       "com.google.guava:guava",
-      "com.google.inject:guice",
       "com.google.protobuf:protobuf-java",
       "com.ibm.icu:icu4j",
       "javax.inject:javax.inject",
@@ -1805,6 +1809,13 @@
     "org.commonmark:commonmark-ext-gfm-tables": [
       "org.commonmark:commonmark"
     ],
+    "org.eclipse.jetty.ee11:jetty-ee11-servlet": [
+      "jakarta.servlet:jakarta.servlet-api",
+      "org.eclipse.jetty:jetty-security",
+      "org.eclipse.jetty:jetty-server",
+      "org.eclipse.jetty:jetty-session",
+      "org.slf4j:slf4j-api"
+    ],
     "org.eclipse.jetty.ee8:jetty-ee8-nested": [
       "org.eclipse.jetty.toolchain:jetty-servlet-api",
       "org.eclipse.jetty:jetty-http",
@@ -1824,32 +1835,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",
@@ -1857,7 +1872,6 @@
       "org.objenesis:objenesis"
     ],
     "org.openid4java:openid4java": [
-      "com.google.inject:guice",
       "net.sourceforge.nekohtml:nekohtml",
       "org.apache.httpcomponents:httpclient",
       "xerces:xercesImpl"
@@ -1991,8 +2005,7 @@
       "autovalue.shaded.com.google.j2objc.annotations",
       "autovalue.shaded.com.squareup.javapoet",
       "autovalue.shaded.net.ltgt.gradle.incap",
-      "autovalue.shaded.org.checkerframework.checker.nullness.qual",
-      "autovalue.shaded.org.checkerframework.framework.qual",
+      "autovalue.shaded.org.jspecify.annotations",
       "autovalue.shaded.org.objectweb.asm",
       "com.google.auto.value.extension",
       "com.google.auto.value.extension.memoized.processor",
@@ -2104,7 +2117,7 @@
     "com.google.inject.extensions:guice-servlet": [
       "com.google.inject.servlet"
     ],
-    "com.google.inject:guice": [
+    "com.google.inject:guice:jar:classes": [
       "com.google.inject",
       "com.google.inject.binder",
       "com.google.inject.internal",
@@ -2599,6 +2612,12 @@
     "jakarta.inject:jakarta.inject-api": [
       "jakarta.inject"
     ],
+    "jakarta.servlet:jakarta.servlet-api": [
+      "jakarta.servlet",
+      "jakarta.servlet.annotation",
+      "jakarta.servlet.descriptor",
+      "jakarta.servlet.http"
+    ],
     "javax.activation:activation": [
       "com.sun.activation.registries",
       "com.sun.activation.viewers",
@@ -3756,6 +3775,15 @@
       "org.commonmark.ext.gfm.tables",
       "org.commonmark.ext.gfm.tables.internal"
     ],
+    "org.eclipse.jetty.ee11:jetty-ee11-servlet": [
+      "org.eclipse.jetty.ee11.servlet",
+      "org.eclipse.jetty.ee11.servlet.internal",
+      "org.eclipse.jetty.ee11.servlet.jmx",
+      "org.eclipse.jetty.ee11.servlet.listener",
+      "org.eclipse.jetty.ee11.servlet.security",
+      "org.eclipse.jetty.ee11.servlet.security.authentication",
+      "org.eclipse.jetty.ee11.servlet.util"
+    ],
     "org.eclipse.jetty.ee8:jetty-ee8-nested": [
       "org.eclipse.jetty.ee8.nested",
       "org.eclipse.jetty.ee8.nested.jmx"
@@ -3778,35 +3806,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 +3843,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",
@@ -4355,7 +4382,7 @@
       "com.google.inject.extensions:guice-assistedinject:jar:sources",
       "com.google.inject.extensions:guice-servlet",
       "com.google.inject.extensions:guice-servlet:jar:sources",
-      "com.google.inject:guice",
+      "com.google.inject:guice:jar:classes",
       "com.google.inject:guice:jar:sources",
       "com.google.j2objc:j2objc-annotations",
       "com.google.j2objc:j2objc-annotations:jar:sources",
@@ -4429,6 +4456,8 @@
       "io.sweers.autotransient:autotransient:jar:sources",
       "jakarta.inject:jakarta.inject-api",
       "jakarta.inject:jakarta.inject-api:jar:sources",
+      "jakarta.servlet:jakarta.servlet-api",
+      "jakarta.servlet:jakarta.servlet-api:jar:sources",
       "javax.activation:activation",
       "javax.activation:activation:jar:sources",
       "javax.inject:javax.inject",
@@ -4519,6 +4548,8 @@
       "org.commonmark:commonmark-ext-gfm-tables",
       "org.commonmark:commonmark-ext-gfm-tables:jar:sources",
       "org.commonmark:commonmark:jar:sources",
+      "org.eclipse.jetty.ee11:jetty-ee11-servlet",
+      "org.eclipse.jetty.ee11:jetty-ee11-servlet:jar:sources",
       "org.eclipse.jetty.ee8:jetty-ee8-nested",
       "org.eclipse.jetty.ee8:jetty-ee8-nested:jar:sources",
       "org.eclipse.jetty.ee8:jetty-ee8-security",
@@ -4537,8 +4568,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",
@@ -4653,7 +4682,7 @@
       "com.google.inject.extensions:guice-assistedinject:jar:sources",
       "com.google.inject.extensions:guice-servlet",
       "com.google.inject.extensions:guice-servlet:jar:sources",
-      "com.google.inject:guice",
+      "com.google.inject:guice:jar:classes",
       "com.google.inject:guice:jar:sources",
       "com.google.j2objc:j2objc-annotations",
       "com.google.j2objc:j2objc-annotations:jar:sources",
@@ -4727,6 +4756,8 @@
       "io.sweers.autotransient:autotransient:jar:sources",
       "jakarta.inject:jakarta.inject-api",
       "jakarta.inject:jakarta.inject-api:jar:sources",
+      "jakarta.servlet:jakarta.servlet-api",
+      "jakarta.servlet:jakarta.servlet-api:jar:sources",
       "javax.activation:activation",
       "javax.activation:activation:jar:sources",
       "javax.inject:javax.inject",
@@ -4817,6 +4848,8 @@
       "org.commonmark:commonmark-ext-gfm-tables",
       "org.commonmark:commonmark-ext-gfm-tables:jar:sources",
       "org.commonmark:commonmark:jar:sources",
+      "org.eclipse.jetty.ee11:jetty-ee11-servlet",
+      "org.eclipse.jetty.ee11:jetty-ee11-servlet:jar:sources",
       "org.eclipse.jetty.ee8:jetty-ee8-nested",
       "org.eclipse.jetty.ee8:jetty-ee8-nested:jar:sources",
       "org.eclipse.jetty.ee8:jetty-ee8-security",
@@ -4835,8 +4868,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",
@@ -5187,7 +5218,7 @@
     },
     "org.apache.sshd:sshd-sftp": {
       "java.nio.file.spi.FileSystemProvider": [
-        "org.apache.sshd.sftp.client.fs.SftpFileSystemProvider"
+        "org.apache.sshd.sftp.client.fs.SftpFileSystemProviderFacade"
       ],
       "org.apache.sshd.server.subsystem.SubsystemFactory": [
         "org.apache.sshd.sftp.server.SftpSubsystemFactory"
@@ -5195,7 +5226,7 @@
     },
     "org.apache.sshd:sshd-sftp:jar:sources": {
       "java.nio.file.spi.FileSystemProvider": [
-        "org.apache.sshd.sftp.client.fs.SftpFileSystemProvider"
+        "org.apache.sshd.sftp.client.fs.SftpFileSystemProviderFacade"
       ],
       "org.apache.sshd.server.subsystem.SubsystemFactory": [
         "org.apache.sshd.sftp.server.SftpSubsystemFactory"
@@ -5215,12 +5246,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/acceptance/ChangeIndexedCounter.java b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
index e4caa1a..f9698dc 100644
--- a/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
+++ b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
@@ -30,7 +30,7 @@
   }
 
   @Override
-  public void onChangeDeleted(int id) {
+  public void onChangeDeleted(String projectName, int id) {
     countsByChange.incrementAndGet(id);
     deletionsByChange.incrementAndGet(id);
   }
diff --git a/java/com/google/gerrit/extensions/events/ChangeIndexedListener.java b/java/com/google/gerrit/extensions/events/ChangeIndexedListener.java
index 82d0a53..64b62a6 100644
--- a/java/com/google/gerrit/extensions/events/ChangeIndexedListener.java
+++ b/java/com/google/gerrit/extensions/events/ChangeIndexedListener.java
@@ -43,7 +43,7 @@
   void onChangeIndexed(String projectName, int id);
 
   /** Invoked when a change is deleted from the index. */
-  void onChangeDeleted(int id);
+  void onChangeDeleted(String projectName, int id);
 
   /**
    * Invoked when all change indexes are deleted for a given project
diff --git a/java/com/google/gerrit/extensions/restapi/Response.java b/java/com/google/gerrit/extensions/restapi/Response.java
index cc0c134..851ec96 100644
--- a/java/com/google/gerrit/extensions/restapi/Response.java
+++ b/java/com/google/gerrit/extensions/restapi/Response.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.extensions.restapi;
 
 import static com.google.common.base.Preconditions.checkState;
-import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
 
 import com.google.common.collect.ImmutableMultimap;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
@@ -23,6 +22,10 @@
 
 /** Special return value to mean specific HTTP status codes in a REST API. */
 public abstract class Response<T> {
+  // Intentionally keep this constant local to avoid introducing an HTTP library
+  // dependency into the public gerrit-extension-api.
+  private static final int HTTP_INTERNAL_SERVER_ERROR = 500;
+
   @SuppressWarnings({"rawtypes"})
   private static final Response NONE = new None();
 
@@ -74,7 +77,7 @@
   /** Arbitrary status code with wrapped result. */
   public static <T> Response<T> withStatusCode(int statusCode, T value) {
     checkState(
-        statusCode < SC_INTERNAL_SERVER_ERROR,
+        statusCode < HTTP_INTERNAL_SERVER_ERROR,
         "Status code must be < 500. To return an internal server error REST endpoint"
             + " implementations should throw an exception");
     return new Impl<>(statusCode, value);
diff --git a/java/com/google/gerrit/httpd/BUILD b/java/com/google/gerrit/httpd/BUILD
index 0142031..18334aa 100644
--- a/java/com/google/gerrit/httpd/BUILD
+++ b/java/com/google/gerrit/httpd/BUILD
@@ -38,6 +38,7 @@
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
         "//lib/commons:lang3",
+        "//lib/commons:net",
         "//lib/errorprone:annotations",
         "//lib/flogger:api",
         "//lib/guice",
diff --git a/java/com/google/gerrit/httpd/ContainerAuthFilter.java b/java/com/google/gerrit/httpd/ContainerAuthFilter.java
index 517d5db..3abe786 100644
--- a/java/com/google/gerrit/httpd/ContainerAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ContainerAuthFilter.java
@@ -66,18 +66,21 @@
   private final AccountCache accountCache;
   private final Config config;
   private final String loginHttpHeader;
+  private final RemoteUserUtil remoteUserUtil;
 
   @Inject
   ContainerAuthFilter(
       DynamicItem<WebSession> session,
       AccountCache accountCache,
       AuthConfig authConfig,
-      @GerritServerConfig Config config) {
+      @GerritServerConfig Config config,
+      RemoteUserUtil remoteUserUtil) {
     this.session = session;
     this.accountCache = accountCache;
     this.config = config;
 
     loginHttpHeader = firstNonNull(emptyToNull(authConfig.getLoginHttpHeader()), AUTHORIZATION);
+    this.remoteUserUtil = remoteUserUtil;
   }
 
   @Override
@@ -98,7 +101,7 @@
   }
 
   private boolean verify(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
-    String username = RemoteUserUtil.getRemoteUser(req, loginHttpHeader);
+    String username = remoteUserUtil.getRemoteUser(req, loginHttpHeader);
     if (username == null) {
       if (isLfsOverSshRequest(req)) {
         // LFS-over-SSH auth request cannot be authorized by container
diff --git a/java/com/google/gerrit/httpd/RemoteUserUtil.java b/java/com/google/gerrit/httpd/RemoteUserUtil.java
index 9ec10e2..3d3e54b 100644
--- a/java/com/google/gerrit/httpd/RemoteUserUtil.java
+++ b/java/com/google/gerrit/httpd/RemoteUserUtil.java
@@ -18,11 +18,71 @@
 import static com.google.common.net.HttpHeaders.AUTHORIZATION;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.common.base.MoreObjects;
+import com.google.common.flogger.FluentLogger;
 import com.google.common.io.BaseEncoding;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletRequest;
+import org.apache.commons.net.util.SubnetUtils;
 
+@Singleton
 public class RemoteUserUtil {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+  /**
+   * Request attribute carrying the TCP-peer address before any X-Forwarded-For rewrite.
+   *
+   * <p>HTTP layers that want their requests evaluated against {@code auth.httpTrustedProxyNetworks}
+   * must set this attribute on each request. If the attribute is unset, {@link
+   * HttpServletRequest#getRemoteAddr()} is used as the peer address.
+   *
+   * <p>Gerrit's default Jetty container wires this automatically (see {@code
+   * JettyServer.ForwardedRequestCustomizer}). Other servlet containers (Tomcat, embedded netty,
+   * etc.) can opt in by installing a {@code Filter} or equivalent that runs <em>before</em> any
+   * X-Forwarded-For rewrite (e.g., Tomcat's {@code RemoteIpValve}) and sets this attribute to
+   * {@code request.getRemoteAddr()}.
+   */
+  public static final String PROXY_REMOTE_ADDRESS_ATTR =
+      "com.google.gerrit.httpd.proxyRemoteAddress";
+
+  private final Set<SubnetUtils.SubnetInfo> trustedProxySubnets;
+  private final Set<String> trustedProxyNetworks;
+
+  @Inject
+  RemoteUserUtil(AuthConfig authConfig) {
+    // The full list of `trustedProxyNetworks` is also kept as Set<String>
+    // for allowing the single-IP matching (networks ending with '/32') fast
+    // lookup whilst the full network matching evaluation is performed
+    // through the trustedProxySubnets loop.
+    trustedProxyNetworks = authConfig.getTrustedProxyNetworks();
+
+    try {
+      trustedProxySubnets =
+          trustedProxyNetworks.stream()
+              // Filter out single IPs because they are not matched by
+              // subnetwork matching but rather direct containment in
+              // trustedProxyNetworks
+              .filter(Predicate.not(RemoteUserUtil::isSingleIp))
+              .map(SubnetUtils::new)
+              .map(SubnetUtils::getInfo)
+              .collect(Collectors.toSet());
+    } catch (IllegalArgumentException e) {
+      throw new ProvisionException("Invalid auth trusted proxy definition: " + e.getMessage(), e);
+    }
+  }
+
+  private static boolean isSingleIp(String network) {
+    return network.endsWith("/32");
+  }
+
   /**
    * Tries to get username from a request with following strategies:
    *
@@ -37,8 +97,10 @@
    * @return the extracted username or null.
    */
   @Nullable
-  public static String getRemoteUser(HttpServletRequest req, String loginHeader) {
-    if (AUTHORIZATION.equals(loginHeader)) {
+  public String getRemoteUser(HttpServletRequest req, String loginHeader) {
+    boolean isAuthorizationHeader = AUTHORIZATION.equals(loginHeader);
+
+    if (isAuthorizationHeader) {
       String user = emptyToNull(req.getRemoteUser());
       if (user != null) {
         // The container performed the authentication, and has the user
@@ -46,16 +108,55 @@
         // configured to honor HTTP authentication.
         return user;
       }
-
-      // If the container didn't do the authentication we might
-      // have done it in the front-end web server. Try to split
-      // the identity out of the Authorization header and honor it.
-      String auth = req.getHeader(AUTHORIZATION);
-      return extractUsername(auth);
     }
-    // Nonstandard HTTP header. We have been told to trust this
-    // header blindly as-is.
-    return emptyToNull(req.getHeader(loginHeader));
+
+    if (!isRequestFromTrustedProxyNetworks(req)) {
+      return null;
+    }
+
+    String auth = req.getHeader(loginHeader);
+    return isAuthorizationHeader
+        ?
+        // If the container didn't do the authentication we might
+        // have done it in the front-end web server. Try to split
+        // the identity out of the Authorization header and honor it.
+        extractUsername(auth)
+        :
+        // Nonstandard HTTP header. We have been told to trust this
+        // header blindly as-is.
+        emptyToNull(auth);
+  }
+
+  private boolean isRequestFromTrustedProxyNetworks(HttpServletRequest req) {
+    if (trustedProxyNetworks.isEmpty()) {
+      return true;
+    }
+
+    String remoteAddress = getRemoteAddress(req);
+    if (isIpv6Address(remoteAddress)) {
+      logger.atWarning().atMostEvery(1, TimeUnit.MINUTES).log(
+          "IPv6 remote address: %s - trusted proxy enforcement supports only IPv4, HTTP header"
+              + " rejected",
+          remoteAddress);
+      return false;
+    }
+
+    if (trustedProxyNetworks.contains(remoteAddress + "/32")) {
+      return true;
+    }
+
+    if (trustedProxySubnets.stream().anyMatch(subnet -> subnet.isInRange(remoteAddress))) {
+      return true;
+    }
+
+    logger.atWarning().atMostEvery(1, TimeUnit.MINUTES).log(
+        "Untrusted remote address: %s - authentication via HTTP header rejected", remoteAddress);
+    return false;
+  }
+
+  private static String getRemoteAddress(HttpServletRequest req) {
+    return MoreObjects.firstNonNull(
+        (String) req.getAttribute(PROXY_REMOTE_ADDRESS_ATTR), req.getRemoteAddr());
   }
 
   /**
@@ -90,4 +191,8 @@
       return null;
     }
   }
+
+  private static boolean isIpv6Address(String ipAddress) {
+    return ipAddress.contains(":");
+  }
 }
diff --git a/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java b/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
index f0a8b89..710adc4 100644
--- a/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
+++ b/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
@@ -67,12 +67,14 @@
   private final String externalIdHeader;
   private final boolean userNameToLowerCase;
   private final ExternalIdKeyFactory externalIdKeyFactory;
+  private final RemoteUserUtil remoteUserUtil;
 
   @Inject
   HttpAuthFilter(
       DynamicItem<WebSession> webSession,
       AuthConfig authConfig,
-      ExternalIdKeyFactory externalIdKeyFactory)
+      ExternalIdKeyFactory externalIdKeyFactory,
+      RemoteUserUtil remoteUserUtil)
       throws IOException {
     this.sessionProvider = webSession;
     this.externalIdKeyFactory = externalIdKeyFactory;
@@ -90,6 +92,7 @@
     emailHeader = emptyToNull(authConfig.getHttpEmailHeader());
     externalIdHeader = emptyToNull(authConfig.getHttpExternalIdHeader());
     userNameToLowerCase = authConfig.isUserNameToLowerCase();
+    this.remoteUserUtil = remoteUserUtil;
   }
 
   @Override
@@ -138,7 +141,7 @@
   }
 
   String getRemoteUser(HttpServletRequest req) {
-    String remoteUser = RemoteUserUtil.getRemoteUser(req, loginHeader);
+    String remoteUser = remoteUserUtil.getRemoteUser(req, loginHeader);
     return (userNameToLowerCase && remoteUser != null)
         ? remoteUser.toLowerCase(Locale.US)
         : remoteUser;
diff --git a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
index 4a76bd8..f7eb673 100644
--- a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
+++ b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.extensions.api.config.Server;
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.client.ListOption;
-import com.google.gerrit.extensions.common.ServerInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.httpd.raw.IndexPreloadingUtil.RequestedPage;
@@ -69,16 +68,13 @@
       String faviconPath,
       Map<String, String[]> urlParameterMap,
       Function<String, SanitizedContent> urlInScriptTagOrdainer,
-      String requestedURL,
-      ServerInfo serverInfo,
-      String serverVersion)
+      String requestedURL)
       throws URISyntaxException, RestApiException {
     ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
     data.putAll(
             staticTemplateData(
                 canonicalURL, cdnPath, faviconPath, urlParameterMap, urlInScriptTagOrdainer))
-        .putAll(
-            dynamicTemplateData(gerritApi, requestedURL, canonicalURL, serverInfo, serverVersion));
+        .putAll(dynamicTemplateData(gerritApi, requestedURL, canonicalURL));
     Set<String> enabledExperiments = new HashSet<>();
     enabledExperiments.addAll(experimentFeatures.getEnabledExperimentFeatures());
     // Add all experiments enabled through url
@@ -114,20 +110,17 @@
 
   /** Returns dynamic parameters of {@code index.html}. */
   public static ImmutableMap<String, Object> dynamicTemplateData(
-      GerritApi gerritApi,
-      String requestedURL,
-      String canonicalURL,
-      ServerInfo serverInfo,
-      String serverVersion)
+      GerritApi gerritApi, String requestedURL, String canonicalURL)
       throws RestApiException, URISyntaxException {
     ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
     Map<String, SanitizedContent> initialData = new HashMap<>();
+    Server serverApi = gerritApi.config().server();
     initialData.put(
-        addCanonicalUrl("/config/server/info", canonicalURL), serializeObject(GSON, serverInfo));
+        addCanonicalUrl("/config/server/info", canonicalURL),
+        serializeObject(GSON, serverApi.getInfo()));
     initialData.put(
         addCanonicalUrl("/config/server/version", canonicalURL),
-        serializeObject(GSON, serverVersion));
-    Server serverApi = gerritApi.config().server();
+        serializeObject(GSON, serverApi.getVersion()));
     initialData.put(
         addCanonicalUrl("/config/server/top-menus", canonicalURL),
         serializeObject(GSON, serverApi.topMenus()));
diff --git a/java/com/google/gerrit/httpd/raw/IndexServlet.java b/java/com/google/gerrit/httpd/raw/IndexServlet.java
index 12a2f82..fcb821e 100644
--- a/java/com/google/gerrit/httpd/raw/IndexServlet.java
+++ b/java/com/google/gerrit/httpd/raw/IndexServlet.java
@@ -14,18 +14,14 @@
 
 package com.google.gerrit.httpd.raw;
 
-import static com.google.gerrit.server.config.ServerConfigCacheImpl.SINGLETON_KEY;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static javax.servlet.http.HttpServletResponse.SC_OK;
 
-import com.google.common.cache.Cache;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.io.Resources;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.common.ServerInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.config.ServerConfigCacheImpl.ServerConfigData;
 import com.google.gerrit.server.experiments.ExperimentFeatures;
 import com.google.template.soy.SoyFileSet;
 import com.google.template.soy.data.SanitizedContent;
@@ -35,7 +31,6 @@
 import java.io.OutputStream;
 import java.net.URISyntaxException;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
 import java.util.function.Function;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -53,21 +48,18 @@
   private final ExperimentFeatures experimentFeatures;
   private final SoySauce soySauce;
   private final Function<String, SanitizedContent> urlOrdainer;
-  private final Cache<String, ServerConfigData> serverConfigCache;
 
   IndexServlet(
       @Nullable String canonicalUrl,
       @Nullable String cdnPath,
       @Nullable String faviconPath,
       GerritApi gerritApi,
-      ExperimentFeatures experimentFeatures,
-      Cache<String, ServerConfigData> serverConfigCache) {
+      ExperimentFeatures experimentFeatures) {
     this.canonicalUrl = canonicalUrl;
     this.cdnPath = cdnPath;
     this.faviconPath = faviconPath;
     this.gerritApi = gerritApi;
     this.experimentFeatures = experimentFeatures;
-    this.serverConfigCache = serverConfigCache;
     this.soySauce =
         SoyFileSet.builder()
             .add(Resources.getResource(POLY_GERRIT_INDEX_HTML_SOY), POLY_GERRIT_INDEX_HTML_SOY)
@@ -83,17 +75,6 @@
   protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
     SoySauce.Renderer renderer;
     try {
-      ServerConfigData configData =
-          serverConfigCache.get(
-              SINGLETON_KEY,
-              () ->
-                  ServerConfigData.create(
-                      gerritApi.config().server().getInfo(),
-                      gerritApi.config().server().getVersion()));
-
-      ServerInfo serverInfo = configData.serverInfo();
-      String serverVersion = configData.serverVersion();
-
       Map<String, String[]> parameterMap = req.getParameterMap();
       // TODO(hiesel): Remove URL ordainer as parameter once Soy is consistent
       ImmutableMap<String, Object> templateData =
@@ -105,17 +86,10 @@
               faviconPath,
               parameterMap,
               urlOrdainer,
-              getRequestUrl(req),
-              serverInfo,
-              serverVersion);
+              getRequestUrl(req));
       renderer = soySauce.renderTemplate("com.google.gerrit.httpd.raw.Index").setData(templateData);
     } catch (URISyntaxException | RestApiException e) {
       throw new IOException(e);
-    } catch (ExecutionException e) {
-      if (e.getCause() instanceof RestApiException) {
-        throw new IOException(e.getCause());
-      }
-      throw new IOException(e.getCause() != null ? e.getCause() : e);
     }
 
     rsp.setCharacterEncoding(UTF_8.name());
diff --git a/java/com/google/gerrit/httpd/raw/StaticModule.java b/java/com/google/gerrit/httpd/raw/StaticModule.java
index c07e0f0..038d9f2 100644
--- a/java/com/google/gerrit/httpd/raw/StaticModule.java
+++ b/java/com/google/gerrit/httpd/raw/StaticModule.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.config.GerritInstanceNameProvider;
 import com.google.gerrit.server.config.GerritOptions;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.ServerConfigCacheImpl;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.experiments.ExperimentFeatures;
 import com.google.inject.Inject;
@@ -275,13 +274,10 @@
         @CanonicalWebUrl @Nullable String canonicalUrl,
         @GerritServerConfig Config cfg,
         GerritApi gerritApi,
-        ExperimentFeatures experimentFeatures,
-        @Named(ServerConfigCacheImpl.CACHE_CONFIG)
-            Cache<String, ServerConfigCacheImpl.ServerConfigData> serverConfigCache) {
+        ExperimentFeatures experimentFeatures) {
       String cdnPath = options.devCdn().orElseGet(() -> cfg.getString("gerrit", null, "cdnPath"));
       String faviconPath = cfg.getString("gerrit", null, "faviconPath");
-      return new IndexServlet(
-          canonicalUrl, cdnPath, faviconPath, gerritApi, experimentFeatures, serverConfigCache);
+      return new IndexServlet(canonicalUrl, cdnPath, faviconPath, gerritApi, experimentFeatures);
     }
 
     @Provides
diff --git a/java/com/google/gerrit/launcher/GerritLauncher.java b/java/com/google/gerrit/launcher/GerritLauncher.java
index 53f4af9..79205ed 100644
--- a/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -325,6 +325,7 @@
     List<URL> extapi = new ArrayList<>();
     move(jars, "gerrit-extension-api-", extapi);
     move(jars, "guice-", extapi);
+    move(jars, "jakarta.inject-api-2.0.1.jar", extapi);
     move(jars, "javax.inject-1.jar", extapi);
     move(jars, "aopalliance-1.0.jar", extapi);
     move(jars, "guice-servlet-", extapi);
@@ -728,9 +729,9 @@
         throw new FileNotFoundException("Cannot extract path from " + u);
       }
 
-      // Pop up to the top-level source folder by looking for WORKSPACE.
+      // Pop up to the top-level source folder by looking for MODULE.bazel.
       dir = Path.of(u.getPath());
-      while (!Files.isRegularFile(dir.resolve("WORKSPACE"))) {
+      while (!Files.isRegularFile(dir.resolve("MODULE.bazel"))) {
         Path parent = dir.getParent();
         if (parent == null) {
           throw new FileNotFoundException("Cannot find source root from " + u);
diff --git a/java/com/google/gerrit/pgm/Reindex.java b/java/com/google/gerrit/pgm/Reindex.java
index 7c0f258..0964dae 100644
--- a/java/com/google/gerrit/pgm/Reindex.java
+++ b/java/com/google/gerrit/pgm/Reindex.java
@@ -315,7 +315,7 @@
       new CacheDisplay(
               sw,
               StreamSupport.stream(cacheMap.spliterator(), false)
-                  .map(e -> CacheInfoFactory.create(e.getExportName(), e.get()))
+                  .map(e -> CacheInfoFactory.create(e.getExportName(), e.get(), true))
                   .collect(Collectors.toList()))
           .displayCaches();
       System.out.print(sw.toString());
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..20f5bb3 100644
--- a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
+++ b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -23,6 +23,7 @@
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.httpd.RemoteUserUtil;
 import com.google.gerrit.pgm.http.jetty.HttpLog.HttpLogFactory;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
@@ -33,6 +34,7 @@
 import com.google.inject.servlet.GuiceFilter;
 import com.google.inject.servlet.GuiceServletContextListener;
 import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.file.Files;
@@ -52,7 +54,15 @@
 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.HttpFields;
 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,27 +70,52 @@
 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;
 
 @Singleton
 public class JettyServer {
+
+  private static final ForwardedRequestCustomizer FORWARDED_REQUEST_CUSTOMIZER =
+      new ForwardedRequestCustomizer() {
+        @Override
+        public Request customize(Request request, HttpFields.Mutable responseHeaders) {
+          /*
+           * The default behavior of ForwardedRequestCustomizer is to overwrite the remote address
+           * with the value of the X-Forwarded-For header, if present.
+           * However, it does not "remember" the original remote address and therefore would
+           * prevent any validation against it.
+           *
+           * ForwardedRequestCustomizer's original code fragment:
+           * <code>
+           * if (forwarded.hasFor())
+           * {
+           *     int forPort = forwarded._for._port > 0 ? forwarded._for._port : request.getRemotePort();
+           *     request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._for._host, forPort));
+           * }
+           * </code>
+           *
+           * What we want to achieve here is to remember what it was the original proxy address before
+           * calling super.customize() and give the possibility to fetch it later down the chain.
+           */
+          request.setAttribute(
+              RemoteUserUtil.PROXY_REMOTE_ADDRESS_ATTR,
+              ((InetSocketAddress) request.getConnectionMetaData().getRemoteSocketAddress())
+                  .getAddress()
+                  .getHostAddress());
+          return super.customize(request, responseHeaders);
+        }
+      };
+
   static class Lifecycle implements LifecycleListener {
     private final JettyServer server;
     private final Config cfg;
@@ -259,15 +294,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 +344,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 '"
@@ -367,17 +416,33 @@
 
       } else if ("proxy-http".equals(u.getScheme())) {
         defaultPort = 8080;
-        config.addCustomizer(new ForwardedRequestCustomizer());
+        config.addCustomizer(FORWARDED_REQUEST_CUSTOMIZER);
         c = newServerConnector(server, acceptors, config);
 
       } else if ("proxy-https".equals(u.getScheme())) {
         defaultPort = 8080;
-        config.addCustomizer(new ForwardedRequestCustomizer());
+        config.addCustomizer(FORWARDED_REQUEST_CUSTOMIZER);
+        // 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 +493,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 +546,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 +586,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 +606,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 +685,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/BUILD b/java/com/google/gerrit/server/BUILD
index edede84..756000b 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -35,6 +35,9 @@
         exclude = CONSTANTS_SRC + GERRIT_GLOBAL_MODULE_SRC + TESTING_SRC +
                   PROLOG_SRC,
     ),
+    # Pin AutoFactory to the javax inject API. See the "Bump auto-factory
+    # version to 1.1.0" change for why; the follow-up removes this pin.
+    javacopts = ["-Acom.google.auto.factory.InjectApi=javax"],
     resource_strip_prefix = "resources",
     resources = ["//resources/com/google/gerrit/server"],
     visibility = ["//visibility:public"],
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/account/ServiceUserClassifierImpl.java b/java/com/google/gerrit/server/account/ServiceUserClassifierImpl.java
index a05baf5..cfe7857 100644
--- a/java/com/google/gerrit/server/account/ServiceUserClassifierImpl.java
+++ b/java/com/google/gerrit/server/account/ServiceUserClassifierImpl.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.server.logging.TraceContext;
 import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.Scopes;
 import com.google.inject.Singleton;
@@ -31,7 +32,6 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
-import javax.inject.Inject;
 
 /**
  * An implementation of {@link ServiceUserClassifier} that will consider a user to be a robot if
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java b/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java
index 560a1c7..1d11bb9 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java
@@ -20,8 +20,8 @@
 import com.google.gerrit.common.UsedAt.Project;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.inject.ImplementedBy;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
 @Singleton
 public class ExternalIdKeyFactory {
diff --git a/java/com/google/gerrit/server/account/externalids/storage/notedb/ExternalIdFactoryNoteDbImpl.java b/java/com/google/gerrit/server/account/externalids/storage/notedb/ExternalIdFactoryNoteDbImpl.java
index aa321e7..cb3e894 100644
--- a/java/com/google/gerrit/server/account/externalids/storage/notedb/ExternalIdFactoryNoteDbImpl.java
+++ b/java/com/google/gerrit/server/account/externalids/storage/notedb/ExternalIdFactoryNoteDbImpl.java
@@ -28,9 +28,9 @@
 import com.google.gerrit.server.account.externalids.ExternalIdFactory;
 import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
 import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
diff --git a/java/com/google/gerrit/server/account/storage/notedb/AccountsNoteDbImpl.java b/java/com/google/gerrit/server/account/storage/notedb/AccountsNoteDbImpl.java
index 02ad518..b0c35be 100644
--- a/java/com/google/gerrit/server/account/storage/notedb/AccountsNoteDbImpl.java
+++ b/java/com/google/gerrit/server/account/storage/notedb/AccountsNoteDbImpl.java
@@ -39,6 +39,7 @@
 import com.google.gerrit.server.config.VersionedDefaultPreferences;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -46,7 +47,6 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Stream;
-import javax.inject.Singleton;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
diff --git a/java/com/google/gerrit/server/account/storage/notedb/AccountsUpdateNoteDbImpl.java b/java/com/google/gerrit/server/account/storage/notedb/AccountsUpdateNoteDbImpl.java
index 663eddd..1820e70 100644
--- a/java/com/google/gerrit/server/account/storage/notedb/AccountsUpdateNoteDbImpl.java
+++ b/java/com/google/gerrit/server/account/storage/notedb/AccountsUpdateNoteDbImpl.java
@@ -57,7 +57,9 @@
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryableAction.Action;
 import com.google.gerrit.server.update.context.RefUpdateContext;
+import com.google.inject.Inject;
 import com.google.inject.Provider;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -67,8 +69,6 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
-import javax.inject.Inject;
-import javax.inject.Singleton;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.ObjectId;
diff --git a/java/com/google/gerrit/server/approval/testing/TestPatchSetApprovalUuidGenerator.java b/java/com/google/gerrit/server/approval/testing/TestPatchSetApprovalUuidGenerator.java
index 676640d..e1caf2f 100644
--- a/java/com/google/gerrit/server/approval/testing/TestPatchSetApprovalUuidGenerator.java
+++ b/java/com/google/gerrit/server/approval/testing/TestPatchSetApprovalUuidGenerator.java
@@ -19,9 +19,9 @@
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.entities.PatchSetApproval.UUID;
 import com.google.gerrit.server.approval.PatchSetApprovalUuidGenerator;
+import com.google.inject.Singleton;
 import java.time.Instant;
 import java.util.Locale;
-import javax.inject.Singleton;
 
 /**
  * Implementation of {@link PatchSetApprovalUuidGenerator} that returns predictable {@link UUID}.
diff --git a/java/com/google/gerrit/server/cache/CacheInfoFactory.java b/java/com/google/gerrit/server/cache/CacheInfoFactory.java
index dafa186..48122db 100644
--- a/java/com/google/gerrit/server/cache/CacheInfoFactory.java
+++ b/java/com/google/gerrit/server/cache/CacheInfoFactory.java
@@ -24,11 +24,11 @@
 
 public class CacheInfoFactory {
 
-  public static CacheInfo create(Cache<?, ?> cache) {
-    return create(null, cache);
+  public static CacheInfo create(Cache<?, ?> cache, boolean includeDiskStats) {
+    return create(null, cache, includeDiskStats);
   }
 
-  public static CacheInfo create(String name, Cache<?, ?> cache) {
+  public static CacheInfo create(String name, Cache<?, ?> cache, boolean includeDiskStats) {
     CacheInfo cacheInfo = new CacheInfo();
     cacheInfo.name = name;
 
@@ -44,10 +44,12 @@
 
     if (cache instanceof PersistentCache) {
       cacheInfo.type = CacheType.DISK;
-      PersistentCache.DiskStats diskStats = ((PersistentCache) cache).diskStats();
-      cacheInfo.entries.setDisk(diskStats.size());
-      cacheInfo.entries.setSpace(diskStats.space());
-      cacheInfo.hitRatio.setDisk(diskStats.hitCount(), diskStats.requestCount());
+      if (includeDiskStats) {
+        PersistentCache.DiskStats diskStats = ((PersistentCache) cache).diskStats();
+        cacheInfo.entries.setDisk(diskStats.size());
+        cacheInfo.entries.setSpace(diskStats.space());
+        cacheInfo.hitRatio.setDisk(diskStats.hitCount(), diskStats.requestCount());
+      }
     } else {
       cacheInfo.type = CacheType.MEM;
     }
diff --git a/java/com/google/gerrit/server/cache/CacheMetrics.java b/java/com/google/gerrit/server/cache/CacheMetrics.java
index 7053df0..e6dd0a7 100644
--- a/java/com/google/gerrit/server/cache/CacheMetrics.java
+++ b/java/com/google/gerrit/server/cache/CacheMetrics.java
@@ -59,6 +59,12 @@
             Long.class,
             new Description("Memory eviction count").setGauge().setUnit("evicted entries"),
             F_NAME);
+    CallbackMetric1<String, Long> memReq =
+        metrics.newCallbackMetric(
+            "caches/memory_request_count",
+            Long.class,
+            new Description("Memory request count").setGauge().setUnit("requests"),
+            F_NAME);
     CallbackMetric1<String, Long> perDiskEnt =
         metrics.newCallbackMetric(
             "caches/disk_cached",
@@ -79,9 +85,18 @@
                 .setGauge()
                 .setUnit("invalidated entries"),
             F_NAME);
+    CallbackMetric1<String, Long> perDiskReq =
+        metrics.newCallbackMetric(
+            "caches/disk_request_count",
+            Long.class,
+            new Description("Disk request count for persistent cache")
+                .setGauge()
+                .setUnit("requests"),
+            F_NAME);
 
     ImmutableSet<CallbackMetric<?>> cacheMetrics =
-        ImmutableSet.of(memEnt, memHit, memEvict, perDiskEnt, perDiskHit, perDiskInvalid);
+        ImmutableSet.of(
+            memEnt, memHit, memEvict, memReq, perDiskEnt, perDiskHit, perDiskInvalid, perDiskReq);
 
     metrics.newTrigger(
         cacheMetrics,
@@ -93,12 +108,14 @@
             memEnt.set(name, c.size());
             memHit.set(name, cstats.hitRate() * 100);
             memEvict.set(name, cstats.evictionCount());
+            memReq.set(name, cstats.requestCount());
             if (c instanceof PersistentCache
                 && config.getBoolean("cache", "enableDiskStatMetrics", false)) {
               PersistentCache.DiskStats d = ((PersistentCache) c).diskStats();
               perDiskEnt.set(name, d.size());
               perDiskHit.set(name, hitRatio(d));
               perDiskInvalid.set(name, d.invalidatedCount());
+              perDiskReq.set(name, d.requestCount());
             }
           }
           cacheMetrics.forEach(CallbackMetric::prune);
diff --git a/java/com/google/gerrit/server/cache/h2/CacheStoreStartupExecutor.java b/java/com/google/gerrit/server/cache/h2/CacheStoreStartupExecutor.java
new file mode 100644
index 0000000..903173c
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/h2/CacheStoreStartupExecutor.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2026 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.h2;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.Retention;
+
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface CacheStoreStartupExecutor {}
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
index 691c3c6..000b03d 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
@@ -87,6 +87,8 @@
   private final boolean pruneOnStartup;
   private final Schedule schedule;
   private final AtomicBoolean isDiskCacheReadOnly;
+  @Nullable private final ExecutorService startupExecutor;
+  private final boolean preWarmForBloomFilter;
 
   @Inject
   H2CacheFactory(
@@ -95,6 +97,7 @@
       DynamicMap<Cache<?, ?>> cacheMap,
       @Nullable @CacheCleanupExecutor ScheduledExecutorService cleanupExecutor,
       @Nullable @CacheStoreExecutor ExecutorService storeExecutor,
+      @Nullable @CacheStoreStartupExecutor ExecutorService startupExecutor,
       @Nullable @CacheDir Path cacheDir,
       Set<CacheOptions> options,
       @Named("DiskCacheReadOnly") AtomicBoolean isDiskCacheReadOnly) {
@@ -102,6 +105,7 @@
     h2CacheSize = cfg.getLong("cache", null, "h2CacheSize", -1);
     h2AutoServer = cfg.getBoolean("cache", null, "h2AutoServer", false);
     pruneOnStartup = cfg.getBoolean("cachePruning", null, "pruneOnStartup", true);
+    preWarmForBloomFilter = cfg.getBoolean("cache", null, "preWarmForBloomFilter", true);
     caches = new ArrayList<>();
     schedule =
         ScheduleConfig.createSchedule(cfg, "cachePruning")
@@ -112,13 +116,15 @@
     this.cleanup = cleanupExecutor;
     this.options = options;
     this.isDiskCacheReadOnly = isDiskCacheReadOnly;
+    this.startupExecutor = startupExecutor;
   }
 
   @Override
   public void start() {
     if (executor != null) {
+      ExecutorService se = startupExecutor != null ? startupExecutor : executor;
       for (H2CacheImpl<?, ?> cache : caches) {
-        executor.execute(cache::start);
+        se.execute(cache::start);
         if (cleanup != null) {
           if (pruneOnStartup) {
             @SuppressWarnings("unused")
@@ -135,6 +141,9 @@
                   TimeUnit.MILLISECONDS);
         }
       }
+      if (se != executor) {
+        se.shutdown();
+      }
     }
   }
 
@@ -142,6 +151,10 @@
   public void stop() {
     if (executor != null) {
       try {
+        if (startupExecutor != null) {
+          startupExecutor.shutdownNow();
+        }
+
         if (cleanup != null) {
           cleanup.shutdownNow();
         }
@@ -213,14 +226,10 @@
   }
 
   private <V, K> SqlStore<K, V> newSqlStore(PersistentCacheDef<K, V> def, long maxSize) {
+    String cacheName = def.name() + "-v" + COMPATIBILITY_VERSION;
     StringBuilder url = new StringBuilder();
     url.append("jdbc:h2:file:")
-        .append(
-            cacheDir
-                .resolve(def.name() + "-v" + COMPATIBILITY_VERSION)
-                .toAbsolutePath()
-                .toString()
-                .replace(";", "\\;"));
+        .append(cacheDir.resolve(cacheName).toAbsolutePath().toString().replace(";", "\\;"));
     if (h2CacheSize >= 0) {
       url.append(";CACHE_SIZE=");
       // H2 CACHE_SIZE is always given in KB
@@ -258,7 +267,9 @@
         refreshAfterWrite,
         options.contains(CacheOptions.BUILD_BLOOM_FILTER),
         options.contains(CacheOptions.TRACK_LAST_ACCESS),
-        isDiskCacheReadOnly);
+        isDiskCacheReadOnly,
+        preWarmForBloomFilter,
+        cacheDir.resolve(cacheName + ".mv.db"));
   }
 
   private boolean has(String name, String var) {
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java b/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
index 39afb38..d4a6052 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.cache.h2;
 
+import com.google.common.base.Stopwatch;
 import com.google.common.base.Throwables;
 import com.google.common.cache.AbstractLoadingCache;
 import com.google.common.cache.Cache;
@@ -37,7 +38,10 @@
 import com.google.gerrit.util.concurrent.ConcurrentBloomFilter;
 import com.google.inject.TypeLiteral;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InvalidClassException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -208,7 +212,9 @@
   }
 
   void start() {
+    logger.atFine().log("Opening disk cache %s...", cacheName);
     store.open();
+    logger.atFine().log("Finished opening disk cache %s...", cacheName);
   }
 
   void stop() {
@@ -346,6 +352,8 @@
     private boolean trackLastAccess;
     private final AtomicBoolean isDiskCacheReadOnly;
     private volatile boolean ensuredSchemaCreation;
+    private final Path cacheFilePath;
+    private final boolean preWarmForBloomFilter;
 
     SqlStore(
         String jdbcUrl,
@@ -359,7 +367,9 @@
         @Nullable Duration refreshAfterWrite,
         boolean buildBloomFilter,
         boolean trackLastAccess,
-        AtomicBoolean isDiskCacheReadOnly) {
+        AtomicBoolean isDiskCacheReadOnly,
+        boolean preWarmForBloomFilter,
+        Path cacheFilePath) {
       this.url = jdbcUrl;
       this.keyType = createKeyType(keyType, keySerializer);
       this.valueSerializer = valueSerializer;
@@ -370,6 +380,8 @@
       this.buildBloomFilter = buildBloomFilter;
       this.trackLastAccess = trackLastAccess;
       this.isDiskCacheReadOnly = isDiskCacheReadOnly;
+      this.cacheFilePath = cacheFilePath;
+      this.preWarmForBloomFilter = preWarmForBloomFilter;
 
       int cores = Runtime.getRuntime().availableProcessors();
       int keep = Math.min(cores, 16);
@@ -422,6 +434,24 @@
       }
     }
 
+    void warmupOsPageCache() {
+      // null check because tests use in-memory h2.
+      if (cacheFilePath == null || !Files.exists(cacheFilePath)) {
+        return;
+      }
+      logger.atFine().log("Warming OS page cache for %s", cacheFilePath.getFileName());
+      Stopwatch sw = Stopwatch.createStarted();
+      byte[] buf = new byte[65536];
+      try (InputStream in = Files.newInputStream(cacheFilePath)) {
+        while (in.read(buf) != -1) {}
+      } catch (IOException e) {
+        logger.atWarning().log(
+            "Failed to warm OS page cache for %s: %s", cacheFilePath.getFileName(), e.getMessage());
+      }
+      logger.atFine().log(
+          "Finished warming OS page cache for %s after %s", cacheFilePath.getFileName(), sw);
+    }
+
     void open() {
       bloomFilter.initIfNeeded();
     }
@@ -459,6 +489,9 @@
     }
 
     private void buildBloomFilter() {
+      if (preWarmForBloomFilter) {
+        warmupOsPageCache();
+      }
       SqlHandle c = null;
       try (TraceTimer ignored = TraceContext.newTimer("Build bloom filter", Metadata.empty())) {
         c = acquire();
@@ -748,6 +781,7 @@
     }
 
     DiskStats diskStats() {
+      warmupOsPageCache();
       long size = 0;
       long space = 0;
       SqlHandle c = null;
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheModule.java b/java/com/google/gerrit/server/cache/h2/H2CacheModule.java
index baf08e6..0ac8123 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheModule.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheModule.java
@@ -115,6 +115,24 @@
   }
 
   @Provides
+  @Singleton
+  @Nullable
+  @CacheStoreStartupExecutor
+  ExecutorService createDiskCacheStoreStartupExecutor(
+      @Nullable @CacheDir Path cacheDir, @GerritServerConfig Config cfg) {
+    if (cacheDir == null) {
+      return null;
+    }
+    int startupThreads = cfg.getInt("cache", null, "startupThreads", 1);
+    if (startupThreads > 1) {
+      return new LoggingContextAwareExecutorService(
+          Executors.newFixedThreadPool(
+              1, new ThreadFactoryBuilder().setNameFormat("DiskCache-Store-Startup-%d").build()));
+    }
+    return null;
+  }
+
+  @Provides
   Set<CacheOptions> getOptions() {
     return options;
   }
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 389932f..a2472e6 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -122,6 +122,8 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeData.ChangedLines;
 import com.google.gerrit.server.util.AttentionSetUtil;
+import com.google.gerrit.server.util.ManualRequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -249,6 +251,7 @@
   private final Metrics metrics;
   private final RevisionJson revisionJson;
   private final Optional<PluginDefinedInfosFactory> pluginDefinedInfosFactory;
+  private final ThreadLocalRequestContext requestContext;
   private final boolean includeMergeable;
   private final boolean lazyLoad;
   private final boolean cacheQueryResultsByChangeNum;
@@ -274,6 +277,7 @@
       RemoveReviewerControl removeReviewerControl,
       TrackingFooters trackingFooters,
       Metrics metrics,
+      ThreadLocalRequestContext requestContext,
       RevisionJson.Factory revisionJsonFactory,
       @GerritServerConfig Config cfg,
       ExperimentFeatures experimentFeatures,
@@ -294,6 +298,7 @@
     this.removeReviewerControl = removeReviewerControl;
     this.trackingFooters = trackingFooters;
     this.metrics = metrics;
+    this.requestContext = requestContext;
     this.revisionJson = revisionJsonFactory.create(options);
     this.options = Sets.immutableEnumSet(options);
     this.includeMergeable = MergeabilityComputationBehavior.fromConfig(cfg).includeInApi();
@@ -367,8 +372,12 @@
     List<ChangeInfo> out =
         in.parallelStream()
             .map(
-                cd ->
-                    format(cd, Optional.empty(), false, pluginInfosByChange.get(cd.getId()), user))
+                cd -> {
+                  try (ManualRequestContext ctx = new ManualRequestContext(user, requestContext)) {
+                    return format(
+                        cd, Optional.empty(), false, pluginInfosByChange.get(cd.getId()), user);
+                  }
+                })
             .collect(toList());
     accountLoader.fill();
     return out;
@@ -571,46 +580,48 @@
               .parallel()
               .mapToObj(
                   i -> {
-                    ChangeData cd = changes.get(i);
-                    // We can only cache and re-use an entity if it's not the last in the list. The
-                    // last entity may later get _moreChanges set. If it was cached or re-used, that
-                    // setting would propagate to the original entity yielding wrong results.
-                    // This problem has two sides where 'last in the list' has to be respected:
-                    // (1) Caching
-                    // (2) Reusing
-                    boolean isCacheable = cacheQueryResultsByChangeNum && (i != changes.size() - 1);
-                    if (cd.hasFailedParsingFromIndex()) {
-                      return createFaultyChangeInfo(cd).orElse(null);
-                    }
-                    try {
-                      Change.Id cdUniqueId = cd.virtualId();
-                      if (isCacheable) {
-                        ChangeInfo info = cache.get(cdUniqueId);
-                        if (info != null) {
-                          return info;
+                    try (ManualRequestContext ctx =
+                        new ManualRequestContext(user, requestContext)) {
+                      ChangeData cd = changes.get(i);
+                      // Cache/re-use only if it is not the last entity in the list.
+                      // The last entity may have _moreChanges set later, which would
+                      // propagate to the original cached/re-used entity, yielding
+                      // incorrect results. This applies to both caching and reusing.
+                      boolean isCacheable =
+                          cacheQueryResultsByChangeNum && (i != changes.size() - 1);
+                      if (cd.hasFailedParsingFromIndex()) {
+                        return createFaultyChangeInfo(cd).orElse(null);
+                      }
+                      try {
+                        Change.Id cdUniqueId = cd.virtualId();
+                        if (isCacheable) {
+                          ChangeInfo info = cache.get(cdUniqueId);
+                          if (info != null) {
+                            return info;
+                          }
                         }
-                      }
 
-                      ChangeInfo info =
-                          format(
-                              cd,
-                              Optional.empty(),
-                              false,
-                              pluginInfosByChange.get(cd.getId()),
-                              user);
-                      if (isCacheable) {
-                        cache.put(cdUniqueId, info);
+                        ChangeInfo info =
+                            format(
+                                cd,
+                                Optional.empty(),
+                                false,
+                                pluginInfosByChange.get(cd.getId()),
+                                user);
+                        if (isCacheable) {
+                          cache.put(cdUniqueId, info);
+                        }
+                        return info;
+                      } catch (RuntimeException e) {
+                        Optional<RequestCancelledException> requestCancelledException =
+                            RequestCancelledException.getFromCausalChain(e);
+                        if (requestCancelledException.isPresent()) {
+                          throw e;
+                        }
+                        logger.atWarning().withCause(e).log(
+                            "Omitting corrupt change %s from results", cd.getId());
+                        return null;
                       }
-                      return info;
-                    } catch (RuntimeException e) {
-                      Optional<RequestCancelledException> requestCancelledException =
-                          RequestCancelledException.getFromCausalChain(e);
-                      if (requestCancelledException.isPresent()) {
-                        throw e;
-                      }
-                      logger.atWarning().withCause(e).log(
-                          "Omitting corrupt change %s from results", cd.getId());
-                      return null;
                     }
                   })
               .filter(java.util.Objects::nonNull)
diff --git a/java/com/google/gerrit/server/change/CommentsValidator.java b/java/com/google/gerrit/server/change/CommentsValidator.java
index c02fb83..9b75b5e 100644
--- a/java/com/google/gerrit/server/change/CommentsValidator.java
+++ b/java/com/google/gerrit/server/change/CommentsValidator.java
@@ -33,12 +33,12 @@
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.patch.PatchListKey;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
 import org.eclipse.jgit.lib.ObjectId;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/change/ParentDataProvider.java b/java/com/google/gerrit/server/change/ParentDataProvider.java
index f5fe120..16b9f71 100644
--- a/java/com/google/gerrit/server/change/ParentDataProvider.java
+++ b/java/com/google/gerrit/server/change/ParentDataProvider.java
@@ -28,11 +28,8 @@
 import java.util.List;
 import java.util.Optional;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.ReachabilityChecker;
-import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
 
 @Singleton
@@ -70,19 +67,12 @@
   /** Returns true if the parent commit {@code parentCommitId} is merged in the target branch. */
   private boolean isMergedInTargetBranch(
       Project.NameKey project, Repository repo, ObjectId parentCommitId, String targetBranch) {
-    try (RevWalk rw = new RevWalk(repo);
-        ObjectReader reader = repo.newObjectReader()) {
+    try (RevWalk rw = new RevWalk(repo)) {
       Ref targetBranchRef = repo.exactRef(targetBranch);
-      if (targetBranchRef == null) {
-        return false;
+      if (targetBranchRef != null) {
+        return rw.isMergedInto(
+            rw.parseCommit(parentCommitId), rw.parseCommit(targetBranchRef.getObjectId()));
       }
-      RevCommit parent = rw.parseCommit(parentCommitId);
-      RevCommit targetBranchCommit = rw.parseCommit(targetBranchRef.getObjectId());
-      ReachabilityChecker checker = reader.createReachabilityChecker(rw);
-      Optional<RevCommit> unreachable =
-          checker.areAllReachable(
-              ImmutableList.of(parent), ImmutableList.of(targetBranchCommit).stream());
-      return unreachable.isEmpty();
     } catch (IOException e) {
       logger.atWarning().withCause(e).log(
           "Failed to check if parent commit %s (project: %s) is merged into target branch %s",
diff --git a/java/com/google/gerrit/server/config/AuthConfig.java b/java/com/google/gerrit/server/config/AuthConfig.java
index 7886cb5..f43a8b8 100644
--- a/java/com/google/gerrit/server/config/AuthConfig.java
+++ b/java/com/google/gerrit/server/config/AuthConfig.java
@@ -18,6 +18,7 @@
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_UUID;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
 import com.google.gerrit.server.account.externalids.ExternalId;
@@ -33,6 +34,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import org.eclipse.jgit.lib.Config;
 
@@ -41,6 +43,7 @@
 public class AuthConfig {
   private final AuthType authType;
   private final String httpHeader;
+  private final ImmutableSet<String> trustedProxyNetworks;
   private final String httpDisplaynameHeader;
   private final String httpEmailHeader;
   private final String httpExternalIdHeader;
@@ -78,6 +81,8 @@
   AuthConfig(@GerritServerConfig Config cfg) throws XsrfException {
     authType = toType(cfg);
     httpHeader = cfg.getString("auth", null, "httpheader");
+    trustedProxyNetworks =
+        ImmutableSet.copyOf(cfg.getStringList("auth", null, "httpTrustedProxyNetworks"));
     httpDisplaynameHeader = cfg.getString("auth", null, "httpdisplaynameheader");
     httpEmailHeader = cfg.getString("auth", null, "httpemailheader");
     httpExternalIdHeader = cfg.getString("auth", null, "httpexternalidheader");
@@ -384,4 +389,8 @@
   public boolean isHttpPasswordFallbackEnabled() {
     return httpPasswordFallbackEnabled;
   }
+
+  public Set<String> getTrustedProxyNetworks() {
+    return trustedProxyNetworks;
+  }
 }
diff --git a/java/com/google/gerrit/server/config/FileBasedAllProjectsConfigProvider.java b/java/com/google/gerrit/server/config/FileBasedAllProjectsConfigProvider.java
index db21e1f..77c5617 100644
--- a/java/com/google/gerrit/server/config/FileBasedAllProjectsConfigProvider.java
+++ b/java/com/google/gerrit/server/config/FileBasedAllProjectsConfigProvider.java
@@ -17,8 +17,8 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.server.project.ProjectConfig;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.util.Optional;
-import javax.inject.Singleton;
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.util.FS;
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 5f5db71..ec251d0 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -284,7 +284,6 @@
     install(PureRevertCache.module());
     install(CommentContextCacheImpl.module());
     install(SubmitRequirementsEvaluatorImpl.module());
-    install(ServerConfigCacheImpl.module());
 
     install(new AccessControlModule());
     install(new AccountModule());
diff --git a/java/com/google/gerrit/server/config/ServerConfigCacheImpl.java b/java/com/google/gerrit/server/config/ServerConfigCacheImpl.java
deleted file mode 100644
index 2707cc2..0000000
--- a/java/com/google/gerrit/server/config/ServerConfigCacheImpl.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2026 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.config;
-
-import com.google.auto.value.AutoValue;
-import com.google.gerrit.extensions.common.ServerInfo;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.inject.Module;
-import java.time.Duration;
-
-public class ServerConfigCacheImpl {
-  public static final String CACHE_CONFIG = "server_config";
-  public static final String SINGLETON_KEY = "GLOBAL";
-
-  @AutoValue
-  public abstract static class ServerConfigData {
-    public abstract ServerInfo serverInfo();
-
-    public abstract String serverVersion();
-
-    public static ServerConfigData create(ServerInfo serverInfo, String serverVersion) {
-      return new AutoValue_ServerConfigCacheImpl_ServerConfigData(serverInfo, serverVersion);
-    }
-  }
-
-  public static Module module() {
-    return new CacheModule() {
-      @Override
-      protected void configure() {
-        cache(CACHE_CONFIG, String.class, ServerConfigData.class)
-            .expireAfterWrite(Duration.ofMinutes(5));
-      }
-    };
-  }
-}
diff --git a/java/com/google/gerrit/server/git/CommitUtil.java b/java/com/google/gerrit/server/git/CommitUtil.java
index 58d862e..0eb142d 100644
--- a/java/com/google/gerrit/server/git/CommitUtil.java
+++ b/java/com/google/gerrit/server/git/CommitUtil.java
@@ -41,6 +41,7 @@
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.approval.ApprovalsUtil;
 import com.google.gerrit.server.change.ChangeInserter;
 import com.google.gerrit.server.change.ChangeMessages;
@@ -115,6 +116,7 @@
   private final ChangeReverted changeReverted;
   private final BatchUpdate.Factory updateFactory;
   private final MessageIdGenerator messageIdGenerator;
+  private final AccountCache accountCache;
 
   @Inject
   CommitUtil(
@@ -129,7 +131,8 @@
       ChangeNotes.Factory changeNotesFactory,
       ChangeReverted changeReverted,
       BatchUpdate.Factory updateFactory,
-      MessageIdGenerator messageIdGenerator) {
+      MessageIdGenerator messageIdGenerator,
+      AccountCache accountCache) {
     this.repoManager = repoManager;
     this.serverIdent = serverIdent;
     this.seq = seq;
@@ -142,6 +145,7 @@
     this.changeReverted = changeReverted;
     this.updateFactory = updateFactory;
     this.messageIdGenerator = messageIdGenerator;
+    this.accountCache = accountCache;
   }
 
   public static CommitInfo toCommitInfo(RevCommit commit) throws IOException {
@@ -473,12 +477,25 @@
         ValidationOptionsUtil.getValidateOptionsAsMultimap(input.validationOptions));
 
     ReviewerSet reviewerSet = approvalsUtil.getReviewers(notes);
-
     Set<Account.Id> reviewers = new HashSet<>();
-    reviewers.add(changeToRevert.getOwner());
-    reviewers.addAll(reviewerSet.byState(ReviewerStateInternal.REVIEWER));
+    if (accountExists(changeToRevert.getOwner())) {
+      reviewers.add(changeToRevert.getOwner());
+    }
+    for (Account.Id reviewer : reviewerSet.byState(ReviewerStateInternal.REVIEWER)) {
+      // Add the original reviewers only if they exist, to avoid adding deleted accounts.
+      if (accountExists(reviewer)) {
+        reviewers.add(reviewer);
+      }
+    }
     reviewers.remove(user.getAccountId());
-    Set<Account.Id> ccs = new HashSet<>(reviewerSet.byState(ReviewerStateInternal.CC));
+
+    Set<Account.Id> ccs = new HashSet<>();
+    for (Account.Id cc : reviewerSet.byState(ReviewerStateInternal.CC)) {
+      // Add the original CCs only if they exist, to avoid adding deleted accounts.
+      if (accountExists(cc)) {
+        ccs.add(cc);
+      }
+    }
     ccs.remove(user.getAccountId());
     ins.setReviewersAndCcsIgnoreVisibility(reviewers, ccs);
     ins.setRevertOf(notes.getChangeId());
@@ -515,6 +532,10 @@
     bu.addOp(revertedChangeId, new PostRevertedMessageOp(revertingChangeKey));
   }
 
+  private boolean accountExists(Account.Id accountId) {
+    return accountCache.get(accountId).isPresent();
+  }
+
   private class ChangeRevertedNotifyOp implements BatchUpdateOp {
     private final Change.Id revertedChangeId;
     private final Change.Id revertingChangeId;
diff --git a/java/com/google/gerrit/server/index/change/AllChangesIndexer.java b/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
index 52f3844..6c79ef9 100644
--- a/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
+++ b/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
@@ -513,7 +513,7 @@
                 logger.atInfo().log("Removing %d changes from index", changesInIndex.size());
                 for (Change.Id id : changesInIndex) {
                   logger.atFine().log("Deleting change %s from index", id);
-                  indexer.delete(id);
+                  indexer.delete(name, id);
                 }
               }
 
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index 1b72b7a..196d56a 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -50,7 +50,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
@@ -376,9 +375,9 @@
     }
   }
 
-  private void fireChangeDeletedFromIndexEvent(int id) {
+  private void fireChangeDeletedFromIndexEvent(String projectName, int id) {
     if (notifyListeners) {
-      indexedListeners.runEach(l -> l.onChangeDeleted(id));
+      indexedListeners.runEach(l -> l.onChangeDeleted(projectName, id));
     }
   }
 
@@ -430,7 +429,7 @@
    */
   public ListenableFuture<ChangeData> deleteAsync(Project.NameKey project, Change.Id id) {
     fireChangeScheduledForDeletionFromIndexEvent(id.get());
-    return submit(new DeleteTask(id, Optional.of(project)));
+    return submit(new DeleteTask(id, project));
   }
 
   /**
@@ -438,9 +437,9 @@
    *
    * @param id change ID to delete.
    */
-  public void delete(Change.Id id) {
+  public void delete(Project.NameKey project, Change.Id id) {
     fireChangeScheduledForDeletionFromIndexEvent(id.get());
-    doDelete(id);
+    doDelete(project, id);
   }
 
   /**
@@ -457,11 +456,7 @@
   }
 
   private void doDelete(Project.NameKey project, Change.Id id) {
-    new DeleteTask(id, Optional.of(project)).call();
-  }
-
-  private void doDelete(Change.Id id) {
-    new DeleteTask(id, Optional.empty()).call();
+    new DeleteTask(id, project).call();
   }
 
   /**
@@ -655,9 +650,9 @@
   // Not AbstractIndexTask as it doesn't need a request context.
   private class DeleteTask implements Callable<ChangeData> {
     private final Change.Id id;
-    private final Optional<Project.NameKey> project;
+    private final Project.NameKey project;
 
-    private DeleteTask(Change.Id id, Optional<Project.NameKey> project) {
+    private DeleteTask(Change.Id id, Project.NameKey project) {
       this.id = id;
       this.project = project;
     }
@@ -678,12 +673,7 @@
                     .changeId(id.get())
                     .indexVersion(i.getSchema().getVersion())
                     .build())) {
-          // Some index implementation require ProjectKey to build a database key
-          // If delete(K) method is used, this will require changeId -> projectKey lookup (index
-          // query), which is expensive.
-          // Use changeData with ProjectKey and deleteByValue(V) method, if possible
-          project.ifPresentOrElse(
-              p -> i.deleteByValue(changeDataFactory.create(p, id)), () -> i.delete(id));
+          i.deleteByValue(changeDataFactory.create(project, id));
         } catch (RuntimeException e) {
           throw new StorageException(
               String.format(
@@ -692,7 +682,7 @@
               e);
         }
       }
-      fireChangeDeletedFromIndexEvent(id.get());
+      fireChangeDeletedFromIndexEvent(project.name(), id.get());
       return null;
     }
   }
diff --git a/java/com/google/gerrit/server/mail/send/DefaultEmailFactories.java b/java/com/google/gerrit/server/mail/send/DefaultEmailFactories.java
index 079f660..dee7f8a 100644
--- a/java/com/google/gerrit/server/mail/send/DefaultEmailFactories.java
+++ b/java/com/google/gerrit/server/mail/send/DefaultEmailFactories.java
@@ -30,12 +30,12 @@
 import com.google.gerrit.server.mail.send.InboundEmailRejectionEmailDecorator.InboundEmailError;
 import com.google.gerrit.server.mail.send.OutgoingEmail.EmailDecorator;
 import com.google.gerrit.server.patch.filediff.FileDiffOutput;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
 import org.eclipse.jgit.lib.ObjectId;
 
 /** Default versions of Gerrit email notifications. */
diff --git a/java/com/google/gerrit/server/mail/send/MailSoySauceModule.java b/java/com/google/gerrit/server/mail/send/MailSoySauceModule.java
index 25b2ebd..46bf8cc 100644
--- a/java/com/google/gerrit/server/mail/send/MailSoySauceModule.java
+++ b/java/com/google/gerrit/server/mail/send/MailSoySauceModule.java
@@ -23,13 +23,13 @@
 import com.google.gerrit.server.CacheRefreshExecutor;
 import com.google.gerrit.server.cache.CacheModule;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
 import com.google.inject.name.Named;
 import com.google.template.soy.jbcsrc.api.SoySauce;
 import java.time.Duration;
 import java.util.concurrent.ExecutionException;
-import javax.inject.Provider;
 
 /**
  * Provides support for soy templates
diff --git a/java/com/google/gerrit/server/notedb/DraftCommentsNotesReader.java b/java/com/google/gerrit/server/notedb/DraftCommentsNotesReader.java
index 6f48aa0..b7cee28 100644
--- a/java/com/google/gerrit/server/notedb/DraftCommentsNotesReader.java
+++ b/java/com/google/gerrit/server/notedb/DraftCommentsNotesReader.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.query.change.ChangeNumberVirtualIdAlgorithm;
+import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -35,7 +36,6 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
-import javax.inject.Inject;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 
diff --git a/java/com/google/gerrit/server/patch/DiffOperationsImpl.java b/java/com/google/gerrit/server/patch/DiffOperationsImpl.java
index a5a58d0..0e3342f 100644
--- a/java/com/google/gerrit/server/patch/DiffOperationsImpl.java
+++ b/java/com/google/gerrit/server/patch/DiffOperationsImpl.java
@@ -102,7 +102,7 @@
   }
 
   @Inject
-  public DiffOperationsImpl(
+  DiffOperationsImpl(
       GitRepositoryManager repoManager,
       ModifiedFilesCache modifiedFilesCache,
       ModifiedFilesCacheImpl modifiedFilesCacheImpl,
@@ -150,6 +150,80 @@
     }
   }
 
+  private ImmutableMap<String, FileDiffOutput> getModifiedFiles(
+      DiffParameters diffParams, DiffOptions diffOptions) throws DiffNotAvailableException {
+    logger.atFine().log(
+        "getModifiedFiles (diffParams: %s, diffOptions: %s)", diffParams, diffOptions);
+    try {
+      Project.NameKey project = diffParams.project();
+      ObjectId newCommit = diffParams.newCommit();
+      ObjectId oldCommit = diffParams.baseCommit();
+      ComparisonType cmp = diffParams.comparisonType();
+
+      ImmutableList<ModifiedFile> modifiedFiles;
+      if (diffOptions.skipRebaseFiltering()) {
+        try (Repository repo = repoManager.openRepository(project);
+            RevWalk revWalk = new RevWalk(repo)) {
+          ModifiedFilesLoader loader =
+              modifiedFilesLoaderFactory
+                  .createWithRetrievingModifiedFilesForTreesFromGitModifiedFilesCache()
+                  .withSkipRebaseFiltering(true);
+          loader.withRenameDetection(RENAME_SCORE);
+          modifiedFiles = loader.load(project, repo.getConfig(), revWalk, oldCommit, newCommit);
+        }
+      } else {
+        modifiedFiles =
+            modifiedFilesCache.get(createModifiedFilesKey(project, oldCommit, newCommit));
+      }
+
+      boolean useTimeout =
+          experimentFeatures.isFeatureEnabled(
+              ExperimentFeaturesConstants.TIMEOUT_FILE_DIFF_COMPUTATION, project);
+      List<FileDiffCacheKey> fileCacheKeys = new ArrayList<>();
+      fileCacheKeys.add(
+          createFileDiffCacheKey(
+              project,
+              oldCommit,
+              newCommit,
+              COMMIT_MSG,
+              DEFAULT_DIFF_ALGORITHM,
+              useTimeout,
+              /* whitespace= */ null));
+
+      if (cmp.isAgainstAutoMerge() || isMergeAgainstParent(cmp, project, newCommit)) {
+        fileCacheKeys.add(
+            createFileDiffCacheKey(
+                project,
+                oldCommit,
+                newCommit,
+                MERGE_LIST,
+                DEFAULT_DIFF_ALGORITHM,
+                useTimeout,
+                /* whitespace= */ null));
+      }
+
+      if (diffParams.skipFiles() == null) {
+        modifiedFiles.stream()
+            .map(
+                entity ->
+                    createFileDiffCacheKey(
+                        project,
+                        oldCommit,
+                        newCommit,
+                        entity.newPath().isPresent()
+                            ? entity.newPath().get()
+                            : entity.oldPath().get(),
+                        DEFAULT_DIFF_ALGORITHM,
+                        useTimeout,
+                        /* whitespace= */ null))
+            .forEach(fileCacheKeys::add);
+      }
+      return getModifiedFilesForKeys(fileCacheKeys, diffOptions);
+    } catch (IOException e) {
+      throw new DiffNotAvailableException(e);
+    }
+  }
+
   @Override
   public Map<String, FileDiffOutput> listModifiedFilesAgainstParent(
       Project.NameKey project, ObjectId newCommit, int parent, DiffOptions diffOptions)
@@ -296,67 +370,6 @@
     return getModifiedFileForKey(key, diffOptions);
   }
 
-  private ImmutableMap<String, FileDiffOutput> getModifiedFiles(
-      DiffParameters diffParams, DiffOptions diffOptions) throws DiffNotAvailableException {
-    logger.atFine().log(
-        "getModifiedFiles (diffParams: %s, diffOptions: %s)", diffParams, diffOptions);
-    try {
-      Project.NameKey project = diffParams.project();
-      ObjectId newCommit = diffParams.newCommit();
-      ObjectId oldCommit = diffParams.baseCommit();
-      ComparisonType cmp = diffParams.comparisonType();
-
-      ImmutableList<ModifiedFile> modifiedFiles =
-          modifiedFilesCache.get(createModifiedFilesKey(project, oldCommit, newCommit));
-
-      boolean useTimeout =
-          experimentFeatures.isFeatureEnabled(
-              ExperimentFeaturesConstants.TIMEOUT_FILE_DIFF_COMPUTATION, project);
-      List<FileDiffCacheKey> fileCacheKeys = new ArrayList<>();
-      fileCacheKeys.add(
-          createFileDiffCacheKey(
-              project,
-              oldCommit,
-              newCommit,
-              COMMIT_MSG,
-              DEFAULT_DIFF_ALGORITHM,
-              useTimeout,
-              /* whitespace= */ null));
-
-      if (cmp.isAgainstAutoMerge() || isMergeAgainstParent(cmp, project, newCommit)) {
-        fileCacheKeys.add(
-            createFileDiffCacheKey(
-                project,
-                oldCommit,
-                newCommit,
-                MERGE_LIST,
-                DEFAULT_DIFF_ALGORITHM,
-                useTimeout,
-                /* whitespace= */ null));
-      }
-
-      if (diffParams.skipFiles() == null) {
-        modifiedFiles.stream()
-            .map(
-                entity ->
-                    createFileDiffCacheKey(
-                        project,
-                        oldCommit,
-                        newCommit,
-                        entity.newPath().isPresent()
-                            ? entity.newPath().get()
-                            : entity.oldPath().get(),
-                        DEFAULT_DIFF_ALGORITHM,
-                        useTimeout,
-                        /* whitespace= */ null))
-            .forEach(fileCacheKeys::add);
-      }
-      return getModifiedFilesForKeys(fileCacheKeys, diffOptions);
-    } catch (IOException e) {
-      throw new DiffNotAvailableException(e);
-    }
-  }
-
   private FileDiffOutput getModifiedFileForKey(FileDiffCacheKey key, DiffOptions diffOptions)
       throws DiffNotAvailableException {
     ImmutableMap<String, FileDiffOutput> diffList =
@@ -425,7 +438,7 @@
         diffs.put(fileDiffOutput.newPath().get(), fileDiffOutput);
       }
     }
-    return diffs.build();
+    return diffs.buildOrThrow();
   }
 
   private static boolean allDueToRebase(FileDiffOutput fileDiffOutput) {
diff --git a/java/com/google/gerrit/server/patch/DiffOptions.java b/java/com/google/gerrit/server/patch/DiffOptions.java
index 4d54be1..7927b10 100644
--- a/java/com/google/gerrit/server/patch/DiffOptions.java
+++ b/java/com/google/gerrit/server/patch/DiffOptions.java
@@ -19,18 +19,32 @@
 @AutoValue
 public abstract class DiffOptions {
   public static final DiffOptions DEFAULTS =
-      DiffOptions.builder().skipFilesWithAllEditsDueToRebase(true).build();
+      DiffOptions.builder()
+          .skipFilesWithAllEditsDueToRebase(true)
+          .skipRebaseFiltering(false)
+          .build();
 
   public abstract boolean skipFilesWithAllEditsDueToRebase();
 
+  /**
+   * Whether to skip the rebase-filtering algorithm in ModifiedFilesLoader.
+   *
+   * <p>If true, the full list of files changed between the two commits will be returned, even if
+   * they are not parent-child or do not share a common parent (e.g. general repository-level
+   * diffs).
+   */
+  public abstract boolean skipRebaseFiltering();
+
   public static DiffOptions.Builder builder() {
-    return new AutoValue_DiffOptions.Builder();
+    return new AutoValue_DiffOptions.Builder().skipRebaseFiltering(false);
   }
 
   @AutoValue.Builder
   public abstract static class Builder {
     public abstract Builder skipFilesWithAllEditsDueToRebase(boolean value);
 
+    public abstract Builder skipRebaseFiltering(boolean value);
+
     public abstract DiffOptions build();
   }
 }
diff --git a/java/com/google/gerrit/server/patch/diff/ModifiedFilesLoader.java b/java/com/google/gerrit/server/patch/diff/ModifiedFilesLoader.java
index 30be303..1091d54 100644
--- a/java/com/google/gerrit/server/patch/diff/ModifiedFilesLoader.java
+++ b/java/com/google/gerrit/server/patch/diff/ModifiedFilesLoader.java
@@ -98,7 +98,8 @@
      * trees that are newly created or that were created in memory only. Also see the javadoc on
      * this class.
      */
-    ModifiedFilesLoader createWithRetrievingModifiedFilesForTreesFromGitModifiedFilesCache() {
+    public ModifiedFilesLoader
+        createWithRetrievingModifiedFilesForTreesFromGitModifiedFilesCache() {
       return new ModifiedFilesLoader(gitModifiedFilesCache);
     }
   }
@@ -107,6 +108,8 @@
 
   @Nullable private Integer renameScore = null;
 
+  private boolean skipRebaseFiltering = false;
+
   ModifiedFilesLoader(@Nullable GitModifiedFilesCache gitModifiedFilesCache) {
     this.gitModifiedFilesCache = gitModifiedFilesCache;
   }
@@ -123,6 +126,12 @@
     return this;
   }
 
+  @CanIgnoreReturnValue
+  public ModifiedFilesLoader withSkipRebaseFiltering(boolean skipRebaseFiltering) {
+    this.skipRebaseFiltering = skipRebaseFiltering;
+    return this;
+  }
+
   /**
    * Loads the files that have been modified between {@code baseCommit} and {@code newCommit}.
    *
@@ -155,6 +164,9 @@
       if (baseCommit.equals(ObjectId.zeroId())) {
         return modifiedFiles;
       }
+      if (skipRebaseFiltering) {
+        return modifiedFiles;
+      }
       RevCommit revCommitBase = DiffUtil.getRevCommit(revWalk, baseCommit);
       RevCommit revCommitNew = DiffUtil.getRevCommit(revWalk, newCommit);
       if (DiffUtil.areRelated(revCommitBase, revCommitNew)) {
diff --git a/java/com/google/gerrit/server/permissions/ChangeControl.java b/java/com/google/gerrit/server/permissions/ChangeControl.java
index 2d2ec18..3941975 100644
--- a/java/com/google/gerrit/server/permissions/ChangeControl.java
+++ b/java/com/google/gerrit/server/permissions/ChangeControl.java
@@ -21,8 +21,8 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
-import javax.inject.Inject;
 
 /** Access control management for a user accessing a single change. */
 public class ChangeControl extends AbstractChangeControl {
diff --git a/java/com/google/gerrit/server/permissions/RefVisibilityControl.java b/java/com/google/gerrit/server/permissions/RefVisibilityControl.java
index 756e922..665f995 100644
--- a/java/com/google/gerrit/server/permissions/RefVisibilityControl.java
+++ b/java/com/google/gerrit/server/permissions/RefVisibilityControl.java
@@ -29,8 +29,8 @@
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.query.change.ChangeData;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import org.eclipse.jgit.lib.Constants;
 
 /**
diff --git a/java/com/google/gerrit/server/plugins/AutoRegisterUtil.java b/java/com/google/gerrit/server/plugins/AutoRegisterUtil.java
index d592d17..20a032d 100644
--- a/java/com/google/gerrit/server/plugins/AutoRegisterUtil.java
+++ b/java/com/google/gerrit/server/plugins/AutoRegisterUtil.java
@@ -23,7 +23,7 @@
   public static Annotation calculateBindAnnotation(Class<Object> impl) {
     Annotation n = impl.getAnnotation(Export.class);
     if (n == null) {
-      n = impl.getAnnotation(javax.inject.Named.class);
+      n = impl.getAnnotation(jakarta.inject.Named.class);
     }
     if (n == null) {
       n = impl.getAnnotation(com.google.inject.name.Named.class);
diff --git a/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java b/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java
index 957b2e2..d0943c7 100644
--- a/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java
+++ b/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java
@@ -175,7 +175,9 @@
               .parse(expression.expressionString());
       PredicateResult predicateResult = changeData.evaluatePredicateTree(predicate);
       return SubmitRequirementExpressionResult.create(expression, predicateResult);
-    } catch (QueryParseException | SubmitRequirementEvaluationException e) {
+    } catch (QueryParseException
+        | SubmitRequirementEvaluationException
+        | IllegalArgumentException e) {
       logger.atWarning().withCause(e).log(
           "Failed to evaluate submit requirement expression: %s", expression.expressionString());
       return SubmitRequirementExpressionResult.error(expression, e.getMessage());
@@ -262,8 +264,8 @@
 
         return timeoutResult(sr, cd);
       } catch (ExecutionException | InterruptedException e) {
-        logger.atSevere().log("Error evaluating Submit requirement: %s", sr.name());
-        throw new RuntimeException(e);
+        logger.atSevere().withCause(e).log("Error evaluating Submit requirement: %s", sr.name());
+        return errorResult(sr, cd, e);
       }
     }
   }
@@ -288,6 +290,29 @@
         .build();
   }
 
+  private SubmitRequirementResult errorResult(SubmitRequirement sr, ChangeData cd, Throwable e) {
+    String msg =
+        (e instanceof ExecutionException && e.getCause() != null)
+            ? e.getCause().getMessage()
+            : e.getMessage();
+    if (msg == null) {
+      msg = e.toString();
+    }
+    SubmitRequirementExpressionResult error =
+        SubmitRequirementExpressionResult.error(sr.submittabilityExpression(), msg);
+
+    return SubmitRequirementResult.builder()
+        .legacy(Optional.of(false))
+        .submitRequirement(sr)
+        .patchSetCommitId(cd.currentPatchSet().commitId())
+        .submittabilityExpressionResult(Optional.of(error))
+        .applicabilityExpressionResult(
+            sr.applicabilityExpression().map(SubmitRequirementExpressionResult::notEvaluated))
+        .overrideExpressionResult(
+            sr.overrideExpression().map(SubmitRequirementExpressionResult::notEvaluated))
+        .build();
+  }
+
   /**
    * Evaluate and return all {@link SubmitRequirement}s.
    *
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 3224c09..a3b1e3e 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -811,12 +811,10 @@
       }
       if (refStates != null) {
         ImmutableSet<RefState> refs = refStates.get(project);
-        if (refs != null) {
-          String metaRef = RefNames.changeMetaRef(getId());
-          for (RefState r : refs) {
-            if (r.ref().equals(metaRef)) {
-              return Optional.of(r.id());
-            }
+        String metaRef = RefNames.changeMetaRef(getId());
+        for (RefState r : refs) {
+          if (r.ref().equals(metaRef)) {
+            return Optional.of(r.id());
           }
         }
       }
diff --git a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index 4476d6d..6c1f35e 100644
--- a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -338,7 +338,7 @@
     for (List<String> part : Iterables.partition(groups, batchSize)) {
       for (ChangeData cd :
           queryExhaustively(querySupplier, byProjectGroupsPredicate(indexConfig, project, part))) {
-        if (!seen.add(cd.virtualId())) {
+        if (seen.add(cd.virtualId())) {
           result.add(cd);
         }
       }
diff --git a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 3ce7e38..9f4e6d2 100644
--- a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -30,6 +30,8 @@
 import com.google.gerrit.index.query.QueryResult;
 import com.google.gerrit.server.DynamicOptions;
 import com.google.gerrit.server.account.AccountAttributeLoader;
+import com.google.gerrit.server.cancellation.RequestCancelledException;
+import com.google.gerrit.server.cancellation.RequestStateProvider;
 import com.google.gerrit.server.config.TrackingFooters;
 import com.google.gerrit.server.data.ChangeAttribute;
 import com.google.gerrit.server.data.PatchSetAttribute;
@@ -224,6 +226,10 @@
           AccountAttributeLoader accountLoader = accountAttributeLoaderFactory.create();
           List<ChangeAttribute> changeAttributes = new ArrayList<>();
           for (ChangeData d : results.entities()) {
+            if (Thread.currentThread().isInterrupted()) {
+              throw new RequestCancelledException(
+                  RequestStateProvider.Reason.CLIENT_CLOSED_REQUEST, null);
+            }
             changeAttributes.add(
                 buildChangeAttribute(d, repos, revWalks, accountLoader, attributesNodeProviders));
           }
diff --git a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
index 8966ec4..05ac95e 100644
--- a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
+++ b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.index.query.QueryResult;
+import com.google.gerrit.server.account.AccountControl;
 import com.google.gerrit.server.account.AccountDirectory.FillOptions;
 import com.google.gerrit.server.account.AccountInfoComparator;
 import com.google.gerrit.server.account.AccountLoader;
@@ -69,6 +70,9 @@
   private final Provider<AccountQueryProcessor> queryProcessorProvider;
   private final boolean suggestConfig;
   private final int suggestFrom;
+  private final AccountControl.Factory accountControlFactory;
+
+  private final AccountVisibility accountVisibility;
 
   private AccountLoader accountLoader;
   private boolean suggest;
@@ -134,11 +138,15 @@
       AccountLoader.Factory accountLoaderFactory,
       AccountQueryBuilder queryBuilder,
       Provider<AccountQueryProcessor> queryProcessorProvider,
-      @GerritServerConfig Config cfg) {
+      @GerritServerConfig Config cfg,
+      AccountControl.Factory accountControlFactory,
+      AccountVisibility accountVisibility) {
     this.permissionBackend = permissionBackend;
     this.accountLoaderFactory = accountLoaderFactory;
     this.queryBuilder = queryBuilder;
     this.queryProcessorProvider = queryProcessorProvider;
+    this.accountControlFactory = accountControlFactory;
+    this.accountVisibility = accountVisibility;
     this.suggestFrom = cfg.getInt("suggest", null, "from", 0);
     this.options = EnumSet.noneOf(ListAccountsOption.class);
 
@@ -167,8 +175,14 @@
       return Response.ok(Collections.emptyList());
     }
 
+    AccountControl accountControl = accountControlFactory.get();
+    boolean canSeeDetails =
+        accountVisibility == AccountVisibility.ALL
+            || accountControl.getUser().isIdentifiedUser()
+            || accountControl.canViewAll();
+
     Set<FillOptions> fillOptions = EnumSet.of(FillOptions.ID);
-    if (options.contains(ListAccountsOption.DETAILS)) {
+    if (options.contains(ListAccountsOption.DETAILS) && canSeeDetails) {
       fillOptions.addAll(AccountLoader.DETAILED_OPTIONS);
     }
     boolean modifyAccountCapabilityChecked = false;
@@ -179,13 +193,12 @@
       fillOptions.add(FillOptions.SECONDARY_EMAILS);
     }
     if (suggest) {
-      fillOptions.addAll(AccountLoader.DETAILED_OPTIONS);
-      fillOptions.add(FillOptions.EMAIL);
+      if (canSeeDetails) {
+        fillOptions.addAll(AccountLoader.DETAILED_OPTIONS);
+        fillOptions.add(FillOptions.EMAIL);
 
-      if (modifyAccountCapabilityChecked) {
-        fillOptions.add(FillOptions.SECONDARY_EMAILS);
-      } else {
-        if (permissionBackend.currentUser().test(GlobalPermission.VIEW_SECONDARY_EMAILS)) {
+        if (modifyAccountCapabilityChecked
+            || permissionBackend.currentUser().test(GlobalPermission.VIEW_SECONDARY_EMAILS)) {
           fillOptions.add(FillOptions.SECONDARY_EMAILS);
         }
       }
@@ -197,6 +210,7 @@
       throw new MethodNotAllowedException("query disabled");
     }
 
+    queryProcessor.enforceVisibility(true);
     queryProcessor.setUserProvidedLimit(limit != null ? limit : 0, /* applyDefaultLimit */ true);
 
     if (start != null) {
diff --git a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
index aff920b..1eeb8d3 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
@@ -66,7 +66,6 @@
   private final Provider<CommentJson> commentJson;
   private final CommentsUtil commentsUtil;
   private final PatchSetUtil psUtil;
-  private final ChangeNotes.Factory changeNotesFactory;
   private final PluginSetContext<CommentValidator> commentValidators;
 
   @Inject
@@ -75,13 +74,11 @@
       Provider<CommentJson> commentJson,
       CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
-      ChangeNotes.Factory changeNotesFactory,
       PluginSetContext<CommentValidator> commentValidators) {
     this.updateFactory = updateFactory;
     this.commentJson = commentJson;
     this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
-    this.changeNotesFactory = changeNotesFactory;
     this.commentValidators = commentValidators;
   }
 
@@ -104,7 +101,7 @@
       throw new BadRequestException(
           String.format("Invalid inReplyTo, comment %s not found", in.inReplyTo));
     }
-    validateDraftComment(rsrc, in, changeNotesFactory, commentValidators, commentsUtil);
+    validateDraftComment(rsrc, in, commentValidators, commentsUtil);
     try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
       try (BatchUpdate bu =
           updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
@@ -120,13 +117,12 @@
   static void validateDraftComment(
       RevisionResource rsrc,
       DraftInput in,
-      ChangeNotes.Factory changeNotesFactory,
       PluginSetContext<CommentValidator> commentValidators,
       CommentsUtil commentsUtil)
       throws BadRequestException {
     HumanComment comment =
         createDraftComment(
-            changeNotesFactory.create(rsrc.getProject(), rsrc.getChange().getId()),
+            rsrc.getNotes(),
             rsrc.getUser(),
             TimeUtil.now(),
             in,
diff --git a/java/com/google/gerrit/server/restapi/change/PatchSetCreator.java b/java/com/google/gerrit/server/restapi/change/PatchSetCreator.java
index 7b7ee13..88ec9c9 100644
--- a/java/com/google/gerrit/server/restapi/change/PatchSetCreator.java
+++ b/java/com/google/gerrit/server/restapi/change/PatchSetCreator.java
@@ -44,14 +44,14 @@
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.util.List;
 import java.util.Optional;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.inject.Singleton;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 8ac5d0c..e388e3f 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -41,6 +41,7 @@
 import com.google.gerrit.entities.LabelType;
 import com.google.gerrit.entities.LabelTypes;
 import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
@@ -83,6 +84,7 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.extensions.events.ReviewerAdded;
 import com.google.gerrit.server.git.CommitUtil;
+import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.logging.Metadata;
 import com.google.gerrit.server.logging.TraceContext;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
@@ -112,6 +114,10 @@
 import java.util.Optional;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
 
 @Singleton
 public class PostReview implements RestModifyView<RevisionResource, ReviewInput> {
@@ -165,6 +171,8 @@
   private final CommentsValidator commentsValidator;
   private final CommitUtil commitUtil;
 
+  private final GitRepositoryManager gitManager;
+
   @Inject
   PostReview(
       RetryHelper retryHelper,
@@ -185,7 +193,8 @@
       ReviewerAdded reviewerAdded,
       ChangeJson.Factory changeJsonFactory,
       CommentsValidator commentsValidator,
-      CommitUtil commitUtil) {
+      CommitUtil commitUtil,
+      GitRepositoryManager gitManager) {
     this.retryHelper = retryHelper;
     this.postReviewOpFactory = postReviewOpFactory;
     this.changeResourceFactory = changeResourceFactory;
@@ -205,6 +214,7 @@
     this.changeJsonFactory = changeJsonFactory;
     this.commentsValidator = commentsValidator;
     this.commitUtil = commitUtil;
+    this.gitManager = gitManager;
   }
 
   @Override
@@ -339,23 +349,34 @@
       }
     }
 
-    BatchUpdates.Result batchUpdateResult =
-        runBatchUpdate(projectState, revision, input, ts, notify, reviewerResults, ccOrReviewer);
-    ChangeData cd =
-        batchUpdateResult.getChangeData(revision.getProject(), revision.getChange().getId());
-    for (ReviewerModification reviewerResult : reviewerResults) {
-      reviewerResult.gatherResults(cd);
-    }
+    try (Repository repo = gitManager.openRepository(projectState.getProject().getNameKey())) {
+      BatchUpdates.Result batchUpdateResult =
+          runBatchUpdate(
+              projectState, revision, input, ts, notify, reviewerResults, ccOrReviewer, repo);
+      ChangeData cd =
+          batchUpdateResult.getChangeData(revision.getProject(), revision.getChange().getId());
+      ObjectId oldId = cd.metaRevisionOrThrow();
+      ObjectId newId = repo.exactRef(RefNames.changeMetaRef(cd.change().getId())).getObjectId();
 
-    // Sending emails and events from ReviewersOps was suppressed so we can send a single batch
-    // email/event here.
-    batchEmailReviewers(revision.getUser(), revision.getChange(), reviewerResults, notify);
-    batchReviewerEvents(revision.getUser(), cd, revision.getPatchSet(), reviewerResults, ts);
+      if (!oldId.equals(newId)) {
+        // Re-read change to take into account results of the update.
+        cd.reloadChange();
+      }
 
-    if (input.responseFormatOptions != null) {
-      output.changeInfo = changeJsonFactory.create(input.responseFormatOptions).format(cd);
-    } else {
-      output.changeInfo = changeJsonFactory.noOptions().format(cd);
+      for (ReviewerModification reviewerResult : reviewerResults) {
+        reviewerResult.gatherResults(cd);
+      }
+
+      // Sending emails and events from ReviewersOps was suppressed so we can send a single batch
+      // email/event here.
+      batchEmailReviewers(revision.getUser(), revision.getChange(), reviewerResults, notify);
+      batchReviewerEvents(revision.getUser(), cd, revision.getPatchSet(), reviewerResults, ts);
+
+      if (input.responseFormatOptions != null) {
+        output.changeInfo = changeJsonFactory.create(input.responseFormatOptions).format(cd);
+      } else {
+        output.changeInfo = changeJsonFactory.noOptions().format(cd);
+      }
     }
 
     return Response.ok(output);
@@ -368,7 +389,8 @@
       Instant ts,
       NotifyResolver.Result notify,
       List<ReviewerModification> reviewerResults,
-      boolean ccOrReviewer)
+      boolean ccOrReviewer,
+      Repository repo)
       throws UpdateException, RestApiException {
     return retryHelper
         .changeUpdate(
@@ -376,8 +398,11 @@
             updateFactory -> {
               try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
                 try (BatchUpdate bu =
-                    updateFactory.create(
-                        revision.getChange().getProject(), revision.getUser(), ts)) {
+                        updateFactory.create(
+                            revision.getChange().getProject(), revision.getUser(), ts);
+                    ObjectInserter oi = repo.newObjectInserter();
+                    RevWalk revWalk = new RevWalk(oi.newReader())) {
+                  bu.setRepository(repo, revWalk, oi);
                   bu.setNotify(notify);
 
                   // Apply reviewer changes first. Revision emails should be sent to the
diff --git a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
index 20db1a7..fd275ab 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.DraftCommentsReader;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.change.DraftCommentResource;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
@@ -58,7 +57,6 @@
   private final DraftCommentsReader draftCommentsReader;
   private final PatchSetUtil psUtil;
   private final Provider<CommentJson> commentJson;
-  private final ChangeNotes.Factory changeNotesFactory;
   private final PluginSetContext<CommentValidator> commentValidators;
 
   @Inject
@@ -69,7 +67,6 @@
       DraftCommentsReader draftCommentsReader,
       PatchSetUtil psUtil,
       Provider<CommentJson> commentJson,
-      ChangeNotes.Factory changeNotesFactory,
       PluginSetContext<CommentValidator> commentValidators) {
     this.updateFactory = updateFactory;
     this.delete = delete;
@@ -77,7 +74,6 @@
     this.draftCommentsReader = draftCommentsReader;
     this.psUtil = psUtil;
     this.commentJson = commentJson;
-    this.changeNotesFactory = changeNotesFactory;
     this.commentValidators = commentValidators;
   }
 
@@ -101,7 +97,7 @@
           String.format("Invalid inReplyTo, comment %s not found", in.inReplyTo));
     }
     CreateDraftComment.validateDraftComment(
-        rsrc.getRevisionResource(), in, changeNotesFactory, commentValidators, commentsUtil);
+        rsrc.getRevisionResource(), in, commentValidators, commentsUtil);
     try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
       try (BatchUpdate bu =
           updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
diff --git a/java/com/google/gerrit/server/restapi/config/GetCache.java b/java/com/google/gerrit/server/restapi/config/GetCache.java
index 23615fa..0f06128 100644
--- a/java/com/google/gerrit/server/restapi/config/GetCache.java
+++ b/java/com/google/gerrit/server/restapi/config/GetCache.java
@@ -26,6 +26,6 @@
 
   @Override
   public Response<CacheInfo> apply(CacheResource rsrc) {
-    return Response.ok(CacheInfoFactory.create(rsrc.getName(), rsrc.getCache()));
+    return Response.ok(CacheInfoFactory.create(rsrc.getName(), rsrc.getCache(), true));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/IndexChanges.java b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
index 6b4831e..c011b56 100644
--- a/java/com/google/gerrit/server/restapi/config/IndexChanges.java
+++ b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
@@ -14,76 +14,130 @@
 
 package com.google.gerrit.server.restapi.config;
 
-import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.change.ChangeFinder;
 import com.google.gerrit.server.config.ConfigResource;
 import com.google.gerrit.server.index.change.ChangeIndexer;
 import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gerrit.server.restapi.config.IndexChanges.Input;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.Singleton;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
-import java.util.Optional;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
 @Singleton
 public class IndexChanges implements RestModifyView<ConfigResource, Input> {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+  private static final Pattern PROJECT_WITH_CHANGE_NUM_REGEX = Pattern.compile("^([^~]+)~(\\d+)$");
 
-  public static class Input {
-    public Set<String> changes;
-    @VisibleForTesting public boolean deleteMissing;
+  public record Input(Set<String> changes, boolean deleteMissing) {
+
+    public Input() {
+      this(Collections.emptySet(), false);
+    }
   }
 
   private final ChangeFinder changeFinder;
   private final ChangeData.Factory changeDataFactory;
+  private final Provider<InternalChangeQuery> queryProvider;
+  private final ChangeNotes.Factory notesFactory;
   private final ChangeIndexer indexer;
 
   @Inject
   IndexChanges(
-      ChangeFinder changeFinder, ChangeData.Factory changeDataFactory, ChangeIndexer indexer) {
+      ChangeFinder changeFinder,
+      ChangeData.Factory changeDataFactory,
+      Provider<InternalChangeQuery> queryProvider,
+      ChangeNotes.Factory notesFactory,
+      ChangeIndexer indexer) {
     this.changeFinder = changeFinder;
     this.changeDataFactory = changeDataFactory;
+    this.queryProvider = queryProvider;
+    this.notesFactory = notesFactory;
     this.indexer = indexer;
   }
 
   @Override
-  public Response<String> apply(ConfigResource resource, Input input) {
+  public Response<String> apply(ConfigResource resource, Input input) throws Exception {
     if (input == null || input.changes == null) {
       return Response.ok("Nothing to index");
     }
 
-    for (String id : input.changes) {
-      List<ChangeNotes> notes = changeFinder.find(id);
+    if (input.deleteMissing) {
+      List<ProjectWithChangeNumTuple> changeIds = new ArrayList<>();
+      for (String id : input.changes) {
+        changeIds.add(parseIntoProjectWithChangeNumTuple(id));
+      }
 
-      if (notes.isEmpty()) {
-        logger.atWarning().log("Change %s missing in NoteDb", id);
-        if (input.deleteMissing) {
-          int tilde = id.lastIndexOf('~');
-          String numericPart = tilde >= 0 ? id.substring(tilde + 1) : id;
-          Optional<Change.Id> changeId = Change.Id.tryParse(numericPart);
-          if (changeId.isPresent()) {
-            logger.atWarning().log("Deleting change %s from index", changeId.get());
-            indexer.delete(changeId.get());
+      for (ProjectWithChangeNumTuple changeInfo : changeIds) {
+        List<ChangeData> changes =
+            queryProvider.get().byProjectChangeNumber(changeInfo.project(), changeInfo.changeId());
+        Preconditions.checkState(
+            changes.size() <= 1,
+            "Ambiguous change ID %s in project %s",
+            changeInfo.changeId(),
+            changeInfo.project());
+
+        if (!changes.isEmpty()) {
+          try {
+            // Probe NoteDb: NoSuchChangeException means the change is in the index
+            // but absent from disk, so it should be deleted.
+            ChangeNotes unused = notesFactory.create(changeInfo.project(), changeInfo.changeId());
+          } catch (NoSuchChangeException e) {
+            logger.atWarning().log(
+                "Change %s~%s missing in NoteDb", changeInfo.project(), changeInfo.changeId());
+            ChangeData cd = changes.getFirst();
+            logger.atWarning().log(
+                "Deleting change %s~%s from index", cd.project(), cd.change().getChangeId());
+            indexer.delete(cd.project(), cd.virtualId());
+            continue;
           }
         }
-        continue;
-      }
 
-      for (ChangeNotes n : notes) {
-        indexer.index(changeDataFactory.create(n));
-        logger.atFine().log("Indexed change %s", id);
+        indexer.index(changeInfo.project, changeInfo.changeId);
+        logger.atFine().log("Indexed change %s:%s", changeInfo.project, changeInfo.changeId);
       }
+    } else {
+      input.changes.stream()
+          .flatMap(cid -> changeFinder.find(cid).stream())
+          .map(changeDataFactory::create)
+          .forEach(
+              cd -> {
+                indexer.index(cd);
+                logger.atFine().log("Indexed change %s:%s", cd.project(), cd.getId());
+              });
     }
 
     return Response.ok("Indexed changes " + input.changes);
   }
+
+  record ProjectWithChangeNumTuple(Project.NameKey project, Change.Id changeId) {}
+
+  ProjectWithChangeNumTuple parseIntoProjectWithChangeNumTuple(String id)
+      throws BadRequestException {
+    Matcher projectWithChangeNumMatcher = PROJECT_WITH_CHANGE_NUM_REGEX.matcher(id);
+    if (projectWithChangeNumMatcher.matches()) {
+      return new ProjectWithChangeNumTuple(
+          Project.nameKey(projectWithChangeNumMatcher.group(1)),
+          Change.id(Integer.parseInt(projectWithChangeNumMatcher.group(2))));
+    }
+    throw new BadRequestException("Change ID must be in project~changeNumber format: " + id);
+  }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/ListCaches.java b/java/com/google/gerrit/server/restapi/config/ListCaches.java
index 6dc17ce..6411615 100644
--- a/java/com/google/gerrit/server/restapi/config/ListCaches.java
+++ b/java/com/google/gerrit/server/restapi/config/ListCaches.java
@@ -35,6 +35,7 @@
 import com.google.inject.Inject;
 import java.util.Map;
 import java.util.TreeMap;
+import java.util.function.Predicate;
 import java.util.stream.Stream;
 import org.kohsuke.args4j.Option;
 
@@ -50,22 +51,34 @@
   @Option(name = "--format", usage = "output format")
   private OutputFormat format;
 
+  @Option(
+      name = "--include-diskstats",
+      usage = "if set, disk stat collection is included for persistent caches")
+  private boolean includeDiskStats;
+
   public ListCaches setFormat(OutputFormat format) {
     this.format = format;
     return this;
   }
 
+  public ListCaches setIncludeDiskStats(boolean includeDiskStats) {
+    this.includeDiskStats = includeDiskStats;
+    return this;
+  }
+
   @Inject
   public ListCaches(DynamicMap<Cache<?, ?>> cacheMap) {
     this.cacheMap = cacheMap;
   }
 
-  public Map<String, CacheInfo> getCacheInfos() {
+  public Map<String, CacheInfo> getCacheInfos(
+      Predicate<String> nameFilter, boolean includeDiskStats) {
     Map<String, CacheInfo> cacheInfos = new TreeMap<>();
     for (Extension<Cache<?, ?>> e : cacheMap) {
-      cacheInfos.put(
-          cacheNameOf(e.getPluginName(), e.getExportName()),
-          CacheInfoFactory.create(e.getProvider().get()));
+      String name = cacheNameOf(e.getPluginName(), e.getExportName());
+      if (nameFilter.test(name)) {
+        cacheInfos.put(name, CacheInfoFactory.create(e.getProvider().get(), includeDiskStats));
+      }
     }
     return cacheInfos;
   }
@@ -73,7 +86,7 @@
   @Override
   public Response<Object> apply(ConfigResource rsrc) {
     if (format == null) {
-      return Response.ok(getCacheInfos());
+      return Response.ok(getCacheInfos(name -> true, includeDiskStats));
     }
     Stream<String> cacheNames =
         Streams.stream(cacheMap)
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/java/com/google/gerrit/server/restapi/project/LabelDefinitionInputParser.java b/java/com/google/gerrit/server/restapi/project/LabelDefinitionInputParser.java
index 11d8b19..abbe50d 100644
--- a/java/com/google/gerrit/server/restapi/project/LabelDefinitionInputParser.java
+++ b/java/com/google/gerrit/server/restapi/project/LabelDefinitionInputParser.java
@@ -77,8 +77,8 @@
       if (newBranch.isEmpty()) {
         continue;
       }
-      if (!RefPattern.isRE(newBranch) && !newBranch.startsWith(RefNames.REFS)) {
-        newBranch = RefNames.REFS_HEADS + newBranch;
+      if (!RefPattern.isRE(newBranch)) {
+        newBranch = RefNames.fullName(newBranch);
       }
       try {
         RefPattern.validate(newBranch);
diff --git a/java/com/google/gerrit/server/restapi/project/ListDiffFiles.java b/java/com/google/gerrit/server/restapi/project/ListDiffFiles.java
index 7970d3a..0a2925a 100644
--- a/java/com/google/gerrit/server/restapi/project/ListDiffFiles.java
+++ b/java/com/google/gerrit/server/restapi/project/ListDiffFiles.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.restapi.project;
 
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.google.gerrit.entities.Patch;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.common.FileInfo;
@@ -59,11 +60,13 @@
   @Option(name = "--name-only", usage = "return only the list of files")
   private boolean nameOnly;
 
+  @CanIgnoreReturnValue
   public ListDiffFiles setBase(String baseSha) {
     this.baseSha = baseSha;
     return this;
   }
 
+  @CanIgnoreReturnValue
   public ListDiffFiles setNameOnly(boolean nameOnly) {
     this.nameOnly = nameOnly;
     return this;
@@ -112,7 +115,10 @@
       // Don't skip files due to rebase - this is a direct commit comparison, not patchset
       // comparison
       DiffOptions diffOptions =
-          DiffOptions.builder().skipFilesWithAllEditsDueToRebase(false).build();
+          DiffOptions.builder()
+              .skipFilesWithAllEditsDueToRebase(false)
+              .skipRebaseFiltering(true)
+              .build();
       Map<String, FileDiffOutput> fileDiffs =
           diffOperations.listModifiedFiles(project, baseCommitId, newCommitId, diffOptions);
 
diff --git a/java/com/google/gerrit/server/restapi/project/PostLabelsReview.java b/java/com/google/gerrit/server/restapi/project/PostLabelsReview.java
index 4e9d432..177f26d 100644
--- a/java/com/google/gerrit/server/restapi/project/PostLabelsReview.java
+++ b/java/com/google/gerrit/server/restapi/project/PostLabelsReview.java
@@ -27,9 +27,9 @@
 import com.google.gerrit.server.restapi.project.RepoMetaDataUpdater.ConfigChangeCreator;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.Map;
-import javax.inject.Singleton;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/restapi/project/PostSubmitRequirements.java b/java/com/google/gerrit/server/restapi/project/PostSubmitRequirements.java
index 71080a5..2c7bfce 100644
--- a/java/com/google/gerrit/server/restapi/project/PostSubmitRequirements.java
+++ b/java/com/google/gerrit/server/restapi/project/PostSubmitRequirements.java
@@ -23,9 +23,9 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.project.ProjectConfig;
 import com.google.gerrit.server.project.SubmitRequirementResource;
+import com.google.inject.Inject;
 import com.google.inject.Provider;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import com.google.inject.Singleton;
 
 @Singleton
 public class PostSubmitRequirements
diff --git a/java/com/google/gerrit/server/restapi/project/PostSubmitRequirementsReview.java b/java/com/google/gerrit/server/restapi/project/PostSubmitRequirementsReview.java
index f0a371a..c9485fc 100644
--- a/java/com/google/gerrit/server/restapi/project/PostSubmitRequirementsReview.java
+++ b/java/com/google/gerrit/server/restapi/project/PostSubmitRequirementsReview.java
@@ -25,8 +25,8 @@
 import com.google.gerrit.server.restapi.project.RepoMetaDataUpdater.ConfigChangeCreator;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
-import javax.inject.Singleton;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/restapi/project/PutConfigReview.java b/java/com/google/gerrit/server/restapi/project/PutConfigReview.java
index 7e6cc19..eda92bb 100644
--- a/java/com/google/gerrit/server/restapi/project/PutConfigReview.java
+++ b/java/com/google/gerrit/server/restapi/project/PutConfigReview.java
@@ -23,9 +23,9 @@
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.restapi.project.RepoMetaDataUpdater.ConfigChangeCreator;
 import com.google.gerrit.server.update.UpdateException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
-import javax.inject.Inject;
-import javax.inject.Singleton;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/restapi/project/RepoMetaDataUpdater.java b/java/com/google/gerrit/server/restapi/project/RepoMetaDataUpdater.java
index 62c88276..d386f85 100644
--- a/java/com/google/gerrit/server/restapi/project/RepoMetaDataUpdater.java
+++ b/java/com/google/gerrit/server/restapi/project/RepoMetaDataUpdater.java
@@ -49,10 +49,10 @@
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
 import java.io.IOException;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.inject.Singleton;
 import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.ObjectId;
diff --git a/java/com/google/gerrit/server/rules/prolog/PrologSubmitRuleUtilImpl.java b/java/com/google/gerrit/server/rules/prolog/PrologSubmitRuleUtilImpl.java
index 6be71f8..4cc30ec 100644
--- a/java/com/google/gerrit/server/rules/prolog/PrologSubmitRuleUtilImpl.java
+++ b/java/com/google/gerrit/server/rules/prolog/PrologSubmitRuleUtilImpl.java
@@ -20,8 +20,8 @@
 import com.google.gerrit.entities.SubmitTypeRecord;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.PrologSubmitRuleUtil;
+import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import javax.inject.Inject;
 
 /** Implementation of {@link PrologSubmitRuleUtil}. */
 @Singleton
diff --git a/java/com/google/gerrit/server/update/SuperprojectUpdateSubmissionListener.java b/java/com/google/gerrit/server/update/SuperprojectUpdateSubmissionListener.java
index 813bee9..fad29a7 100644
--- a/java/com/google/gerrit/server/update/SuperprojectUpdateSubmissionListener.java
+++ b/java/com/google/gerrit/server/update/SuperprojectUpdateSubmissionListener.java
@@ -21,12 +21,12 @@
 import com.google.gerrit.server.submit.MergeOpRepoManager;
 import com.google.gerrit.server.submit.SubmoduleOp;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import com.google.inject.Provides;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
-import javax.inject.Inject;
 import org.eclipse.jgit.transport.ReceiveCommand;
 
 /** Update superprojects after submission is done */
diff --git a/java/com/google/gerrit/sshd/commands/ShowCaches.java b/java/com/google/gerrit/sshd/commands/ShowCaches.java
index 7a958b7..bf7c154 100644
--- a/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -45,8 +45,13 @@
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 import org.apache.sshd.common.io.IoAcceptor;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.mina.MinaSession;
@@ -79,6 +84,17 @@
   @Option(name = "--show-threads", usage = "show detailed thread counts")
   private boolean showThreads;
 
+  @Option(
+      name = "--include-diskstats",
+      usage = "include disk stat collection for persistent caches")
+  private boolean includeDiskStats;
+
+  @Option(
+      name = "--cache",
+      usage = "show the named cache; may be supplied more than once",
+      metaVar = "NAME")
+  private List<String> caches = new ArrayList<>();
+
   @Inject private SshDaemon daemon;
   @Inject private ListCaches listCaches;
   @Inject private GetSummary getSummary;
@@ -153,14 +169,21 @@
   }
 
   private Collection<CacheInfo> getCaches() {
-    @SuppressWarnings("unchecked")
-    Map<String, CacheInfo> caches =
-        (Map<String, CacheInfo>) listCaches.apply(new ConfigResource()).value();
-    for (Map.Entry<String, CacheInfo> entry : caches.entrySet()) {
+    Map<String, CacheInfo> selected;
+    if (caches.isEmpty()) {
+      selected = listCaches.getCacheInfos(name -> true, includeDiskStats);
+    } else {
+      Set<String> filter =
+          caches.stream().map(n -> n.toLowerCase(Locale.US)).collect(Collectors.toSet());
+      selected =
+          listCaches.getCacheInfos(
+              n -> filter.contains(n.toLowerCase(Locale.US)), includeDiskStats);
+    }
+    for (Map.Entry<String, CacheInfo> entry : selected.entrySet()) {
       CacheInfo cache = entry.getValue();
       cache.name = entry.getKey();
     }
-    return caches.values();
+    return selected.values();
   }
 
   private void memSummary(MemSummaryInfo memSummary) {
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index 745f89a..c37dce9 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -190,6 +190,11 @@
 
   @Override
   protected void configure() {
+    configure(true);
+  }
+
+  protected void configure(boolean bindGerritApi) {
+
     // Do NOT bind @RemotePeer, as it is bound in a child injector of
     // ChangeMergeQueue (bound via GerritGlobalModule below), so there cannot be
     // a binding in the parent injector. If you need @RemotePeer, you must bind
@@ -226,7 +231,11 @@
 
     AuthConfig authConfig = cfgInjector.getInstance(AuthConfig.class);
     install(new AuthModule(authConfig));
-    install(new GerritApiModule());
+
+    if (bindGerritApi) {
+      install(new GerritApiModule());
+    }
+
     install(new ProjectQueryBuilderModule());
     install(new DefaultRefLogIdentityProvider.Module());
     factory(PluginUser.Factory.class);
diff --git a/java/com/google/gerrit/util/cli/BUILD b/java/com/google/gerrit/util/cli/BUILD
index f62aea90..70954a9 100644
--- a/java/com/google/gerrit/util/cli/BUILD
+++ b/java/com/google/gerrit/util/cli/BUILD
@@ -1,4 +1,4 @@
-load("@rules_java//java:defs.bzl", "java_library")
+load("@rules_java//java:defs.bzl", "java_binary", "java_library")
 
 java_library(
     name = "cli",
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AbstractAccountIT.java
similarity index 80%
rename from javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
rename to javatests/com/google/gerrit/acceptance/api/accounts/AbstractAccountIT.java
index 67f33e8..e15741e 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AbstractAccountIT.java
@@ -19,7 +19,6 @@
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.gerrit.acceptance.GitUtil.deleteRef;
 import static com.google.gerrit.acceptance.GitUtil.fetch;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
@@ -32,8 +31,6 @@
 import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithExpiration;
 import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithSecondUserId;
 import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithoutExpiration;
-import static com.google.gerrit.server.account.AccountProperties.ACCOUNT;
-import static com.google.gerrit.server.account.AccountProperties.ACCOUNT_CONFIG;
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPGKEY;
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
 import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
@@ -41,13 +38,8 @@
 import static com.google.gerrit.server.project.ProjectCache.illegalState;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
-import static com.google.gerrit.truth.ConfigSubject.assertThat;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toSet;
-import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
@@ -112,13 +104,12 @@
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInput;
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInput.CheckAccountsInput;
-import com.google.gerrit.extensions.client.ProjectWatchInfo;
+import com.google.gerrit.extensions.client.ListAccountsOption;
 import com.google.gerrit.extensions.common.AccountDetailInfo;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.AccountStateInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.common.CommentInfo;
-import com.google.gerrit.extensions.common.EmailInfo;
 import com.google.gerrit.extensions.common.GpgKeyInfo;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.common.MetadataInfo;
@@ -138,12 +129,10 @@
 import com.google.gerrit.httpd.CacheBasedWebSession;
 import com.google.gerrit.server.ExceptionHook;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.Sequences;
 import com.google.gerrit.server.ServerInitiated;
 import com.google.gerrit.server.account.AccountControl;
 import com.google.gerrit.server.account.AccountLimits;
-import com.google.gerrit.server.account.AccountProperties;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.account.AccountStateProvider;
 import com.google.gerrit.server.account.AccountsUpdate;
@@ -155,17 +144,11 @@
 import com.google.gerrit.server.account.externalids.ExternalIdFactory;
 import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
 import com.google.gerrit.server.account.externalids.ExternalIds;
-import com.google.gerrit.server.account.externalids.storage.notedb.ExternalIdFactoryNoteDbImpl;
-import com.google.gerrit.server.account.externalids.storage.notedb.ExternalIdNotes;
-import com.google.gerrit.server.account.externalids.storage.notedb.ExternalIdsNoteDbImpl;
-import com.google.gerrit.server.account.storage.notedb.AccountsUpdateNoteDbImpl;
 import com.google.gerrit.server.change.AccountPatchReviewStore;
 import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.group.testing.TestGroupBackend;
 import com.google.gerrit.server.index.account.AccountIndexer;
-import com.google.gerrit.server.index.account.StalenessChecker;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.project.RefPattern;
@@ -173,7 +156,6 @@
 import com.google.gerrit.server.restapi.account.GetCapabilities;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryListener;
-import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gerrit.server.validators.AccountActivationValidationListener;
 import com.google.gerrit.server.validators.ValidationException;
 import com.google.gerrit.testing.ConfigSuite;
@@ -198,11 +180,9 @@
 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;
-import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.BasicCookieStore;
 import org.apache.http.impl.client.CloseableHttpClient;
@@ -215,26 +195,16 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.CommitBuilder;
 import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.transport.PushCertificateIdent;
-import org.eclipse.jgit.transport.PushResult;
-import org.eclipse.jgit.transport.RemoteRefUpdate;
-import org.eclipse.jgit.treewalk.TreeWalk;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-public class AccountIT extends AbstractDaemonTest {
+public abstract class AbstractAccountIT extends AbstractDaemonTest {
   @ConfigSuite.Default
   public static Config enableSignedPushConfig() {
     Config cfg = new Config();
@@ -254,23 +224,20 @@
 
   @Inject protected GroupOperations groupOperations;
 
-  @Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
+  @Inject @ServerInitiated protected Provider<AccountsUpdate> accountsUpdateProvider;
   @Inject private AccountIndexer accountIndexer;
-  @Inject private ExternalIdNotes.Factory extIdNotesFactory;
-  @Inject private ExternalIdsNoteDbImpl externalIdsNoteDbImpl;
-  @Inject private GitReferenceUpdated gitReferenceUpdated;
-  @Inject private Provider<InternalAccountQuery> accountQueryProvider;
-  @Inject private Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory;
+  @Inject protected ExternalIds externalIds;
+  @Inject protected Provider<InternalAccountQuery> accountQueryProvider;
+  @Inject protected Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory;
   @Inject private Provider<PublicKeyStore> publicKeyStoreProvider;
   @Inject private RetryHelper.Metrics retryMetrics;
-  @Inject private Sequences seq;
-  @Inject private StalenessChecker stalenessChecker;
+  @Inject protected Sequences seq;
   @Inject private VersionedAuthorizedKeys.Accessor authorizedKeys;
   @Inject private PluginSetContext<ExceptionHook> exceptionHooks;
   @Inject private PluginSetContext<RetryListener> retryListeners;
-  @Inject private ExternalIdKeyFactory externalIdKeyFactory;
-  @Inject private ExternalIdFactoryNoteDbImpl externalIdFactoryNoteDbImpl;
-  @Inject private AuthConfig authConfig;
+  @Inject protected ExternalIdKeyFactory externalIdKeyFactory;
+  @Inject protected ExternalIdFactory externalIdFactory;
+  @Inject protected AuthConfig authConfig;
   @Inject private AccountControl.Factory accountControlFactory;
   @Inject private AccountOperations accountOperations;
   @Inject private AccountLimits.Factory limitsFactory;
@@ -341,18 +308,6 @@
   }
 
   @Test
-  public void createByAccountCreator() throws Exception {
-    RefUpdateCounter refUpdateCounter = createRefUpdateCounter();
-    try (Registration registration = extensionRegistry.newRegistration().add(refUpdateCounter)) {
-      Account.Id accountId = createByAccountCreator(1);
-      refUpdateCounter.assertRefUpdateFor(
-          RefUpdateCounter.projectRef(allUsers, RefNames.refsUsers(accountId)),
-          RefUpdateCounter.projectRef(allUsers, RefNames.REFS_EXTERNAL_IDS),
-          RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequence.NAME_ACCOUNTS));
-    }
-  }
-
-  @Test
   public void createWithInvalidEmailAddress() throws Exception {
     AccountInput input = new AccountInput();
     input.username = name("test");
@@ -372,37 +327,6 @@
     return new AccountIndexedCounter();
   }
 
-  @UsedAt(UsedAt.Project.GOOGLE)
-  protected Account.Id createByAccountCreator(int expectedAccountReindexCalls) throws Exception {
-    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
-    try (Registration registration =
-        extensionRegistry.newRegistration().add(accountIndexedCounter)) {
-      String name = "foo";
-      TestAccount foo = accountCreator.create(name);
-      AccountInfo info = gApi.accounts().id(foo.id().get()).get();
-      if (server.isUsernameSupported()) {
-        assertThat(info.username).isEqualTo(name);
-      } else {
-        assertThat(info.email).isEqualTo(foo.email());
-      }
-      assertThat(info.name).isEqualTo(name);
-      accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
-      assertUserBranch(foo.id(), name, null);
-      return foo.id();
-    }
-  }
-
-  @Test
-  public void createAnonymousCowardByAccountCreator() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
-    try (Registration registration =
-        extensionRegistry.newRegistration().add(accountIndexedCounter)) {
-      TestAccount anonymousCoward = accountCreator.create();
-      accountIndexedCounter.assertReindexOf(anonymousCoward);
-      assertUserBranchWithoutAccountConfig(anonymousCoward.id());
-    }
-  }
-
   @Test
   public void create() throws Exception {
     AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
@@ -453,56 +377,6 @@
   }
 
   @Test
-  public void commitMessageOnAccountUpdates() throws Exception {
-    AccountsUpdate au = accountsUpdateProvider.get();
-    Account.Id accountId = Account.id(seq.nextAccountId());
-    au.insert("Create Test Account", accountId, u -> {});
-    assertLastCommitMessageOfUserBranch(accountId, "Create Test Account");
-
-    au.update("Set Status", accountId, u -> u.setStatus("Foo"));
-    assertLastCommitMessageOfUserBranch(accountId, "Set Status");
-  }
-
-  private void assertLastCommitMessageOfUserBranch(Account.Id accountId, String expectedMessage)
-      throws Exception {
-    try (Repository repo = repoManager.openRepository(allUsers);
-        RevWalk rw = new RevWalk(repo)) {
-      Ref exactRef = repo.exactRef(RefNames.refsUsers(accountId));
-      assertThat(rw.parseCommit(exactRef.getObjectId()).getShortMessage())
-          .isEqualTo(expectedMessage);
-    }
-  }
-
-  @Test
-  @UseClockStep
-  public void createAtomically() throws Exception {
-    Account.Id accountId = Account.id(seq.nextAccountId());
-    String fullName = "Foo";
-    ExternalId extId = getExternalIdFactory().createEmail(accountId, "foo@example.com");
-    AccountState accountState =
-        accountsUpdateProvider
-            .get()
-            .insert(
-                "Create Account Atomically",
-                accountId,
-                u -> u.setFullName(fullName).addExternalId(extId));
-    assertThat(accountState.account().fullName()).isEqualTo(fullName);
-
-    AccountInfo info = gApi.accounts().id(accountId.get()).get();
-    assertThat(info.name).isEqualTo(fullName);
-
-    List<EmailInfo> emails = gApi.accounts().id(accountId.get()).getEmails();
-    assertThat(emails.stream().map(e -> e.email).collect(toSet())).containsExactly(extId.email());
-
-    RevCommit commitUserBranch =
-        projectOperations.project(allUsers).getHead(RefNames.refsUsers(accountId));
-    RevCommit commitRefsMetaExternalIds =
-        projectOperations.project(allUsers).getHead(RefNames.REFS_EXTERNAL_IDS);
-    assertThat(commitUserBranch.getCommitTime())
-        .isEqualTo(commitRefsMetaExternalIds.getCommitTime());
-  }
-
-  @Test
   public void updateNonExistingAccount() throws Exception {
     Account.Id nonExistingAccountId = Account.id(999999);
     AtomicBoolean consumerCalled = new AtomicBoolean();
@@ -516,59 +390,6 @@
   }
 
   @Test
-  public void updateAccountWithoutAccountConfigNoteDb() throws Exception {
-    TestAccount anonymousCoward = accountCreator.create();
-    assertUserBranchWithoutAccountConfig(anonymousCoward.id());
-
-    String status = "OOO";
-    Optional<AccountState> accountState =
-        accountsUpdateProvider
-            .get()
-            .update("Set status", anonymousCoward.id(), u -> u.setStatus(status));
-    assertThat(accountState).isPresent();
-    Account account = accountState.get().account();
-    assertThat(account.fullName()).isNull();
-    assertThat(account.status()).isEqualTo(status);
-    assertUserBranch(anonymousCoward.id(), null, status);
-  }
-
-  private void assertUserBranchWithoutAccountConfig(Account.Id accountId) throws Exception {
-    assertUserBranch(accountId, null, null);
-  }
-
-  private void assertUserBranch(
-      Account.Id accountId, @Nullable String name, @Nullable String status) throws Exception {
-    try (Repository repo = repoManager.openRepository(allUsers);
-        RevWalk rw = new RevWalk(repo);
-        ObjectReader or = repo.newObjectReader()) {
-      Ref ref = repo.exactRef(RefNames.refsUsers(accountId));
-      assertThat(ref).isNotNull();
-      RevCommit c = rw.parseCommit(ref.getObjectId());
-      long timestampDiffMs =
-          Math.abs(c.getCommitTime() * 1000L - getAccount(accountId).registeredOn().toEpochMilli());
-      assertThat(timestampDiffMs).isAtMost(SECONDS.toMillis(1));
-
-      // Check the 'account.config' file.
-      try (TreeWalk tw = TreeWalk.forPath(or, ACCOUNT_CONFIG, c.getTree())) {
-        if (name != null || status != null) {
-          assertThat(tw).isNotNull();
-          Config cfg = new Config();
-          cfg.fromText(new String(or.open(tw.getObjectId(0), OBJ_BLOB).getBytes(), UTF_8));
-          assertThat(cfg)
-              .stringValue(ACCOUNT, null, AccountProperties.KEY_FULL_NAME)
-              .isEqualTo(name);
-          assertThat(cfg)
-              .stringValue(ACCOUNT, null, AccountProperties.KEY_STATUS)
-              .isEqualTo(status);
-        } else {
-          // No account properties were set, hence an 'account.config' file was not created.
-          assertThat(tw).isNull();
-        }
-      }
-    }
-  }
-
-  @Test
   public void randomNIds() throws Exception {
     accountOperations.newAccount().create();
     accountOperations.newAccount().create();
@@ -1165,7 +986,7 @@
     assertThat(
             gApi.accounts().id(foo.id().get()).getEmails().stream()
                 .map(e -> e.email)
-                .collect(toSet()))
+                .collect(toImmutableSet()))
         .containsExactly(email, secondaryEmail);
   }
 
@@ -1187,7 +1008,7 @@
     assertThat(
             gApi.accounts().id(foo.id().get()).getEmails().stream()
                 .map(e -> e.email)
-                .collect(toSet()))
+                .collect(toImmutableSet()))
         .containsExactly(email, secondaryEmail);
   }
 
@@ -1433,7 +1254,7 @@
       assertThat(
               gApi.accounts().self().getExternalIds().stream()
                   .map(e -> e.identity)
-                  .collect(toSet()))
+                  .collect(toImmutableSet()))
           .containsAtLeast(extId1, extId2);
 
       requestScopeOperations.resetCurrentApiUser();
@@ -1447,7 +1268,7 @@
       assertThat(
               gApi.accounts().self().getExternalIds().stream()
                   .map(e -> e.identity)
-                  .collect(toSet()))
+                  .collect(toImmutableSet()))
           .containsNoneOf(extId1, extId2);
     }
   }
@@ -1486,7 +1307,9 @@
     requestScopeOperations.resetCurrentApiUser();
     assertThat(getEmails()).contains(ldapEmail);
     assertThat(
-            gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
+            gApi.accounts().self().getExternalIds().stream()
+                .map(e -> e.identity)
+                .collect(toImmutableSet()))
         .contains(ldapExternalId);
   }
 
@@ -1516,7 +1339,9 @@
                                 admin.id(),
                                 ldapEmail)));
     assertThat(
-            gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
+            gApi.accounts().self().getExternalIds().stream()
+                .map(e -> e.identity)
+                .collect(toImmutableSet()))
         .containsAtLeast(ldapExternalId, nonLdapExternalId);
 
     requestScopeOperations.resetCurrentApiUser();
@@ -1527,7 +1352,9 @@
     requestScopeOperations.resetCurrentApiUser();
     assertThat(getExtIdsEmail()).doesNotContain(nonLdapEMail);
     assertThat(
-            gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
+            gApi.accounts().self().getExternalIds().stream()
+                .map(e -> e.identity)
+                .collect(toImmutableSet()))
         .contains(ldapExternalId);
   }
 
@@ -1745,98 +1572,20 @@
   public void refsUsersSelfIsAdvertised() throws Exception {
     TestRepository<?> testRepository = cloneProject(allUsers, user);
     try (Git git = testRepository.git()) {
-      List<String> advertisedRefs =
-          git.lsRemote().call().stream().map(Ref::getName).collect(toList());
+      ImmutableList<String> advertisedRefs =
+          git.lsRemote().call().stream().map(Ref::getName).collect(toImmutableList());
       assertThat(advertisedRefs).contains(RefNames.REFS_USERS_SELF);
     }
   }
 
   @Test
-  public void createDefaultUserBranch() throws Exception {
-    try (Repository repo = repoManager.openRepository(allUsers)) {
-      assertThat(repo.exactRef(RefNames.REFS_USERS_DEFAULT)).isNull();
-    }
-
-    projectOperations
-        .project(allUsers)
-        .forUpdate()
-        .add(allow(Permission.CREATE).ref(RefNames.REFS_USERS_DEFAULT).group(adminGroupUuid()))
-        .add(allow(Permission.PUSH).ref(RefNames.REFS_USERS_DEFAULT).group(adminGroupUuid()))
-        .update();
-
-    TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    pushFactory
-        .create(admin.newIdent(), allUsersRepo)
-        .to(RefNames.REFS_USERS_DEFAULT)
-        .assertOkStatus();
-
-    try (Repository repo = repoManager.openRepository(allUsers)) {
-      assertThat(repo.exactRef(RefNames.REFS_USERS_DEFAULT)).isNotNull();
-    }
-  }
-
-  @Test
-  public void cannotDeleteUserBranch() throws Exception {
-    projectOperations
-        .project(allUsers)
-        .forUpdate()
-        .add(
-            allow(Permission.DELETE)
-                .ref(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}")
-                .group(REGISTERED_USERS)
-                .force(true))
-        .update();
-
-    TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    String userRef = RefNames.refsUsers(admin.id());
-    PushResult r = deleteRef(allUsersRepo, userRef);
-    RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
-    assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
-    assertThat(refUpdate.getMessage()).contains("Not allowed to delete user branch.");
-
-    try (Repository repo = repoManager.openRepository(allUsers)) {
-      assertThat(repo.exactRef(userRef)).isNotNull();
-    }
-  }
-
-  @Test
-  public void deleteUserBranchWithAccessDatabaseCapability() throws Exception {
-    projectOperations
-        .allProjectsForUpdate()
-        .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
-        .update();
-    projectOperations
-        .project(allUsers)
-        .forUpdate()
-        .add(
-            allow(Permission.DELETE)
-                .ref(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}")
-                .group(REGISTERED_USERS)
-                .force(true))
-        .update();
-
-    TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    String userRef = RefNames.refsUsers(admin.id());
-    PushResult r = deleteRef(allUsersRepo, userRef);
-    RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
-    assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.OK);
-
-    try (Repository repo = repoManager.openRepository(allUsers)) {
-      assertThat(repo.exactRef(userRef)).isNull();
-    }
-
-    assertThat(accountCache.get(admin.id())).isEmpty();
-    assertThat(accountQueryProvider.get().byDefault(admin.id().toString(), true)).isEmpty();
-  }
-
-  @Test
   public void addGpgKey() throws Exception {
     TestKey key = validKeyWithoutExpiration();
     String id = key.getKeyIdString();
     addExternalIdEmail(admin, "test1@example.com");
 
     sender.clear();
-    assertKeyMapContains(key, addGpgKey(key.getPublicKeyArmored()));
+    assertKeyMapContains(key, addGpgKeyForSelf(key.getPublicKeyArmored()));
     assertKeys(key);
     assertThat(sender.getMessages()).hasSize(1);
     assertThat(sender.getMessages().get(0).body()).contains("new GPG keys have been added");
@@ -1855,7 +1604,9 @@
 
     sender.clear();
     requestScopeOperations.setApiUser(admin.id());
-    assertThrows(ResourceNotFoundException.class, () -> addGpgKey(user, key.getPublicKeyArmored()));
+    assertThrows(
+        ResourceNotFoundException.class,
+        () -> addGpgKeyForAccount(user, key.getPublicKeyArmored()));
   }
 
   @Test
@@ -1866,7 +1617,7 @@
     PGPPublicKey pk = key.getPublicKey();
 
     sender.clear();
-    GpgKeyInfo info = addGpgKey(armor(pk)).get(id);
+    GpgKeyInfo info = addGpgKeyForSelf(armor(pk)).get(id);
     assertThat(info.userIds).hasSize(2);
     assertIteratorSize(2, getOnlyKeyFromStore(key).getUserIDs());
     assertThat(sender.getMessages()).hasSize(1);
@@ -1899,12 +1650,13 @@
       accountIndexedCounter.assertReindexOf(user);
 
       TestKey key = validKeyWithSecondUserId();
-      addGpgKey(key.getPublicKeyArmored());
+      addGpgKeyForSelf(key.getPublicKeyArmored());
       requestScopeOperations.setApiUser(user.id());
 
       ResourceConflictException thrown =
           assertThrows(
-              ResourceConflictException.class, () -> addGpgKey(user, key.getPublicKeyArmored()));
+              ResourceConflictException.class,
+              () -> addGpgKeyForAccount(user, key.getPublicKeyArmored()));
       assertThat(thrown)
           .hasMessageThat()
           .contains("GPG key already associated with another account");
@@ -1938,7 +1690,7 @@
       TestKey key = validKeyWithoutExpiration();
       String id = key.getKeyIdString();
       addExternalIdEmail(admin, "test1@example.com");
-      addGpgKey(key.getPublicKeyArmored());
+      addGpgKeyForSelf(key.getPublicKeyArmored());
       assertKeys(key);
       accountIndexedCounter.clear();
 
@@ -2010,7 +1762,8 @@
   @Test
   public void addMalformedGpgKey() throws Exception {
     String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\ntest\n-----END PGP PUBLIC KEY BLOCK-----";
-    BadRequestException unused = assertThrows(BadRequestException.class, () -> addGpgKey(key));
+    BadRequestException unused =
+        assertThrows(BadRequestException.class, () -> addGpgKeyForSelf(key));
   }
 
   @Test
@@ -2227,33 +1980,6 @@
     assertThat(accountQueryProvider.get().byDefault(name, true)).hasSize(2);
   }
 
-  @Test
-  public void checkMetaIdAndUniqueTag() throws Exception {
-    // In open-source Gerrit, the uniqueTag and metaId are always the same. Check them together
-    // in this test.
-    // metaId and uniqueTag are set when account is loaded
-    assertThat(accounts.get(admin.id()).get().account().metaId()).isEqualTo(getMetaId(admin.id()));
-    assertThat(accounts.get(admin.id()).get().account().uniqueTag())
-        .isEqualTo(getMetaId(admin.id()));
-
-    // metaId and uniqueTag are set when account is created
-    AccountsUpdate au = accountsUpdateProvider.get();
-    Account.Id accountId = Account.id(seq.nextAccountId());
-    AccountState accountState = au.insert("Create Test Account", accountId, u -> {});
-    assertThat(accountState.account().metaId()).isEqualTo(getMetaId(accountId));
-    assertThat(accountState.account().uniqueTag()).isEqualTo(getMetaId(accountId));
-
-    // metaId and uniqueTag are set when account is updated
-    Optional<AccountState> updatedAccountState =
-        au.update("Set Full Name", accountId, u -> u.setFullName("foo"));
-    assertThat(updatedAccountState).isPresent();
-    Account updatedAccount = updatedAccountState.get().account();
-    assertThat(accountState.account().metaId()).isNotEqualTo(updatedAccount.metaId());
-    assertThat(accountState.account().uniqueTag()).isNotEqualTo(updatedAccount.uniqueTag());
-    assertThat(updatedAccount.metaId()).isEqualTo(getMetaId(accountId));
-    assertThat(updatedAccount.uniqueTag()).isEqualTo(getMetaId(accountId));
-  }
-
   private EmailInput newEmailInput(String email, boolean noConfirmation) {
     EmailInput input = new EmailInput();
     input.email = email;
@@ -2265,16 +1991,6 @@
     return newEmailInput(email, true);
   }
 
-  @Nullable
-  private String getMetaId(Account.Id accountId) throws IOException {
-    try (Repository repo = repoManager.openRepository(allUsers);
-        RevWalk rw = new RevWalk(repo);
-        ObjectReader or = repo.newObjectReader()) {
-      Ref ref = repo.exactRef(RefNames.refsUsers(accountId));
-      return ref != null ? ref.getObjectId().name() : null;
-    }
-  }
-
   @Test
   public void allGroupsForAnAdminAccountCanBeRetrieved() throws Exception {
     List<GroupInfo> groups = gApi.accounts().id(admin.id().get()).getGroups();
@@ -2480,12 +2196,12 @@
             "Set Status",
             admin.id(),
             (a, u) -> {
-              if ("A-1".equals(a.account().status())) {
+              if (Objects.equals(a.account().status(), "A-1")) {
                 bgIndicatorA1ToB1.set(true);
                 u.setStatus("B-1");
               }
 
-              if ("A-2".equals(a.account().status())) {
+              if (Objects.equals(a.account().status(), "A-2")) {
                 bgIndicatorA2ToB2.set(true);
                 u.setStatus("B-2");
               }
@@ -2540,7 +2256,7 @@
     assertThat(
             gApi.accounts().id(accountId.get()).getExternalIds().stream()
                 .map(i -> i.identity)
-                .collect(toSet()))
+                .collect(toImmutableSet()))
         .containsExactly(extIdA1.key().get());
 
     ExternalId extIdB1 = getExternalIdFactory().create("foo", "B-1", accountId);
@@ -2572,99 +2288,11 @@
     assertThat(
             gApi.accounts().id(accountId.get()).getExternalIds().stream()
                 .map(i -> i.identity)
-                .collect(toSet()))
+                .collect(toImmutableSet()))
         .containsExactly(extIdB2.key().get());
   }
 
   @Test
-  public void stalenessChecker() throws Exception {
-    // Newly created account is not stale.
-    AccountInfo accountInfo = gApi.accounts().create(name("foo")).get();
-    Account.Id accountId = Account.id(accountInfo._accountId);
-    assertThat(stalenessChecker.check(accountId).isStale()).isFalse();
-
-    // Manually updating the user ref makes the index document stale.
-    String userRef = RefNames.refsUsers(accountId);
-    testRefAction(
-        () -> {
-          try (Repository repo = repoManager.openRepository(allUsers);
-              ObjectInserter oi = repo.newObjectInserter();
-              RevWalk rw = new RevWalk(repo)) {
-            RevCommit commit = rw.parseCommit(repo.exactRef(userRef).getObjectId());
-
-            PersonIdent ident = new PersonIdent(serverIdent.get(), TimeUtil.now());
-            CommitBuilder cb = new CommitBuilder();
-            cb.setTreeId(commit.getTree());
-            cb.setCommitter(ident);
-            cb.setAuthor(ident);
-            cb.setMessage(commit.getFullMessage());
-            ObjectId emptyCommit = oi.insert(cb);
-            oi.flush();
-
-            RefUpdate updateRef = repo.updateRef(userRef);
-            updateRef.setExpectedOldObjectId(commit.toObjectId());
-            updateRef.setNewObjectId(emptyCommit);
-            assertThat(updateRef.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
-          }
-        });
-    assertStaleAccountAndReindex(accountId);
-
-    // Manually inserting/updating/deleting an external ID of the user makes the index document
-    // stale.
-    try (Repository repo = repoManager.openRepository(allUsers)) {
-      testRefAction(
-          () -> {
-            ExternalIdNotes extIdNotes = getExternalIdNotes(repo);
-
-            ExternalId.Key key = externalIdKeyFactory.create("foo", "foo");
-            extIdNotes.insert(getExternalIdFactory().create(key, accountId));
-            try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
-              extIdNotes.commit(update);
-            }
-            assertStaleAccountAndReindex(accountId);
-
-            extIdNotes = getExternalIdNotes(repo);
-            extIdNotes.upsert(
-                getExternalIdFactory().createWithEmail(key, accountId, "foo@example.com"));
-            try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
-              extIdNotes.commit(update);
-            }
-            assertStaleAccountAndReindex(accountId);
-
-            extIdNotes = getExternalIdNotes(repo);
-            extIdNotes.delete(accountId, key);
-            try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
-              extIdNotes.commit(update);
-            }
-          });
-      assertStaleAccountAndReindex(accountId);
-    }
-
-    // Manually delete account
-    testRefAction(
-        () -> {
-          try (Repository repo = repoManager.openRepository(allUsers);
-              RevWalk rw = new RevWalk(repo)) {
-            RevCommit commit = rw.parseCommit(repo.exactRef(userRef).getObjectId());
-            RefUpdate updateRef = repo.updateRef(userRef);
-            updateRef.setExpectedOldObjectId(commit.toObjectId());
-            updateRef.setNewObjectId(ObjectId.zeroId());
-            updateRef.setForceUpdate(true);
-            assertThat(updateRef.delete()).isEqualTo(RefUpdate.Result.FORCED);
-          }
-        });
-    assertStaleAccountAndReindex(accountId);
-  }
-
-  private void assertStaleAccountAndReindex(Account.Id accountId) throws IOException {
-    assertThat(stalenessChecker.check(accountId).isStale()).isTrue();
-
-    // Reindex fixes staleness
-    accountIndexer.index(accountId);
-    assertThat(stalenessChecker.check(accountId).isStale()).isFalse();
-  }
-
-  @Test
   @UseClockStep
   public void deleteAllDraftComments() throws Exception {
     try {
@@ -2912,55 +2540,6 @@
   }
 
   @Test
-  public void externalIdBatchUpdates() throws Exception {
-    String extId1String = "foo:bar";
-    String extId2String = "foo:baz";
-    ExternalId extId1 =
-        getExternalIdFactory()
-            .createWithEmail(externalIdKeyFactory.parse(extId1String), admin.id(), "1@foo.com");
-    ExternalId extId2 =
-        getExternalIdFactory()
-            .createWithEmail(externalIdKeyFactory.parse(extId2String), user.id(), "2@foo.com");
-
-    int initialCommits = countExternalIdsCommits();
-    AccountsUpdate.UpdateArguments ua1 =
-        new AccountsUpdate.UpdateArguments(
-            "Add External ID", admin.id(), u -> u.addExternalId(extId1));
-    AccountsUpdate.UpdateArguments ua2 =
-        new AccountsUpdate.UpdateArguments(
-            "Add External ID", user.id(), u -> u.addExternalId(extId2));
-    ImmutableList<Optional<AccountState>> accountStates =
-        accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
-    assertThat(accountStates).hasSize(2);
-    assertThat(accountStates.get(0).get().externalIds()).contains(extId1);
-    assertThat(accountStates.get(1).get().externalIds()).contains(extId2);
-    assertThat(
-            gApi.accounts().id(admin.id().get()).getExternalIds().stream()
-                .map(e -> e.identity)
-                .collect(toSet()))
-        .contains(extId1String);
-    assertThat(
-            gApi.accounts().id(user.id().get()).getExternalIds().stream()
-                .map(e -> e.identity)
-                .collect(toSet()))
-        .contains(extId2String);
-
-    // Ensure that we only applied one single commit.
-    int afterUpdateCommits = countExternalIdsCommits();
-    assertThat(afterUpdateCommits).isEqualTo(initialCommits + 1);
-  }
-
-  @UsedAt(UsedAt.Project.GOOGLE)
-  protected int countExternalIdsCommits() throws Exception {
-    try (Repository allUsersRepo = repoManager.openRepository(allUsers);
-        Git git = new Git(allUsersRepo)) {
-      ObjectId refsMetaExternalIdsHead =
-          allUsersRepo.exactRef(RefNames.REFS_EXTERNAL_IDS).getObjectId();
-      return Iterables.size(git.log().add(refsMetaExternalIdsHead).call());
-    }
-  }
-
-  @Test
   public void externalIdBatchUpdates_fail_sameAccount() {
     ExternalId extId1 =
         getExternalIdFactory()
@@ -3006,49 +2585,6 @@
   }
 
   @Test
-  public void externalIdBatchUpdates_commitMsg_multipleAccounts() throws Exception {
-    ExternalId extId1 =
-        getExternalIdFactory()
-            .createWithEmail(externalIdKeyFactory.parse("foo:bar"), admin.id(), "1@foo.com");
-    ExternalId extId2 =
-        getExternalIdFactory()
-            .createWithEmail(externalIdKeyFactory.parse("foo:baz"), user.id(), "2@foo.com");
-
-    AccountsUpdate.UpdateArguments ua1 =
-        new AccountsUpdate.UpdateArguments(
-            "first message", admin.id(), u -> u.addExternalId(extId1));
-    AccountsUpdate.UpdateArguments ua2 =
-        new AccountsUpdate.UpdateArguments(
-            "second message", user.id(), u -> u.addExternalId(extId2));
-    accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
-
-    try (Repository allUsersRepo = repoManager.openRepository(allUsers);
-        RevWalk rw = new RevWalk(allUsersRepo)) {
-      RevCommit commit =
-          rw.parseCommit(allUsersRepo.exactRef(RefNames.REFS_EXTERNAL_IDS).getObjectId());
-
-      assertThat(commit.getFullMessage()).isEqualTo("Batch update for 2 accounts\n");
-    }
-  }
-
-  @Test
-  public void externalIdBatchUpdates_commitMsg_singleAccount() throws Exception {
-    ExternalId extId =
-        getExternalIdFactory()
-            .createWithEmail(externalIdKeyFactory.parse("foo:bar"), admin.id(), "1@foo.com");
-
-    accountsUpdateProvider.get().update("foobar", admin.id(), u -> u.addExternalId(extId));
-
-    try (Repository allUsersRepo = repoManager.openRepository(allUsers);
-        RevWalk rw = new RevWalk(allUsersRepo)) {
-      RevCommit commit =
-          rw.parseCommit(allUsersRepo.exactRef(RefNames.REFS_EXTERNAL_IDS).getObjectId());
-
-      assertThat(commit.getFullMessage()).isEqualTo("foobar\n");
-    }
-  }
-
-  @Test
   public void searchForSecondaryEmailRequiresModifyAccountPermission() throws Exception {
     String email = "preferred@example.com";
     TestAccount foo = accountCreator.create(name("foo"), email, "Foo", null);
@@ -3070,24 +2606,6 @@
         .isEqualTo(foo.id().get());
   }
 
-  @Test
-  public void getAccountFromMetaId() throws Exception {
-    AccountState preUpdateState = accountCache.get(admin.id()).get();
-    requestScopeOperations.setApiUser(admin.id());
-    gApi.accounts().self().setStatus("New status");
-
-    AccountState postUpdateStatus = accountCache.get(admin.id()).get();
-    assertThat(postUpdateStatus).isNotEqualTo(preUpdateState);
-    assertThat(
-            accountCache.getFromMetaId(
-                admin.id(), ObjectId.fromString(preUpdateState.account().metaId())))
-        .isEqualTo(preUpdateState);
-    assertThat(
-            accountCache.getFromMetaId(
-                admin.id(), ObjectId.fromString(postUpdateStatus.account().metaId())))
-        .isEqualTo(postUpdateStatus);
-  }
-
   @CanIgnoreReturnValue
   private CommentInfo createDraft(Result r, String path, String message) throws Exception {
     DraftInput in = new DraftInput();
@@ -3121,204 +2639,6 @@
   }
 
   @Test
-  public void projectWatchesUpdate_refsUsersUpdated() throws Exception {
-    AccountState preUpdateState = accountCache.get(admin.id()).get();
-    requestScopeOperations.setApiUser(admin.id());
-
-    ProjectWatchInfo projectWatchInfo = new ProjectWatchInfo();
-    projectWatchInfo.project = project.get();
-    projectWatchInfo.notifyAllComments = true;
-    gApi.accounts().self().setWatchedProjects(ImmutableList.of(projectWatchInfo));
-
-    AccountState updatedState1 = accountCache.get(admin.id()).get();
-    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState1.account().metaId());
-
-    gApi.accounts().self().deleteWatchedProjects(ImmutableList.of(projectWatchInfo));
-
-    AccountState updatedState2 = accountCache.get(admin.id()).get();
-    assertThat(updatedState1.account().metaId()).isNotEqualTo(updatedState2.account().metaId());
-  }
-
-  @Test
-  public void updateExternalId_externalIdApiUpdate_refsUsersUpdated() throws Exception {
-    AccountState preUpdateState = accountCache.get(admin.id()).get();
-    requestScopeOperations.setApiUser(admin.id());
-
-    gApi.accounts().self().addEmail(newEmailInput("secondary@non.google"));
-    assertExternalIds(
-        admin.id(),
-        ImmutableSet.of(
-            "mailto:admin@example.com", "username:admin", "mailto:secondary@non.google"));
-
-    AccountState updatedState1 = accountCache.get(admin.id()).get();
-    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState1.account().metaId());
-
-    gApi.accounts().self().deleteExternalIds(ImmutableList.of("mailto:secondary@non.google"));
-
-    AccountState updatedState2 = accountCache.get(admin.id()).get();
-    assertThat(updatedState1.account().metaId()).isNotEqualTo(updatedState2.account().metaId());
-  }
-
-  @Test
-  public void addExternalId_accountUpdate_refsUsersUpdated() throws Exception {
-    AccountState preUpdateState = accountCache.get(admin.id()).get();
-    requestScopeOperations.setApiUser(admin.id());
-
-    ExternalId externalId = getExternalIdFactory().create("custom", "value", admin.id());
-    accountsUpdateProvider
-        .get()
-        .update("Add External ID", admin.id(), u -> u.addExternalId(externalId));
-    assertExternalIds(
-        admin.id(), ImmutableSet.of("mailto:admin@example.com", "username:admin", "custom:value"));
-
-    AccountState updatedState = accountCache.get(admin.id()).get();
-    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState.account().metaId());
-  }
-
-  @Test
-  public void deleteExternalId_accountUpdate_refsUsersUpdated() throws Exception {
-    AccountState preUpdateState = accountCache.get(admin.id()).get();
-    requestScopeOperations.setApiUser(admin.id());
-
-    ExternalId externalId = createEmailExternalId(admin.id(), "admin@example.com");
-    accountsUpdateProvider
-        .get()
-        .update("Remove External ID", admin.id(), u -> u.deleteExternalId(externalId));
-    assertExternalIds(admin.id(), ImmutableSet.of("username:admin"));
-
-    AccountState updatedState = accountCache.get(admin.id()).get();
-    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState.account().metaId());
-  }
-
-  @Test
-  public void updateExternalId_accountUpdate_refsUsersUpdated() throws Exception {
-    AccountState preUpdateState = accountCache.get(admin.id()).get();
-    requestScopeOperations.setApiUser(admin.id());
-
-    ExternalId externalId =
-        getExternalIdFactory()
-            .createWithEmail(
-                SCHEME_MAILTO, "secondary@non.google", admin.id(), "secondary@non.google");
-    accountsUpdateProvider
-        .get()
-        .update("Update External ID", admin.id(), u -> u.updateExternalId(externalId));
-    assertExternalIds(
-        admin.id(),
-        ImmutableSet.of(
-            "mailto:admin@example.com", "username:admin", "mailto:secondary@non.google"));
-
-    AccountState updatedState = accountCache.get(admin.id()).get();
-    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState.account().metaId());
-  }
-
-  @Test
-  public void replaceExternalId_accountUpdate_refsUsersUpdated() throws Exception {
-    AccountState preUpdateState = accountCache.get(admin.id()).get();
-    requestScopeOperations.setApiUser(admin.id());
-
-    ExternalId externalId =
-        getExternalIdFactory()
-            .createWithEmail(
-                SCHEME_MAILTO, "secondary@non.google", admin.id(), "secondary@non.google");
-    ExternalId oldExternalId =
-        getExternalIdsReader().get(createEmailExternalId(admin.id(), admin.email()).key()).get();
-    accountsUpdateProvider
-        .get()
-        .update(
-            "Replace External ID", admin.id(), u -> u.replaceExternalId(oldExternalId, externalId));
-    assertExternalIds(admin.id(), ImmutableSet.of("mailto:secondary@non.google", "username:admin"));
-
-    AccountState updatedState = accountCache.get(admin.id()).get();
-    assertThat(accountCache.get(admin.id()).get()).isNotSameInstanceAs(preUpdateState);
-    if (preUpdateState.account().metaId() == null) {
-      // When the test is executed on google infrastructure, metaId should be either always set
-      // or always be null.
-      assertThat(updatedState.account().metaId()).isNull();
-    } else {
-      assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState.account().metaId());
-    }
-  }
-
-  @Test
-  public void accountUpdate_updateBatch_allUsersExternalIdsUpdated_refsUsersUpdated()
-      throws Exception {
-    AccountState preUpdateAdminState = accountCache.get(admin.id()).get();
-    AccountState preUpdateUserState = accountCache.get(user.id()).get();
-
-    requestScopeOperations.setApiUser(admin.id());
-    ExternalId extId1 =
-        getExternalIdFactory()
-            .createWithEmail("custom", "admin-id", admin.id(), "admin-id@test.com");
-
-    ExternalId extId2 =
-        getExternalIdFactory().createWithEmail("custom", "user-id", user.id(), "user-id@test.com");
-
-    AccountsUpdate.UpdateArguments ua1 =
-        new AccountsUpdate.UpdateArguments(
-            "Add External ID", admin.id(), u -> u.addExternalId(extId1));
-    AccountsUpdate.UpdateArguments ua2 =
-        new AccountsUpdate.UpdateArguments(
-            "Add External ID", user.id(), u -> u.addExternalId(extId2));
-    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
-    try (Registration registration =
-        extensionRegistry.newRegistration().add(accountIndexedCounter)) {
-      accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
-    }
-    accountIndexedCounter.assertReindexOf(admin.id(), 1);
-    accountIndexedCounter.assertReindexOf(user.id(), 1);
-
-    assertExternalIds(
-        admin.id(),
-        ImmutableSet.of("mailto:admin@example.com", "username:admin", "custom:admin-id"));
-    assertExternalIds(
-        user.id(), ImmutableSet.of("username:user1", "mailto:user1@example.com", "custom:user-id"));
-    // Assert reindexing has worked on the updated accounts.
-    assertThat(
-            Iterables.getOnlyElement(gApi.accounts().query("admin-id@test.com").get())._accountId)
-        .isEqualTo(admin.id().get());
-    assertThat(Iterables.getOnlyElement(gApi.accounts().query("user-id@test.com").get())._accountId)
-        .isEqualTo(user.id().get());
-    AccountState updatedAdminState = accountCache.get(admin.id()).get();
-    AccountState updatedUserState = accountCache.get(user.id()).get();
-    assertThat(preUpdateAdminState.account().metaId())
-        .isNotEqualTo(updatedAdminState.account().metaId());
-    assertThat(preUpdateUserState.account().metaId())
-        .isNotEqualTo(updatedUserState.account().metaId());
-  }
-
-  @Test
-  public void accountUpdate_updateBatch_someUsersExternalIdsUpdated_refsUsersUpdated()
-      throws Exception {
-    AccountState preUpdateAdminState = accountCache.get(admin.id()).get();
-    AccountState preUpdateUserState = accountCache.get(user.id()).get();
-
-    requestScopeOperations.setApiUser(admin.id());
-    AccountsUpdate.UpdateArguments ua1 =
-        new AccountsUpdate.UpdateArguments(
-            "Update Display Name", admin.id(), u -> u.setDisplayName("DN"));
-    AccountsUpdate.UpdateArguments ua2 =
-        new AccountsUpdate.UpdateArguments(
-            "Remove external Id",
-            user.id(),
-            u -> u.deleteExternalId(createEmailExternalId(user.id(), user.email())));
-    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
-    try (Registration registration =
-        extensionRegistry.newRegistration().add(accountIndexedCounter)) {
-      accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
-    }
-    accountIndexedCounter.assertReindexOf(admin.id(), 1);
-    accountIndexedCounter.assertReindexOf(user.id(), 1);
-
-    // Only the version in config of the user with external id update was updated.
-    AccountState updatedAdminState = accountCache.get(admin.id()).get();
-    AccountState updatedUserState = accountCache.get(user.id()).get();
-    assertThat(preUpdateAdminState.account().metaId())
-        .isNotEqualTo(updatedAdminState.account().metaId());
-    assertThat(preUpdateUserState.account().metaId())
-        .isNotEqualTo(updatedUserState.account().metaId());
-  }
-
-  @Test
   public void accountUpdate_emptyStringsToUnset() throws Exception {
     AccountState preUpdateState = accountCache.get(admin.id()).get();
     requestScopeOperations.setApiUser(admin.id());
@@ -3499,7 +2819,7 @@
         deleted,
         PushCertificateIdent.parse(validKeyWithoutExpiration().getFirstUserId()).getEmailAddress());
     TestKey key = validKeyWithoutExpiration();
-    addGpgKey(deleted, key.getPublicKeyArmored());
+    addGpgKeyForAccount(deleted, key.getPublicKeyArmored());
     assertKeys(key);
     assertIteratorSize(1, getOnlyKeyFromStore(key).getUserIDs());
 
@@ -3795,10 +3115,11 @@
     GroupMembership testGroupMembership =
         new GroupMembership() {
           @Override
-          public Set<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(toSet());
+                .collect(toImmutableSet());
           }
 
           @Override
@@ -3833,7 +3154,7 @@
     return testGroupBackend;
   }
 
-  private void assertExternalIds(Account.Id accountId, ImmutableSet<String> extIds)
+  protected void assertExternalIds(Account.Id accountId, ImmutableSet<String> extIds)
       throws Exception {
     assertExternalIds(
         gApi.accounts().id(accountId.get()).getExternalIds().stream()
@@ -3925,10 +3246,10 @@
     Account.Id currAccountId = localCtx.getContext().getUser().getAccountId();
     Iterable<String> expectedFps =
         expected.transform(k -> BaseEncoding.base16().encode(k.getPublicKey().getFingerprint()));
-    Set<String> actualFps =
+    ImmutableSet<String> actualFps =
         getExternalIdsReader().byAccount(currAccountId, SCHEME_GPGKEY).stream()
             .map(e -> e.key().id())
-            .collect(toSet());
+            .collect(toImmutableSet());
     assertWithMessage("external IDs in database")
         .that(actualFps)
         .containsExactlyElementsIn(expectedFps);
@@ -3976,12 +3297,13 @@
   }
 
   @CanIgnoreReturnValue
-  private Map<String, GpgKeyInfo> addGpgKey(String armored) throws Exception {
-    return addGpgKey(admin, armored);
+  private Map<String, GpgKeyInfo> addGpgKeyForSelf(String armored) throws Exception {
+    return addGpgKeyForAccount(admin, armored);
   }
 
   @CanIgnoreReturnValue
-  private Map<String, GpgKeyInfo> addGpgKey(TestAccount account, String armored) throws Exception {
+  private Map<String, GpgKeyInfo> addGpgKeyForAccount(TestAccount account, String armored)
+      throws Exception {
     return testRefAction(
         () -> {
           AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
@@ -4035,15 +3357,13 @@
     return gApi.accounts().id(user.id().get());
   }
 
-  private Set<String> getCookiesNames() {
-    Set<String> cookieNames =
-        httpCookieStore.getCookies().stream()
-            .map(cookie -> cookie.getName())
-            .collect(Collectors.toSet());
-    return cookieNames;
+  private ImmutableSet<String> getCookiesNames() {
+    return httpCookieStore.getCookies().stream()
+        .map(cookie -> cookie.getName())
+        .collect(toImmutableSet());
   }
 
-  private void webLogin(Integer accountId) throws IOException, ClientProtocolException {
+  private void webLogin(Integer accountId) throws IOException {
     httpGetAndAssertStatus(
         "login?account_id=" + accountId, HttpServletResponse.SC_MOVED_TEMPORARILY);
   }
@@ -4065,50 +3385,21 @@
             r -> r.withBlockStrategy(noSleepBlockStrategy)));
   }
 
-  private ExternalIdNotes getExternalIdNotes(Repository allUsersRepo)
-      throws ConfigInvalidException, IOException {
-    return ExternalIdNotes.load(
-        allUsers,
-        allUsersRepo,
-        externalIdFactoryNoteDbImpl,
-        authConfig.isUserNameCaseInsensitiveMigrationMode());
-  }
+  @UsedAt(UsedAt.Project.GOOGLE)
+  protected abstract AccountsUpdate getAccountsUpdateWithRunnables(
+      Runnable afterReadRevision, Runnable beforeCommit, RetryHelper retryHelper);
 
   @UsedAt(UsedAt.Project.GOOGLE)
   protected ExternalIdFactory getExternalIdFactory() {
-    return externalIdFactoryNoteDbImpl;
+    return externalIdFactory;
   }
 
   @UsedAt(UsedAt.Project.GOOGLE)
   protected ExternalIds getExternalIdsReader() {
-    return externalIdsNoteDbImpl;
+    return externalIds;
   }
 
-  @UsedAt(UsedAt.Project.GOOGLE)
-  protected AccountsUpdate getAccountsUpdateWithRunnables(
-      Runnable afterReadRevision, Runnable beforeCommit, RetryHelper retryHelper) {
-    return getAccountsUpdateNoteDbImplWithRunnables(afterReadRevision, beforeCommit, retryHelper);
-  }
-
-  @UsedAt(UsedAt.Project.GOOGLE)
-  protected final AccountsUpdateNoteDbImpl getAccountsUpdateNoteDbImplWithRunnables(
-      Runnable afterReadRevision, Runnable beforeCommit, RetryHelper retryHelper) {
-    return new AccountsUpdateNoteDbImpl(
-        repoManager,
-        gitReferenceUpdated,
-        Optional.empty(),
-        allUsers,
-        externalIdsNoteDbImpl,
-        extIdNotesFactory,
-        metaDataUpdateInternalFactory,
-        retryHelper,
-        serverIdent.get(),
-        afterReadRevision,
-        beforeCommit);
-  }
-
-  private void httpGetAndAssertStatus(String urlPath, int expectedHttpStatus)
-      throws ClientProtocolException, IOException {
+  private void httpGetAndAssertStatus(String urlPath, int expectedHttpStatus) throws IOException {
     HttpGet httpGet = new HttpGet(canonicalWebUrl.get() + urlPath);
     HttpResponse loginResponse = httpclient.execute(httpGet);
     assertThat(loginResponse.getStatusLine().getStatusCode()).isEqualTo(expectedHttpStatus);
@@ -4166,7 +3457,7 @@
   }
 
   public static class TestAccountStateProvider implements AccountStateProvider {
-    private ArrayList<MetadataInfo> metadataList = new ArrayList<>();
+    private final List<MetadataInfo> metadataList = new ArrayList<>();
 
     public MetadataInfo addMetadata(
         String name, @Nullable String value, @Nullable String description) {
@@ -4183,4 +3474,104 @@
       return ImmutableList.copyOf(metadataList);
     }
   }
+
+  @Test
+  @GerritConfig(name = "accounts.visibility", value = "SAME_GROUP")
+  public void queryAccountsVisibilitySameGroup() throws Exception {
+    TestAccount user2 = accountCreator.user2();
+
+    // Switch to user context (user and user2 are not in the same group by default)
+    requestScopeOperations.setApiUser(user.id());
+
+    // Querying for user2 should return nothing
+    List<AccountInfo> result = gApi.accounts().query("email:" + user2.email()).get();
+    assertThat(result).isEmpty();
+
+    // Querying for user2 with details should also return nothing (reproduces Bypass 1)
+    result =
+        gApi.accounts()
+            .query("email:" + user2.email())
+            .withOption(ListAccountsOption.DETAILS)
+            .get();
+    assertThat(result).isEmpty();
+
+    // Suggesting for user2 should also return nothing (reproduces Bypass 2)
+    result = gApi.accounts().suggestAccounts(user2.email()).get();
+    assertThat(result).isEmpty();
+
+    // Querying for self should still work
+    result = gApi.accounts().query("email:" + user.email()).get();
+    assertThat(result).hasSize(1);
+    assertThat(result.get(0)._accountId).isEqualTo(user.id().get());
+  }
+
+  @Test
+  @GerritConfig(name = "accounts.visibility", value = "NONE")
+  public void queryAccountsVisibilityNone() throws Exception {
+    TestAccount user2 = accountCreator.user2();
+
+    requestScopeOperations.setApiUser(user.id());
+
+    // Querying for user2 should return nothing
+    List<AccountInfo> result = gApi.accounts().query("email:" + user2.email()).get();
+    assertThat(result).isEmpty();
+
+    // Querying for self should still return self
+    result = gApi.accounts().query("email:" + user.email()).get();
+    assertThat(result).hasSize(1);
+    assertThat(result.get(0)._accountId).isEqualTo(user.id().get());
+  }
+
+  @Test
+  @GerritConfig(name = "accounts.visibility", value = "SAME_GROUP")
+  public void queryAccountsAnonymousUserSameGroup() throws Exception {
+    requestScopeOperations.setApiUserAnonymous();
+
+    // Anonymous user is not in same group as user, so query should return empty list
+    List<AccountInfo> result =
+        gApi.accounts().query("email:" + user.email()).withOption(ListAccountsOption.DETAILS).get();
+    assertThat(result).isEmpty();
+
+    // Suggest should also return empty list
+    result = gApi.accounts().suggestAccounts(user.email()).get();
+    assertThat(result).isEmpty();
+  }
+
+  @Test
+  @GerritConfig(name = "accounts.visibility", value = "VISIBLE_GROUP")
+  public void queryAccountsAnonymousUserVisibleGroup() throws Exception {
+    requestScopeOperations.setApiUserAnonymous();
+
+    // Anonymous user cannot see user, so query should return empty list
+    List<AccountInfo> result =
+        gApi.accounts().query("email:" + user.email()).withOption(ListAccountsOption.DETAILS).get();
+    assertThat(result).isEmpty();
+
+    // Suggest should also return empty list
+    result = gApi.accounts().suggestAccounts(user.email()).get();
+    assertThat(result).isEmpty();
+  }
+
+  @Test
+  @GerritConfig(name = "accounts.visibility", value = "ALL")
+  public void queryAccountsAnonymousUserVisibilityAll() throws Exception {
+    requestScopeOperations.setApiUserAnonymous();
+
+    // Query with details should return details (name, email)
+    List<AccountInfo> result =
+        gApi.accounts().query("email:" + user.email()).withOption(ListAccountsOption.DETAILS).get();
+    assertThat(result).isNotEmpty();
+    for (AccountInfo info : result) {
+      assertThat(info.name).isEqualTo(user.fullName());
+      assertThat(info.email).isEqualTo(user.email());
+    }
+
+    // Suggest should return details (name, email)
+    result = gApi.accounts().suggestAccounts(user.email()).get();
+    assertThat(result).isNotEmpty();
+    for (AccountInfo info : result) {
+      assertThat(info.name).isEqualTo(user.fullName());
+      assertThat(info.email).isEqualTo(user.email());
+    }
+  }
 }
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/accounts/AccountNoteDbIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountNoteDbIT.java
new file mode 100644
index 0000000..7a32314
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountNoteDbIT.java
@@ -0,0 +1,783 @@
+package com.google.gerrit.acceptance.api.accounts;
+
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.GitUtil.deleteRef;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.server.account.AccountProperties.ACCOUNT;
+import static com.google.gerrit.server.account.AccountProperties.ACCOUNT_CONFIG;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
+import static com.google.gerrit.truth.ConfigSubject.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.AccountIndexedCounter;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Permission;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.client.ProjectWatchInfo;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.EmailInfo;
+import com.google.gerrit.server.Sequence;
+import com.google.gerrit.server.account.AccountProperties;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIds;
+import com.google.gerrit.server.account.externalids.storage.notedb.ExternalIdFactoryNoteDbImpl;
+import com.google.gerrit.server.account.externalids.storage.notedb.ExternalIdNotes;
+import com.google.gerrit.server.account.externalids.storage.notedb.ExternalIdsNoteDbImpl;
+import com.google.gerrit.server.account.storage.notedb.AccountsUpdateNoteDbImpl;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.server.index.account.AccountIndexer;
+import com.google.gerrit.server.index.account.StalenessChecker;
+import com.google.gerrit.server.project.RefPattern;
+import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.Test;
+
+public class AccountNoteDbIT extends AbstractAccountIT {
+  @Inject private AccountIndexer accountIndexer;
+  @Inject private StalenessChecker stalenessChecker;
+  @Inject private ExternalIdNotes.Factory extIdNotesFactory;
+  @Inject private ExternalIdsNoteDbImpl externalIdsNoteDbImpl;
+  @Inject private GitReferenceUpdated gitReferenceUpdated;
+  @Inject private ExternalIdFactoryNoteDbImpl externalIdFactoryNoteDbImpl;
+
+  @Override
+  protected ExternalIdFactory getExternalIdFactory() {
+    return externalIdFactoryNoteDbImpl;
+  }
+
+  @Override
+  protected ExternalIds getExternalIdsReader() {
+    return externalIdsNoteDbImpl;
+  }
+
+  @Override
+  protected AccountsUpdate getAccountsUpdateWithRunnables(
+      Runnable afterReadRevision, Runnable beforeCommit, RetryHelper retryHelper) {
+    return getAccountsUpdateNoteDbImplWithRunnables(afterReadRevision, beforeCommit, retryHelper);
+  }
+
+  @UsedAt(UsedAt.Project.GOOGLE)
+  protected Account.Id createByAccountCreator(int expectedAccountReindexCalls) throws Exception {
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
+    try (Registration registration =
+        extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+      String name = "foo";
+      TestAccount foo = accountCreator.create(name);
+      AccountInfo info = gApi.accounts().id(foo.id().get()).get();
+      if (server.isUsernameSupported()) {
+        assertThat(info.username).isEqualTo(name);
+      } else {
+        assertThat(info.email).isEqualTo(foo.email());
+      }
+      assertThat(info.name).isEqualTo(name);
+      accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
+      assertUserBranch(foo.id(), name, null);
+      return foo.id();
+    }
+  }
+
+  @Test
+  public void createByAccountCreator() throws Exception {
+    RefUpdateCounter refUpdateCounter = createRefUpdateCounter();
+    try (Registration registration = extensionRegistry.newRegistration().add(refUpdateCounter)) {
+      Account.Id accountId = createByAccountCreator(1);
+      refUpdateCounter.assertRefUpdateFor(
+          RefUpdateCounter.projectRef(allUsers, RefNames.refsUsers(accountId)),
+          RefUpdateCounter.projectRef(allUsers, RefNames.REFS_EXTERNAL_IDS),
+          RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequence.NAME_ACCOUNTS));
+    }
+  }
+
+  @Test
+  public void createAnonymousCowardByAccountCreator() throws Exception {
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
+    try (Registration registration =
+        extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+      TestAccount anonymousCoward = accountCreator.create();
+      accountIndexedCounter.assertReindexOf(anonymousCoward);
+      assertUserBranchWithoutAccountConfig(anonymousCoward.id());
+    }
+  }
+
+  @Test
+  public void commitMessageOnAccountUpdates() throws Exception {
+    AccountsUpdate au = accountsUpdateProvider.get();
+    Account.Id accountId = Account.id(seq.nextAccountId());
+    au.insert("Create Test Account", accountId, u -> {});
+    assertLastCommitMessageOfUserBranch(accountId, "Create Test Account");
+
+    au.update("Set Status", accountId, u -> u.setStatus("Foo"));
+    assertLastCommitMessageOfUserBranch(accountId, "Set Status");
+  }
+
+  private void assertLastCommitMessageOfUserBranch(Account.Id accountId, String expectedMessage)
+      throws Exception {
+    try (Repository repo = repoManager.openRepository(allUsers);
+        RevWalk rw = new RevWalk(repo)) {
+      Ref exactRef = repo.exactRef(RefNames.refsUsers(accountId));
+      assertThat(rw.parseCommit(exactRef.getObjectId()).getShortMessage())
+          .isEqualTo(expectedMessage);
+    }
+  }
+
+  @Test
+  @UseClockStep
+  public void createAtomically() throws Exception {
+    Account.Id accountId = Account.id(seq.nextAccountId());
+    String fullName = "Foo";
+    ExternalId extId = getExternalIdFactory().createEmail(accountId, "foo@example.com");
+    AccountState accountState =
+        accountsUpdateProvider
+            .get()
+            .insert(
+                "Create Account Atomically",
+                accountId,
+                u -> u.setFullName(fullName).addExternalId(extId));
+    assertThat(accountState.account().fullName()).isEqualTo(fullName);
+
+    AccountInfo info = gApi.accounts().id(accountId.get()).get();
+    assertThat(info.name).isEqualTo(fullName);
+
+    List<EmailInfo> emails = gApi.accounts().id(accountId.get()).getEmails();
+    assertThat(emails.stream().map(e -> e.email).collect(toImmutableSet()))
+        .containsExactly(extId.email());
+
+    RevCommit commitUserBranch =
+        projectOperations.project(allUsers).getHead(RefNames.refsUsers(accountId));
+    RevCommit commitRefsMetaExternalIds =
+        projectOperations.project(allUsers).getHead(RefNames.REFS_EXTERNAL_IDS);
+    assertThat(commitUserBranch.getCommitTime())
+        .isEqualTo(commitRefsMetaExternalIds.getCommitTime());
+  }
+
+  @Test
+  public void updateAccountWithoutAccountConfigNoteDb() throws Exception {
+    TestAccount anonymousCoward = accountCreator.create();
+    assertUserBranchWithoutAccountConfig(anonymousCoward.id());
+
+    String status = "OOO";
+    Optional<AccountState> accountState =
+        accountsUpdateProvider
+            .get()
+            .update("Set status", anonymousCoward.id(), u -> u.setStatus(status));
+    assertThat(accountState).isPresent();
+    Account account = accountState.get().account();
+    assertThat(account.fullName()).isNull();
+    assertThat(account.status()).isEqualTo(status);
+    assertUserBranch(anonymousCoward.id(), null, status);
+  }
+
+  private void assertUserBranchWithoutAccountConfig(Account.Id accountId) throws Exception {
+    assertUserBranch(accountId, null, null);
+  }
+
+  private void assertUserBranch(
+      Account.Id accountId, @Nullable String name, @Nullable String status) throws Exception {
+    try (Repository repo = repoManager.openRepository(allUsers);
+        RevWalk rw = new RevWalk(repo);
+        ObjectReader or = repo.newObjectReader()) {
+      Ref ref = repo.exactRef(RefNames.refsUsers(accountId));
+      assertThat(ref).isNotNull();
+      RevCommit c = rw.parseCommit(ref.getObjectId());
+      long timestampDiffMs =
+          Math.abs(c.getCommitTime() * 1000L - getAccount(accountId).registeredOn().toEpochMilli());
+      assertThat(timestampDiffMs).isAtMost(Duration.ofSeconds(1).toMillis());
+
+      // Check the 'account.config' file.
+      try (TreeWalk tw = TreeWalk.forPath(or, ACCOUNT_CONFIG, c.getTree())) {
+        if (name != null || status != null) {
+          assertThat(tw).isNotNull();
+          Config cfg = new Config();
+          cfg.fromText(new String(or.open(tw.getObjectId(0), OBJ_BLOB).getBytes(), UTF_8));
+          assertThat(cfg)
+              .stringValue(ACCOUNT, null, AccountProperties.KEY_FULL_NAME)
+              .isEqualTo(name);
+          assertThat(cfg)
+              .stringValue(ACCOUNT, null, AccountProperties.KEY_STATUS)
+              .isEqualTo(status);
+        } else {
+          // No account properties were set, hence an 'account.config' file was not created.
+          assertThat(tw).isNull();
+        }
+      }
+    }
+  }
+
+  @Test
+  public void checkMetaIdAndUniqueTag() throws Exception {
+    // In open-source Gerrit, the uniqueTag and metaId are always the same. Check them together
+    // in this test.
+    // metaId and uniqueTag are set when account is loaded
+    assertThat(accounts.get(admin.id()).get().account().metaId()).isEqualTo(getMetaId(admin.id()));
+    assertThat(accounts.get(admin.id()).get().account().uniqueTag())
+        .isEqualTo(getMetaId(admin.id()));
+
+    // metaId and uniqueTag are set when account is created
+    AccountsUpdate au = accountsUpdateProvider.get();
+    Account.Id accountId = Account.id(seq.nextAccountId());
+    AccountState accountState = au.insert("Create Test Account", accountId, u -> {});
+    assertThat(accountState.account().metaId()).isEqualTo(getMetaId(accountId));
+    assertThat(accountState.account().uniqueTag()).isEqualTo(getMetaId(accountId));
+
+    // metaId and uniqueTag are set when account is updated
+    Optional<AccountState> updatedAccountState =
+        au.update("Set Full Name", accountId, u -> u.setFullName("foo"));
+    assertThat(updatedAccountState).isPresent();
+    Account updatedAccount = updatedAccountState.get().account();
+    assertThat(accountState.account().metaId()).isNotEqualTo(updatedAccount.metaId());
+    assertThat(accountState.account().uniqueTag()).isNotEqualTo(updatedAccount.uniqueTag());
+    assertThat(updatedAccount.metaId()).isEqualTo(getMetaId(accountId));
+    assertThat(updatedAccount.uniqueTag()).isEqualTo(getMetaId(accountId));
+  }
+
+  @Nullable
+  private String getMetaId(Account.Id accountId) throws IOException {
+    try (Repository repo = repoManager.openRepository(allUsers);
+        RevWalk rw = new RevWalk(repo);
+        ObjectReader or = repo.newObjectReader()) {
+      Ref ref = repo.exactRef(RefNames.refsUsers(accountId));
+      return ref != null ? ref.getObjectId().name() : null;
+    }
+  }
+
+  @Test
+  public void externalIdBatchUpdates() throws Exception {
+    String extId1String = "foo:bar";
+    String extId2String = "foo:baz";
+    ExternalId extId1 =
+        getExternalIdFactory()
+            .createWithEmail(externalIdKeyFactory.parse(extId1String), admin.id(), "1@foo.com");
+    ExternalId extId2 =
+        getExternalIdFactory()
+            .createWithEmail(externalIdKeyFactory.parse(extId2String), user.id(), "2@foo.com");
+
+    int initialCommits = countExternalIdsCommits();
+    AccountsUpdate.UpdateArguments ua1 =
+        new AccountsUpdate.UpdateArguments(
+            "Add External ID", admin.id(), u -> u.addExternalId(extId1));
+    AccountsUpdate.UpdateArguments ua2 =
+        new AccountsUpdate.UpdateArguments(
+            "Add External ID", user.id(), u -> u.addExternalId(extId2));
+    ImmutableList<Optional<AccountState>> accountStates =
+        accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
+    assertThat(accountStates).hasSize(2);
+    assertThat(accountStates.get(0).get().externalIds()).contains(extId1);
+    assertThat(accountStates.get(1).get().externalIds()).contains(extId2);
+    assertThat(
+            gApi.accounts().id(admin.id().get()).getExternalIds().stream()
+                .map(e -> e.identity)
+                .collect(toImmutableSet()))
+        .contains(extId1String);
+    assertThat(
+            gApi.accounts().id(user.id().get()).getExternalIds().stream()
+                .map(e -> e.identity)
+                .collect(toImmutableSet()))
+        .contains(extId2String);
+
+    // Ensure that we only applied one single commit.
+    int afterUpdateCommits = countExternalIdsCommits();
+    assertThat(afterUpdateCommits).isEqualTo(initialCommits + 1);
+  }
+
+  @UsedAt(UsedAt.Project.GOOGLE)
+  protected int countExternalIdsCommits() throws Exception {
+    try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+        Git git = new Git(allUsersRepo)) {
+      ObjectId refsMetaExternalIdsHead =
+          allUsersRepo.exactRef(RefNames.REFS_EXTERNAL_IDS).getObjectId();
+      return Iterables.size(git.log().add(refsMetaExternalIdsHead).call());
+    }
+  }
+
+  @Test
+  public void externalIdBatchUpdates_commitMsg_multipleAccounts() throws Exception {
+    ExternalId extId1 =
+        getExternalIdFactory()
+            .createWithEmail(externalIdKeyFactory.parse("foo:bar"), admin.id(), "1@foo.com");
+    ExternalId extId2 =
+        getExternalIdFactory()
+            .createWithEmail(externalIdKeyFactory.parse("foo:baz"), user.id(), "2@foo.com");
+
+    AccountsUpdate.UpdateArguments ua1 =
+        new AccountsUpdate.UpdateArguments(
+            "first message", admin.id(), u -> u.addExternalId(extId1));
+    AccountsUpdate.UpdateArguments ua2 =
+        new AccountsUpdate.UpdateArguments(
+            "second message", user.id(), u -> u.addExternalId(extId2));
+    accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
+
+    try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+        RevWalk rw = new RevWalk(allUsersRepo)) {
+      RevCommit commit =
+          rw.parseCommit(allUsersRepo.exactRef(RefNames.REFS_EXTERNAL_IDS).getObjectId());
+
+      assertThat(commit.getFullMessage()).isEqualTo("Batch update for 2 accounts\n");
+    }
+  }
+
+  @Test
+  public void externalIdBatchUpdates_commitMsg_singleAccount() throws Exception {
+    ExternalId extId =
+        getExternalIdFactory()
+            .createWithEmail(externalIdKeyFactory.parse("foo:bar"), admin.id(), "1@foo.com");
+
+    accountsUpdateProvider.get().update("foobar", admin.id(), u -> u.addExternalId(extId));
+
+    try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+        RevWalk rw = new RevWalk(allUsersRepo)) {
+      RevCommit commit =
+          rw.parseCommit(allUsersRepo.exactRef(RefNames.REFS_EXTERNAL_IDS).getObjectId());
+
+      assertThat(commit.getFullMessage()).isEqualTo("foobar\n");
+    }
+  }
+
+  @Test
+  public void getAccountFromMetaId() throws Exception {
+    AccountState preUpdateState = accountCache.get(admin.id()).get();
+    requestScopeOperations.setApiUser(admin.id());
+    gApi.accounts().self().setStatus("New status");
+
+    AccountState postUpdateStatus = accountCache.get(admin.id()).get();
+    assertThat(postUpdateStatus).isNotEqualTo(preUpdateState);
+    assertThat(
+            accountCache.getFromMetaId(
+                admin.id(), ObjectId.fromString(preUpdateState.account().metaId())))
+        .isEqualTo(preUpdateState);
+    assertThat(
+            accountCache.getFromMetaId(
+                admin.id(), ObjectId.fromString(postUpdateStatus.account().metaId())))
+        .isEqualTo(postUpdateStatus);
+  }
+
+  @Test
+  public void projectWatchesUpdate_refsUsersUpdated() throws Exception {
+    AccountState preUpdateState = accountCache.get(admin.id()).get();
+    requestScopeOperations.setApiUser(admin.id());
+
+    ProjectWatchInfo projectWatchInfo = new ProjectWatchInfo();
+    projectWatchInfo.project = project.get();
+    projectWatchInfo.notifyAllComments = true;
+    gApi.accounts().self().setWatchedProjects(ImmutableList.of(projectWatchInfo));
+
+    AccountState updatedState1 = accountCache.get(admin.id()).get();
+    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState1.account().metaId());
+
+    gApi.accounts().self().deleteWatchedProjects(ImmutableList.of(projectWatchInfo));
+
+    AccountState updatedState2 = accountCache.get(admin.id()).get();
+    assertThat(updatedState1.account().metaId()).isNotEqualTo(updatedState2.account().metaId());
+  }
+
+  @Test
+  public void updateExternalId_externalIdApiUpdate_refsUsersUpdated() throws Exception {
+    AccountState preUpdateState = accountCache.get(admin.id()).get();
+    requestScopeOperations.setApiUser(admin.id());
+
+    gApi.accounts().self().addEmail(newEmailInput("secondary@non.google"));
+    assertExternalIds(
+        admin.id(),
+        ImmutableSet.of(
+            "mailto:admin@example.com", "username:admin", "mailto:secondary@non.google"));
+
+    AccountState updatedState1 = accountCache.get(admin.id()).get();
+    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState1.account().metaId());
+
+    gApi.accounts().self().deleteExternalIds(ImmutableList.of("mailto:secondary@non.google"));
+
+    AccountState updatedState2 = accountCache.get(admin.id()).get();
+    assertThat(updatedState1.account().metaId()).isNotEqualTo(updatedState2.account().metaId());
+  }
+
+  @Test
+  public void addExternalId_accountUpdate_refsUsersUpdated() throws Exception {
+    AccountState preUpdateState = accountCache.get(admin.id()).get();
+    requestScopeOperations.setApiUser(admin.id());
+
+    ExternalId externalId = getExternalIdFactory().create("custom", "value", admin.id());
+    accountsUpdateProvider
+        .get()
+        .update("Add External ID", admin.id(), u -> u.addExternalId(externalId));
+    assertExternalIds(
+        admin.id(), ImmutableSet.of("mailto:admin@example.com", "username:admin", "custom:value"));
+
+    AccountState updatedState = accountCache.get(admin.id()).get();
+    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState.account().metaId());
+  }
+
+  @Test
+  public void deleteExternalId_accountUpdate_refsUsersUpdated() throws Exception {
+    AccountState preUpdateState = accountCache.get(admin.id()).get();
+    requestScopeOperations.setApiUser(admin.id());
+
+    ExternalId externalId = createEmailExternalId(admin.id(), "admin@example.com");
+    accountsUpdateProvider
+        .get()
+        .update("Remove External ID", admin.id(), u -> u.deleteExternalId(externalId));
+    assertExternalIds(admin.id(), ImmutableSet.of("username:admin"));
+
+    AccountState updatedState = accountCache.get(admin.id()).get();
+    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState.account().metaId());
+  }
+
+  @Test
+  public void updateExternalId_accountUpdate_refsUsersUpdated() throws Exception {
+    AccountState preUpdateState = accountCache.get(admin.id()).get();
+    requestScopeOperations.setApiUser(admin.id());
+
+    ExternalId externalId =
+        getExternalIdFactory()
+            .createWithEmail(
+                SCHEME_MAILTO, "secondary@non.google", admin.id(), "secondary@non.google");
+    accountsUpdateProvider
+        .get()
+        .update("Update External ID", admin.id(), u -> u.updateExternalId(externalId));
+    assertExternalIds(
+        admin.id(),
+        ImmutableSet.of(
+            "mailto:admin@example.com", "username:admin", "mailto:secondary@non.google"));
+
+    AccountState updatedState = accountCache.get(admin.id()).get();
+    assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState.account().metaId());
+  }
+
+  @Test
+  public void replaceExternalId_accountUpdate_refsUsersUpdated() throws Exception {
+    AccountState preUpdateState = accountCache.get(admin.id()).get();
+    requestScopeOperations.setApiUser(admin.id());
+
+    ExternalId externalId =
+        getExternalIdFactory()
+            .createWithEmail(
+                SCHEME_MAILTO, "secondary@non.google", admin.id(), "secondary@non.google");
+    ExternalId oldExternalId =
+        getExternalIdsReader().get(createEmailExternalId(admin.id(), admin.email()).key()).get();
+    accountsUpdateProvider
+        .get()
+        .update(
+            "Replace External ID", admin.id(), u -> u.replaceExternalId(oldExternalId, externalId));
+    assertExternalIds(admin.id(), ImmutableSet.of("mailto:secondary@non.google", "username:admin"));
+
+    AccountState updatedState = accountCache.get(admin.id()).get();
+    assertThat(accountCache.get(admin.id()).get()).isNotSameInstanceAs(preUpdateState);
+    if (preUpdateState.account().metaId() == null) {
+      // When the test is executed on google infrastructure, metaId should be either always set
+      // or always be null.
+      assertThat(updatedState.account().metaId()).isNull();
+    } else {
+      assertThat(preUpdateState.account().metaId()).isNotEqualTo(updatedState.account().metaId());
+    }
+  }
+
+  @Test
+  public void accountUpdate_updateBatch_allUsersExternalIdsUpdated_refsUsersUpdated()
+      throws Exception {
+    AccountState preUpdateAdminState = accountCache.get(admin.id()).get();
+    AccountState preUpdateUserState = accountCache.get(user.id()).get();
+
+    requestScopeOperations.setApiUser(admin.id());
+    ExternalId extId1 =
+        getExternalIdFactory()
+            .createWithEmail("custom", "admin-id", admin.id(), "admin-id@test.com");
+
+    ExternalId extId2 =
+        getExternalIdFactory().createWithEmail("custom", "user-id", user.id(), "user-id@test.com");
+
+    AccountsUpdate.UpdateArguments ua1 =
+        new AccountsUpdate.UpdateArguments(
+            "Add External ID", admin.id(), u -> u.addExternalId(extId1));
+    AccountsUpdate.UpdateArguments ua2 =
+        new AccountsUpdate.UpdateArguments(
+            "Add External ID", user.id(), u -> u.addExternalId(extId2));
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
+    try (Registration registration =
+        extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+      accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
+    }
+    accountIndexedCounter.assertReindexOf(admin.id(), 1);
+    accountIndexedCounter.assertReindexOf(user.id(), 1);
+
+    assertExternalIds(
+        admin.id(),
+        ImmutableSet.of("mailto:admin@example.com", "username:admin", "custom:admin-id"));
+    assertExternalIds(
+        user.id(), ImmutableSet.of("username:user1", "mailto:user1@example.com", "custom:user-id"));
+    // Assert reindexing has worked on the updated accounts.
+    assertThat(
+            Iterables.getOnlyElement(gApi.accounts().query("admin-id@test.com").get())._accountId)
+        .isEqualTo(admin.id().get());
+    assertThat(Iterables.getOnlyElement(gApi.accounts().query("user-id@test.com").get())._accountId)
+        .isEqualTo(user.id().get());
+    AccountState updatedAdminState = accountCache.get(admin.id()).get();
+    AccountState updatedUserState = accountCache.get(user.id()).get();
+    assertThat(preUpdateAdminState.account().metaId())
+        .isNotEqualTo(updatedAdminState.account().metaId());
+    assertThat(preUpdateUserState.account().metaId())
+        .isNotEqualTo(updatedUserState.account().metaId());
+  }
+
+  @Test
+  public void accountUpdate_updateBatch_someUsersExternalIdsUpdated_refsUsersUpdated()
+      throws Exception {
+    AccountState preUpdateAdminState = accountCache.get(admin.id()).get();
+    AccountState preUpdateUserState = accountCache.get(user.id()).get();
+
+    requestScopeOperations.setApiUser(admin.id());
+    AccountsUpdate.UpdateArguments ua1 =
+        new AccountsUpdate.UpdateArguments(
+            "Update Display Name", admin.id(), u -> u.setDisplayName("DN"));
+    AccountsUpdate.UpdateArguments ua2 =
+        new AccountsUpdate.UpdateArguments(
+            "Remove external Id",
+            user.id(),
+            u -> u.deleteExternalId(createEmailExternalId(user.id(), user.email())));
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
+    try (Registration registration =
+        extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+      accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
+    }
+    accountIndexedCounter.assertReindexOf(admin.id(), 1);
+    accountIndexedCounter.assertReindexOf(user.id(), 1);
+
+    // Only the version in config of the user with external id update was updated.
+    AccountState updatedAdminState = accountCache.get(admin.id()).get();
+    AccountState updatedUserState = accountCache.get(user.id()).get();
+    assertThat(preUpdateAdminState.account().metaId())
+        .isNotEqualTo(updatedAdminState.account().metaId());
+    assertThat(preUpdateUserState.account().metaId())
+        .isNotEqualTo(updatedUserState.account().metaId());
+  }
+
+  @Test
+  public void deleteUserBranchWithAccessDatabaseCapability() throws Exception {
+    projectOperations
+        .allProjectsForUpdate()
+        .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+        .update();
+    projectOperations
+        .project(allUsers)
+        .forUpdate()
+        .add(
+            allow(Permission.DELETE)
+                .ref(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}")
+                .group(REGISTERED_USERS)
+                .force(true))
+        .update();
+
+    TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+    String userRef = RefNames.refsUsers(admin.id());
+    PushResult r = deleteRef(allUsersRepo, userRef);
+    RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
+    assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.OK);
+
+    try (Repository repo = repoManager.openRepository(allUsers)) {
+      assertThat(repo.exactRef(userRef)).isNull();
+    }
+
+    assertThat(accountCache.get(admin.id())).isEmpty();
+    assertThat(accountQueryProvider.get().byDefault(admin.id().toString(), true)).isEmpty();
+  }
+
+  @Test
+  public void cannotDeleteUserBranch() throws Exception {
+    projectOperations
+        .project(allUsers)
+        .forUpdate()
+        .add(
+            allow(Permission.DELETE)
+                .ref(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}")
+                .group(REGISTERED_USERS)
+                .force(true))
+        .update();
+
+    TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+    String userRef = RefNames.refsUsers(admin.id());
+    PushResult r = deleteRef(allUsersRepo, userRef);
+    RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
+    assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
+    assertThat(refUpdate.getMessage()).contains("Not allowed to delete user branch.");
+
+    try (Repository repo = repoManager.openRepository(allUsers)) {
+      assertThat(repo.exactRef(userRef)).isNotNull();
+    }
+  }
+
+  @Test
+  public void createDefaultUserBranch() throws Exception {
+    try (Repository repo = repoManager.openRepository(allUsers)) {
+      assertThat(repo.exactRef(RefNames.REFS_USERS_DEFAULT)).isNull();
+    }
+
+    projectOperations
+        .project(allUsers)
+        .forUpdate()
+        .add(allow(Permission.CREATE).ref(RefNames.REFS_USERS_DEFAULT).group(adminGroupUuid()))
+        .add(allow(Permission.PUSH).ref(RefNames.REFS_USERS_DEFAULT).group(adminGroupUuid()))
+        .update();
+
+    TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+    pushFactory
+        .create(admin.newIdent(), allUsersRepo)
+        .to(RefNames.REFS_USERS_DEFAULT)
+        .assertOkStatus();
+
+    try (Repository repo = repoManager.openRepository(allUsers)) {
+      assertThat(repo.exactRef(RefNames.REFS_USERS_DEFAULT)).isNotNull();
+    }
+  }
+
+  @Test
+  public void stalenessChecker() throws Exception {
+    // Newly created account is not stale.
+    AccountInfo accountInfo = gApi.accounts().create(name("foo")).get();
+    Account.Id accountId = Account.id(accountInfo._accountId);
+    assertThat(stalenessChecker.check(accountId).isStale()).isFalse();
+
+    // Manually updating the user ref makes the index document stale.
+    String userRef = RefNames.refsUsers(accountId);
+    testRefAction(
+        () -> {
+          try (Repository repo = repoManager.openRepository(allUsers);
+              ObjectInserter oi = repo.newObjectInserter();
+              RevWalk rw = new RevWalk(repo)) {
+            RevCommit commit = rw.parseCommit(repo.exactRef(userRef).getObjectId());
+
+            PersonIdent ident = new PersonIdent(serverIdent.get(), TimeUtil.now());
+            CommitBuilder cb = new CommitBuilder();
+            cb.setTreeId(commit.getTree());
+            cb.setCommitter(ident);
+            cb.setAuthor(ident);
+            cb.setMessage(commit.getFullMessage());
+            ObjectId emptyCommit = oi.insert(cb);
+            oi.flush();
+
+            RefUpdate updateRef = repo.updateRef(userRef);
+            updateRef.setExpectedOldObjectId(commit.toObjectId());
+            updateRef.setNewObjectId(emptyCommit);
+            assertThat(updateRef.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
+          }
+        });
+    assertStaleAccountAndReindex(accountId);
+
+    // Manually inserting/updating/deleting an external ID of the user makes the index document
+    // stale.
+    try (Repository repo = repoManager.openRepository(allUsers)) {
+      testRefAction(
+          () -> {
+            ExternalIdNotes extIdNotes = getExternalIdNotes(repo);
+
+            ExternalId.Key key = externalIdKeyFactory.create("foo", "foo");
+            extIdNotes.insert(getExternalIdFactory().create(key, accountId));
+            try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
+              extIdNotes.commit(update);
+            }
+            assertStaleAccountAndReindex(accountId);
+
+            extIdNotes = getExternalIdNotes(repo);
+            extIdNotes.upsert(
+                getExternalIdFactory().createWithEmail(key, accountId, "foo@example.com"));
+            try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
+              extIdNotes.commit(update);
+            }
+            assertStaleAccountAndReindex(accountId);
+
+            extIdNotes = getExternalIdNotes(repo);
+            extIdNotes.delete(accountId, key);
+            try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
+              extIdNotes.commit(update);
+            }
+          });
+      assertStaleAccountAndReindex(accountId);
+    }
+
+    // Manually delete account
+    testRefAction(
+        () -> {
+          try (Repository repo = repoManager.openRepository(allUsers);
+              RevWalk rw = new RevWalk(repo)) {
+            RevCommit commit = rw.parseCommit(repo.exactRef(userRef).getObjectId());
+            RefUpdate updateRef = repo.updateRef(userRef);
+            updateRef.setExpectedOldObjectId(commit.toObjectId());
+            updateRef.setNewObjectId(ObjectId.zeroId());
+            updateRef.setForceUpdate(true);
+            assertThat(updateRef.delete()).isEqualTo(RefUpdate.Result.FORCED);
+          }
+        });
+    assertStaleAccountAndReindex(accountId);
+  }
+
+  private ExternalIdNotes getExternalIdNotes(Repository allUsersRepo)
+      throws ConfigInvalidException, IOException {
+    return ExternalIdNotes.load(
+        allUsers,
+        allUsersRepo,
+        externalIdFactoryNoteDbImpl,
+        authConfig.isUserNameCaseInsensitiveMigrationMode());
+  }
+
+  @UsedAt(UsedAt.Project.GOOGLE)
+  protected final AccountsUpdateNoteDbImpl getAccountsUpdateNoteDbImplWithRunnables(
+      Runnable afterReadRevision, Runnable beforeCommit, RetryHelper retryHelper) {
+    return new AccountsUpdateNoteDbImpl(
+        repoManager,
+        gitReferenceUpdated,
+        Optional.empty(),
+        allUsers,
+        externalIdsNoteDbImpl,
+        extIdNotesFactory,
+        metaDataUpdateInternalFactory,
+        retryHelper,
+        serverIdent.get(),
+        afterReadRevision,
+        beforeCommit);
+  }
+
+  private void assertStaleAccountAndReindex(Account.Id accountId) throws IOException {
+    assertThat(stalenessChecker.check(accountId).isStale()).isTrue();
+
+    // Reindex fixes staleness
+    accountIndexer.index(accountId);
+    assertThat(stalenessChecker.check(accountId).isStale()).isFalse();
+  }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/BUILD b/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
index c441402..a09ecf5 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
@@ -2,7 +2,10 @@
 load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
 
 acceptance_tests(
-    srcs = glob(["*IT.java"]),
+    srcs = glob(
+        ["*IT.java"],
+        exclude = ["AbstractAccountIT.java"],
+    ),
     group = "api_account",
     labels = [
         "api",
@@ -20,7 +23,10 @@
 java_library(
     name = "util",
     testonly = True,
-    srcs = glob(["TestRealm.java"]),
+    srcs = [
+        "AbstractAccountIT.java",
+        "TestRealm.java",
+    ],
     deps = [
         "//java/com/google/gerrit/acceptance:lib",
     ],
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/MessageIdGeneratorIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/MessageIdGeneratorIT.java
index 984b32d..a1a6755 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/MessageIdGeneratorIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/MessageIdGeneratorIT.java
@@ -26,8 +26,8 @@
 import com.google.gerrit.mail.MailMessage;
 import com.google.gerrit.server.mail.send.MessageIdGenerator;
 import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.inject.Inject;
 import java.time.Instant;
-import javax.inject.Inject;
 import org.eclipse.jgit.lib.Repository;
 import org.junit.Test;
 
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 09b4933..fee36d3 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -42,6 +42,7 @@
 import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_COMMIT;
 import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
 import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
+import static com.google.gerrit.extensions.client.ListChangesOption.DOWNLOAD_COMMANDS;
 import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
 import static com.google.gerrit.extensions.client.ListChangesOption.PUSH_CERTIFICATES;
 import static com.google.gerrit.extensions.client.ListChangesOption.REVIEWED;
@@ -155,6 +156,7 @@
 import com.google.gerrit.extensions.common.LabelInfo;
 import com.google.gerrit.extensions.common.RevisionInfo;
 import com.google.gerrit.extensions.common.TrackingIdInfo;
+import com.google.gerrit.extensions.config.DownloadScheme;
 import com.google.gerrit.extensions.events.AttentionSetListener;
 import com.google.gerrit.extensions.events.ChangeIndexedListener;
 import com.google.gerrit.extensions.registration.DynamicSet;
@@ -1234,7 +1236,7 @@
 
   @SuppressWarnings("FutureReturnValueIgnored")
   @Test
-  public void deleteChangeFromIndexNotifiesListenersWithoutProjectName() {
+  public void deleteChangeFromIndexNotifiesListeners() {
     TestChange change = changeOperations.newChange().createAndGet();
     TestDeleteForProjectListener deleteAllForProjectsListener = new TestDeleteForProjectListener();
     String projectName = "my-test-project";
@@ -1247,7 +1249,7 @@
     }
 
     assertThat(deleteAllForProjectsListener.getSingleChangeDeletedFiredCount()).isEqualTo(1);
-    assertThat(deleteAllForProjectsListener.getReceivedProjectName()).isNull();
+    assertThat(deleteAllForProjectsListener.getReceivedProjectName()).isEqualTo(projectName);
   }
 
   @Test
@@ -2968,6 +2970,30 @@
   }
 
   @Test
+  public void queryChangesOptionsBulk() throws Exception {
+    int numChanges = 20;
+    for (int i = 0; i < numChanges; i++) {
+      createChange();
+    }
+
+    try (Registration registration =
+        extensionRegistry.newRegistration().add(new TestDownloadScheme(), "test-scheme")) {
+      List<ChangeInfo> results =
+          gApi.changes()
+              .query("project:" + project.get() + " status:open")
+              .withOptions(CHANGE_ACTIONS, CURRENT_REVISION, CURRENT_ACTIONS, DOWNLOAD_COMMANDS)
+              .get();
+      assertThat(results).hasSize(numChanges);
+      for (ChangeInfo result : results) {
+        assertThat(result.actions).containsKey("abandon");
+        RevisionInfo rev = Iterables.getOnlyElement(result.revisions.values());
+        assertThat(rev.actions).isNotEmpty();
+        assertThat(rev.fetch).isNotEmpty();
+      }
+    }
+  }
+
+  @Test
   public void queryChangesOwnerWithDifferentUsers() throws Exception {
     PushOneCommit.Result r = createChange();
     assertThat(
@@ -5317,7 +5343,7 @@
     Change.Id changeId = change.getChange().getId();
     String metaRef = changeMetaRef(changeId);
 
-    indexer.delete(changeId);
+    indexer.delete(change.getChange().project(), changeId);
 
     try (Repository repo = repoManager.openRepository(project);
         ObjectInserter inserter = repo.newObjectInserter();
@@ -5444,8 +5470,9 @@
     public void onChangeIndexed(String projectName, int id) {}
 
     @Override
-    public void onChangeDeleted(int id) {
+    public void onChangeDeleted(String projectName, int id) {
       singleChangeDeletedFiredCount.incrementAndGet();
+      receivedProjectName = projectName;
     }
 
     @Override
@@ -5469,4 +5496,31 @@
   private void voteLabel(String changeId, String labelName, int score) throws RestApiException {
     gApi.changes().id(changeId).current().review(new ReviewInput().label(labelName, score));
   }
+
+  private static class TestDownloadScheme extends DownloadScheme {
+    @Override
+    public String getUrl(String project) {
+      return "http://foo/" + project;
+    }
+
+    @Override
+    public boolean isAuthRequired() {
+      return true;
+    }
+
+    @Override
+    public boolean isAuthSupported() {
+      return true;
+    }
+
+    @Override
+    public boolean isEnabled() {
+      return true;
+    }
+
+    @Override
+    public boolean isHidden() {
+      return false;
+    }
+  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
index 8fc4c65..44190f8 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
@@ -57,6 +57,7 @@
 import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
 import com.google.gerrit.extensions.api.changes.ReviewResult;
 import com.google.gerrit.extensions.api.changes.ReviewerInput;
+import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.client.Side;
@@ -69,6 +70,7 @@
 import com.google.gerrit.extensions.events.ReviewerDeletedListener;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.validators.CommentForValidation;
 import com.google.gerrit.extensions.validators.CommentValidationContext;
 import com.google.gerrit.extensions.validators.CommentValidator;
@@ -804,6 +806,30 @@
   }
 
   @Test
+  public void changeInfoStatusReflectsSynchronousSubmitByCommentAddedListener() throws Exception {
+    PushOneCommit.Result r = createChange();
+
+    CommentAddedListener submittingListener =
+        event -> {
+          try {
+            gApi.changes()
+                .id(event.getChange().project, event.getChange()._number)
+                .current()
+                .submit();
+          } catch (RestApiException e) {
+            throw new RuntimeException(e);
+          }
+        };
+
+    try (Registration registration = extensionRegistry.newRegistration().add(submittingListener)) {
+      ReviewResult reviewResult =
+          gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+
+      assertThat(reviewResult.changeInfo.status).isEqualTo(ChangeStatus.MERGED);
+    }
+  }
+
+  @Test
   public void submitRulesAreInvokedOnlyOnce() throws Exception {
     PushOneCommit.Result r = createChange();
 
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
index 8e2a3ba..62d1941 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.acceptance.api.change;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
@@ -34,6 +35,7 @@
 import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Permission;
 import com.google.gerrit.entities.Project;
@@ -516,6 +518,53 @@
   }
 
   @Test
+  public void revertDoesNotAddDeletedReviewersOrCcs() throws Exception {
+    PushOneCommit.Result r = createChange();
+
+    TestAccount reviewer =
+        accountCreator.create("reviewer-temp", "reviewer-temp@example.com", "Reviewer Temp", null);
+    TestAccount cc = accountCreator.create("cc-temp", "cc-temp@example.com", "CC Temp", null);
+    TestAccount reverter = accountCreator.admin2();
+
+    ReviewInput in = ReviewInput.approve();
+    in.reviewer(reviewer.email());
+    in.reviewer(cc.email(), ReviewerState.CC, true);
+    in.reviewer(reverter.email());
+
+    gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
+    gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+
+    // Delete reviewer and CC accounts
+    deleteAccount(reviewer.id());
+    deleteAccount(cc.id());
+
+    requestScopeOperations.setApiUser(reverter.id());
+    Map<ReviewerState, Collection<AccountInfo>> result =
+        gApi.changes().id(r.getChangeId()).revert().get().reviewers;
+
+    // The deleted reviewer and CC should not be added.
+    // Only the change owner (admin) should be added as reviewer (since they are not deleted).
+    // Reverter (admin2) is the new owner, so they are not in reviewers list.
+    if (result.containsKey(ReviewerState.REVIEWER)) {
+      ImmutableList<Integer> reviewers =
+          result.get(ReviewerState.REVIEWER).stream()
+              .map(a -> a._accountId)
+              .collect(toImmutableList());
+      assertThat(reviewers).containsExactly(admin.id().get());
+    }
+    if (result.containsKey(ReviewerState.CC)) {
+      ImmutableList<Integer> ccs =
+          result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toImmutableList());
+      assertThat(ccs).isEmpty();
+    }
+  }
+
+  private void deleteAccount(Account.Id id) throws Exception {
+    requestScopeOperations.setApiUser(id);
+    gApi.accounts().self().delete();
+  }
+
+  @Test
   @GerritConfig(name = "accounts.visibility", value = "SAME_GROUP")
   public void revertWithNonVisibleUsers() throws Exception {
     projectOperations
diff --git a/javatests/com/google/gerrit/acceptance/api/flow/BUILD b/javatests/com/google/gerrit/acceptance/api/flow/BUILD
index 24aea20..384e7e7 100644
--- a/javatests/com/google/gerrit/acceptance/api/flow/BUILD
+++ b/javatests/com/google/gerrit/acceptance/api/flow/BUILD
@@ -1,3 +1,4 @@
+load("@rules_java//java:defs.bzl", "java_library")
 load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
 
 acceptance_tests(
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/acceptance/api/revision/BUILD b/javatests/com/google/gerrit/acceptance/api/revision/BUILD
index 9c6584e..191832d 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/BUILD
+++ b/javatests/com/google/gerrit/acceptance/api/revision/BUILD
@@ -1,3 +1,4 @@
+load("@rules_java//java:defs.bzl", "java_library")
 load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
 
 [acceptance_tests(
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 4df5a0a..95eee42 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -1850,7 +1850,7 @@
           }
 
           @Override
-          public void onChangeDeleted(int id) {}
+          public void onChangeDeleted(String projectName, int id) {}
         };
 
     try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 4ccb80b..3e82712 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -1904,7 +1904,7 @@
     r = push.to("refs/for/master");
     r.assertOkStatus();
 
-    indexer.delete(r.getChange().getId());
+    indexer.delete(project, r.getChange().getId());
 
     assertPushRejected(
         pushHead(testRepo, "refs/for/master", false),
diff --git a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
index 90a5d94..0d6115b 100644
--- a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
@@ -422,17 +422,18 @@
         .add(allow(Permission.PUSH).ref("refs/*").group(REGISTERED_USERS))
         .update();
 
-    // We use "refs/main" instead of "refs/heads/main", because the latter only allows commits.
+    // We use "refs/other/main" instead of "refs/heads/main", because the latter only allows
+    // commits.
     {
       // An extra colon (:) makes it a tree reference
-      PushResult r = push(commit.getId().getName() + "::refs/main");
+      PushResult r = push(commit.getId().getName() + "::refs/other/main");
       RemoteRefUpdate refUpdate = r.getRemoteUpdates().stream().findFirst().get();
       assertThat(refUpdate.getStatus()).isEqualTo(Status.REJECTED_OTHER_REASON);
       assertThat(refUpdate.getMessage()).contains("is neither Commit or Tag");
     }
 
     {
-      PushResult r = push(commit.getTree().getId().getName() + ":refs/main");
+      PushResult r = push(commit.getTree().getId().getName() + ":refs/other/main");
       RemoteRefUpdate refUpdate = r.getRemoteUpdates().stream().findFirst().get();
       assertThat(refUpdate.getStatus()).isEqualTo(Status.REJECTED_OTHER_REASON);
       assertThat(refUpdate.getMessage()).contains("is neither Commit or Tag");
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index c3b1672..b8f805c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -1195,7 +1195,7 @@
     }
 
     @Override
-    public void onChangeDeleted(int id) {}
+    public void onChangeDeleted(String projectName, int id) {}
   }
 
   private static class TraceSubmitRule implements SubmitRule {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 9140a71..45e95db 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -97,6 +97,7 @@
 import com.google.gerrit.server.index.change.ChangeIndexer;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.project.testing.TestLabels;
+import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.restapi.change.Submit;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
@@ -1042,12 +1043,12 @@
     // Cherry-pick strategy does not query from index
     assume().that(getSubmitType()).isNotEqualTo(CHERRY_PICK);
     // retry on index
-    PushOneCommit.Result change = createChange();
+    ChangeData change = createChange().getChange();
 
     // Submit using full change Id to avoid using index.
-    String id = change.getChange().project() + "~" + change.getChange().getId().get();
+    String id = change.project() + "~" + change.getId().get();
     approve(id);
-    changeIndex.delete(change.getChange().getId());
+    changeIndex.delete(change.project(), change.getId());
 
     TestSubmitInput input = new TestSubmitInput();
 
@@ -1076,14 +1077,14 @@
       throws Throwable {
     // Cherry-pick strategy does not query from the index
     assume().that(getSubmitType()).isNotEqualTo(CHERRY_PICK);
-    PushOneCommit.Result change = createChange();
+    ChangeData change = createChange().getChange();
 
     // Submit using full change Id to avoid using index.
-    String id = change.getChange().project() + "~" + change.getChange().getId().get();
+    String id = change.project() + "~" + change.getId().get();
     approve(id);
 
     // Delete the change from the index, to ensure the use of the backfill mechanism
-    changeIndex.delete(change.getChange().getId());
+    changeIndex.delete(change.project(), change.getId());
 
     testMetricMaker.reset();
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java b/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
index ef95b61..416f8d2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
@@ -16,7 +16,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.entities.RefNames.changeMetaRef;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -24,9 +26,11 @@
 import com.google.gerrit.acceptance.ExtensionRegistry;
 import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
 import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Permission;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.index.IndexConfig;
@@ -35,31 +39,46 @@
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.server.index.change.IndexedChangeQuery;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeNumberVirtualIdAlgorithm;
 import com.google.gerrit.server.restapi.config.IndexChanges;
 import com.google.inject.Inject;
 import java.util.Optional;
 import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
 import org.junit.Test;
 
 public class IndexChangesIT extends AbstractDaemonTest {
+  private static final String TEST_CHANGE_NUM = "1";
+  private static final String TEST_CHANGE_ID = "I8350971af868ee34b17fc8703aa9ef40c03f5ec5";
+  private static final boolean PRESERVE_MISSING = false;
+  private static final boolean DELETE_MISSING = true;
 
   @Inject private ProjectOperations projectOperations;
   @Inject private ExtensionRegistry extensionRegistry;
   @Inject private ChangeIndexCollection changeIndexCollection;
   @Inject private IndexConfig indexConfig;
+  @Inject private ChangeNumberVirtualIdAlgorithm changeNumberVirtualIdAlgorithm;
 
   @Test
   public void indexRequestFromNonAdminRejected() throws Exception {
     ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(changeIndexedCounter)) {
-      String changeId = createChange().getChangeId();
-      IndexChanges.Input in = new IndexChanges.Input();
-      in.changes = ImmutableSet.of(changeId);
+      PushOneCommit.Result change = createChange();
       changeIndexedCounter.clear();
-      userRestSession.post("/config/server/index.changes", in).assertForbidden();
-      assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(0);
+      userRestSession
+          .post("/config/server/index.changes", indexChangesInput(change.getChange().getId()))
+          .assertForbidden();
+      assertThat(changeIndexedCounter.getCount(info(change.getChangeId()))).isEqualTo(0);
     }
   }
 
@@ -68,50 +87,76 @@
     ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(changeIndexedCounter)) {
-      String changeId = createChange().getChangeId();
-      IndexChanges.Input in = new IndexChanges.Input();
-      in.changes = ImmutableSet.of(changeId);
+      PushOneCommit.Result change = createChange();
       changeIndexedCounter.clear();
-      adminRestSession.post("/config/server/index.changes", in).assertOK();
-      assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
+      adminRestSession
+          .post("/config/server/index.changes", indexChangesInput(change.getChange().getId()))
+          .assertOK();
+      assertThat(changeIndexedCounter.getCount(info(change.getChangeId()))).isEqualTo(1);
     }
   }
 
   @Test
+  public void indexChangeNotInIndex() throws Exception {
+    PushOneCommit.Result change = createChange();
+    Change.Id changeId = change.getChange().getId();
+
+    assertThat(getChangeFromIndex(changeId)).isPresent();
+    indexer.delete(project, changeId);
+    assertThat(getChangeFromIndex(changeId)).isEmpty();
+
+    adminRestSession.post("/config/server/index.changes", indexChangesInput(changeId)).assertOK();
+    assertThat(getChangeFromIndex(changeId)).isPresent();
+  }
+
+  @Test
   public void indexNonVisibleChange() throws Exception {
     ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(changeIndexedCounter)) {
-      String changeId = createChange().getChangeId();
+      String changeId = projectAndChangeNumId(project, createChange().getChange().getId());
       ChangeInfo changeInfo = info(changeId);
       projectOperations
           .project(project)
           .forUpdate()
           .add(block(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
           .update();
-      IndexChanges.Input in = new IndexChanges.Input();
       changeIndexedCounter.clear();
-      in.changes = ImmutableSet.of(changeId);
-      adminRestSession.post("/config/server/index.changes", in).assertOK();
+      adminRestSession.post("/config/server/index.changes", indexChangesInput(changeId)).assertOK();
       assertThat(changeIndexedCounter.getCount(changeInfo)).isEqualTo(1);
     }
   }
 
   @Test
-  public void deleteMissingChangeFromIndexByNumericId() throws Exception {
-    PushOneCommit.Result result = createChange();
-    Change.Id changeId = result.getChange().getId();
+  public void indexChangeWithPlainNumericIdAccepted() throws Exception {
+    Change.Id changeId = createChange().getChange().getId();
+    adminRestSession
+        .post("/config/server/index.changes", indexChangesInput(String.valueOf(changeId.get())))
+        .assertOK();
+  }
 
-    assertThat(getChangeFromIndex(changeId)).isPresent();
-    deleteChangeFromNoteDbWithoutUpdatingIndex(changeId);
-    assertThat(getChangeFromIndex(changeId)).isPresent();
+  @Test
+  public void deleteMissingChangeFromIndexWithPlainNumericIdRejected() throws Exception {
+    adminRestSession
+        .post("/config/server/index.changes", indexChangesInput(TEST_CHANGE_NUM, DELETE_MISSING))
+        .assertBadRequest();
+  }
 
-    IndexChanges.Input in = new IndexChanges.Input();
-    in.changes = ImmutableSet.of(String.valueOf(changeId.get()));
-    in.deleteMissing = true;
-    adminRestSession.post("/config/server/index.changes", in).assertOK();
+  @Test
+  public void indexChangeWithTripletIdAccepted() throws Exception {
+    String changeId = createChange().getChangeId();
+    adminRestSession
+        .post("/config/server/index.changes", indexChangesInput(project + "~master~" + changeId))
+        .assertOK();
+  }
 
-    assertThat(getChangeFromIndex(changeId)).isEmpty();
+  @Test
+  public void deleteMissingChangeFromIndexWithTripletIdRejected() throws Exception {
+    adminRestSession
+        .post(
+            "/config/server/index.changes",
+            indexChangesInput(project + "~master~" + TEST_CHANGE_ID, DELETE_MISSING))
+        .assertBadRequest();
   }
 
   @Test
@@ -123,10 +168,11 @@
     deleteChangeFromNoteDbWithoutUpdatingIndex(changeId);
     assertThat(getChangeFromIndex(changeId)).isPresent();
 
-    IndexChanges.Input in = new IndexChanges.Input();
-    in.changes = ImmutableSet.of(project.get() + "~" + changeId.get());
-    in.deleteMissing = true;
-    adminRestSession.post("/config/server/index.changes", in).assertOK();
+    adminRestSession
+        .post(
+            "/config/server/index.changes",
+            indexChangesInput(projectAndChangeNumId(project, changeId), DELETE_MISSING))
+        .assertOK();
 
     assertThat(getChangeFromIndex(changeId)).isEmpty();
   }
@@ -138,18 +184,93 @@
         extensionRegistry.newRegistration().add(changeIndexedCounter)) {
       ImmutableSet.Builder<String> changeIds = ImmutableSet.builder();
       for (int i = 0; i < 10; i++) {
-        changeIds.add(createChange().getChangeId());
+        changeIds.add(projectAndChangeNumId(project, createChange().getChange().getId()));
       }
-      IndexChanges.Input in = new IndexChanges.Input();
-      in.changes = changeIds.build();
+      IndexChanges.Input in = new IndexChanges.Input(changeIds.build(), PRESERVE_MISSING);
       changeIndexedCounter.clear();
       adminRestSession.post("/config/server/index.changes", in).assertOK();
-      for (String changeId : in.changes) {
+      for (String changeId : in.changes()) {
         assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
       }
     }
   }
 
+  @Test
+  @GerritConfig(name = "gerrit.importedServerId", value = "imported-server-id")
+  public void deleteMissingImportedChangeFromIndex() throws Exception {
+    PushOneCommit.Result result = createImportedChange();
+    Change.Id changeId = result.getChange().getId();
+    Change.Id virtualId =
+        changeNumberVirtualIdAlgorithm.apply(() -> "imported-server-id", changeId);
+
+    assertThat(getChangeFromIndex(virtualId)).isPresent();
+    deleteChangeFromNoteDbWithoutUpdatingIndex(changeId);
+    assertThat(getChangeFromIndex(virtualId)).isPresent();
+
+    adminRestSession
+        .post("/config/server/index.changes", indexChangesInput(changeId, PRESERVE_MISSING))
+        .assertOK();
+    assertThat(getChangeFromIndex(virtualId)).isPresent();
+
+    adminRestSession
+        .post("/config/server/index.changes", indexChangesInput(changeId, DELETE_MISSING))
+        .assertOK();
+    assertThat(getChangeFromIndex(virtualId)).isEmpty();
+  }
+
+  private IndexChanges.Input indexChangesInput(String changeId) {
+    return new IndexChanges.Input(ImmutableSet.of(changeId), PRESERVE_MISSING);
+  }
+
+  private IndexChanges.Input indexChangesInput(Change.Id changeId) {
+    return indexChangesInput(projectAndChangeNumId(project, changeId));
+  }
+
+  private IndexChanges.Input indexChangesInput(String changeId, boolean deleteMissing) {
+    return new IndexChanges.Input(ImmutableSet.of(changeId), deleteMissing);
+  }
+
+  private IndexChanges.Input indexChangesInput(Change.Id changeId, boolean deleteMissing) {
+    return indexChangesInput(projectAndChangeNumId(project, changeId), deleteMissing);
+  }
+
+  private PushOneCommit.Result createImportedChange() throws Exception {
+    PushOneCommit.Result change = createChange();
+    Change.Id changeId = change.getChange().getId();
+    String metaRef = changeMetaRef(changeId);
+
+    try (Repository repo = repoManager.openRepository(project);
+        ObjectInserter inserter = repo.newObjectInserter();
+        ObjectReader reader = repo.newObjectReader();
+        RevWalk revWalk = new RevWalk(reader);
+        var ignored = openTestRefUpdateContext()) {
+
+      Ref ref = repo.getRefDatabase().exactRef(metaRef);
+      RevCommit tip = revWalk.parseCommit(ref.getObjectId());
+
+      CommitBuilder commit = new CommitBuilder();
+      commit.setTreeId(tip.getTree());
+      commit.setAuthor(
+          new PersonIdent("Gerrit User " + admin.id(), admin.id() + "@imported-server-id"));
+      commit.setCommitter(new PersonIdent("Gerrit Code Review", admin.email()));
+      commit.setMessage(tip.getFullMessage());
+
+      ObjectId commitId = inserter.insert(commit);
+      inserter.flush();
+
+      RefUpdate refUpdate = repo.updateRef(metaRef);
+      refUpdate.setNewObjectId(commitId);
+      refUpdate.forceUpdate();
+    }
+
+    // Re-index after rewriting the meta-ref so the index reflects the imported serverId,
+    // ensuring the virtualId in the index matches what the API will compute at delete time.
+    indexer.delete(project, changeId);
+    indexer.index(project, changeId);
+
+    return change;
+  }
+
   private void deleteChangeFromNoteDbWithoutUpdatingIndex(Change.Id changeId) throws Exception {
     try (Repository repo = repoManager.openRepository(project);
         TestRepository<Repository> testRepo = new TestRepository<>(repo)) {
@@ -162,4 +283,8 @@
     QueryOptions opts = IndexedChangeQuery.createOptions(indexConfig, 0, 1, ImmutableSet.of());
     return idx.get(changeId, opts);
   }
+
+  String projectAndChangeNumId(Project.NameKey project, Change.Id changeNum) {
+    return project + "~" + changeNum;
+  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/ListCachesIT.java b/javatests/com/google/gerrit/acceptance/rest/config/ListCachesIT.java
index a987225..7f2e39e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/ListCachesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/ListCachesIT.java
@@ -21,6 +21,7 @@
 import com.google.common.io.BaseEncoding;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.UseLocalDisk;
 import com.google.gerrit.extensions.common.CacheInfo;
 import com.google.gson.reflect.TypeToken;
 import java.util.Arrays;
@@ -88,4 +89,36 @@
   public void listCaches_BadRequest() throws Exception {
     adminRestSession.get("/config/server/caches/?format=NONSENSE").assertBadRequest();
   }
+
+  @Test
+  public void listCaches_withoutIncludeDiskStats_memCacheUnaffected() throws Exception {
+    RestResponse r = adminRestSession.get("/config/server/caches/");
+    r.assertOK();
+    Map<String, CacheInfo> result =
+        newGson().fromJson(r.getReader(), new TypeToken<Map<String, CacheInfo>>() {}.getType());
+
+    assertThat(result).containsKey("accounts");
+    CacheInfo accountsCacheInfo = result.get("accounts");
+    assertThat(accountsCacheInfo.type).isEqualTo(CacheInfo.CacheType.MEM);
+    assertThat(accountsCacheInfo.entries.mem).isAtLeast(1L);
+    assertThat(accountsCacheInfo.hitRatio.mem).isAtLeast(0);
+
+    assertThat(accountsCacheInfo.entries.disk).isNull();
+    assertThat(accountsCacheInfo.hitRatio.disk).isNull();
+  }
+
+  @Test
+  @UseLocalDisk
+  public void listCaches_withIncludeDiskStats_diskCacheHasDiskStats() throws Exception {
+    RestResponse r = adminRestSession.get("/config/server/caches/?include-diskstats=true");
+    r.assertOK();
+    Map<String, CacheInfo> result =
+        newGson().fromJson(r.getReader(), new TypeToken<Map<String, CacheInfo>>() {}.getType());
+
+    assertThat(result).containsKey("accounts");
+    CacheInfo accountsInfo = result.get("accounts");
+    assertThat(accountsInfo.type).isEqualTo(CacheInfo.CacheType.DISK);
+    assertThat(accountsInfo.entries.mem).isNotNull();
+    assertThat(accountsInfo.entries.disk).isNotNull();
+  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/server/change/BUILD b/javatests/com/google/gerrit/acceptance/server/change/BUILD
index 56c27a0..3f9767f 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/BUILD
+++ b/javatests/com/google/gerrit/acceptance/server/change/BUILD
@@ -1,3 +1,4 @@
+load("@rules_java//java:defs.bzl", "java_library")
 load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
 
 acceptance_tests(
diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java b/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java
index 0e13732..8cf8206 100644
--- a/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java
@@ -51,10 +51,10 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import com.google.inject.Module;
 import java.util.Collection;
 import java.util.stream.StreamSupport;
-import javax.inject.Inject;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.junit.Before;
diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/GroupBackedUserPermissionIT.java b/javatests/com/google/gerrit/acceptance/server/permissions/GroupBackedUserPermissionIT.java
index 4ad1caa..f0dd3a7 100644
--- a/javatests/com/google/gerrit/acceptance/server/permissions/GroupBackedUserPermissionIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/permissions/GroupBackedUserPermissionIT.java
@@ -42,8 +42,8 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.query.change.GroupBackedUser;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import com.google.inject.Module;
-import javax.inject.Inject;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.junit.Before;
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ProjectCacheIT.java b/javatests/com/google/gerrit/acceptance/server/project/ProjectCacheIT.java
index d10d559..2418172 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ProjectCacheIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ProjectCacheIT.java
@@ -26,9 +26,9 @@
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.project.ProjectCacheImpl;
 import com.google.gerrit.server.project.ProjectConfig;
+import com.google.inject.Inject;
 import com.google.inject.name.Named;
 import java.util.Optional;
-import javax.inject.Inject;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.util.FS;
 import org.junit.Test;
diff --git a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
index 945c0b9..52a380f 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
@@ -1014,6 +1014,29 @@
     assertThat(srResult.errorMessage().get()).isEqualTo("Invalid content pattern.");
   }
 
+  @Test
+  public void invalidPathRegex_returnsErrorExpressionResult() throws Exception {
+    SubmitRequirementExpression exp = SubmitRequirementExpression.create("path:\"^foo[\"");
+
+    SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, changeData);
+    assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.ERROR);
+    assertThat(srResult.errorMessage().get()).contains("unexpected end-of-string");
+  }
+
+  @Test
+  public void invalidPathRegex_returnsErrorRequirementResult() throws Exception {
+    SubmitRequirement sr =
+        createSubmitRequirement(
+            /* applicabilityExpr= */ null,
+            /* submittabilityExpr= */ "path:\"^foo[\"",
+            /* overrideExpr= */ null);
+
+    SubmitRequirementResult srResult = evaluator.evaluateRequirement(sr, changeData);
+    assertThat(srResult.status()).isEqualTo(SubmitRequirementResult.Status.ERROR);
+    assertThat(srResult.submittabilityExpressionResult().get().errorMessage().get())
+        .contains("unexpected end-of-string");
+  }
+
   private void voteLabel(String changeId, String labelName, int score) throws RestApiException {
     gApi.changes().id(changeId).current().review(new ReviewInput().label(labelName, score));
   }
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/index/DefaultIndexBindingIT.java b/javatests/com/google/gerrit/acceptance/testsuite/index/DefaultIndexBindingIT.java
index f6e5fb3..107d241 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/index/DefaultIndexBindingIT.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/index/DefaultIndexBindingIT.java
@@ -23,7 +23,7 @@
 import com.google.gerrit.index.testing.AbstractFakeIndex;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.testing.SystemPropertiesTestRule;
-import javax.inject.Inject;
+import com.google.inject.Inject;
 import org.junit.ClassRule;
 import org.junit.Test;
 
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/index/FakeIndexBindingIT.java b/javatests/com/google/gerrit/acceptance/testsuite/index/FakeIndexBindingIT.java
index acb2e5a..d7afb69 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/index/FakeIndexBindingIT.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/index/FakeIndexBindingIT.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.index.testing.AbstractFakeIndex;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.testing.SystemPropertiesTestRule;
-import javax.inject.Inject;
+import com.google.inject.Inject;
 import org.junit.ClassRule;
 import org.junit.Test;
 
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/index/LuceneIndexBindingIT.java b/javatests/com/google/gerrit/acceptance/testsuite/index/LuceneIndexBindingIT.java
index 5dd6f01..36ea5d1 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/index/LuceneIndexBindingIT.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/index/LuceneIndexBindingIT.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.lucene.LuceneChangeIndex;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.testing.SystemPropertiesTestRule;
-import javax.inject.Inject;
+import com.google.inject.Inject;
 import org.junit.ClassRule;
 import org.junit.Test;
 
diff --git a/javatests/com/google/gerrit/httpd/BUILD b/javatests/com/google/gerrit/httpd/BUILD
index 2b93045..b8258bb 100644
--- a/javatests/com/google/gerrit/httpd/BUILD
+++ b/javatests/com/google/gerrit/httpd/BUILD
@@ -8,7 +8,9 @@
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/httpd",
         "//java/com/google/gerrit/server",
+        "//java/com/google/gerrit/server/restapi",
         "//java/com/google/gerrit/testing:gerrit-junit",
+        "//java/com/google/gerrit/testing:gerrit-test-util",
         "//javatests/com/google/gerrit/util/http/testutil",
         "//lib:gson",
         "//lib:guava",
diff --git a/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java b/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java
index f012ee3..ab464e8 100644
--- a/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java
+++ b/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java
@@ -15,11 +15,45 @@
 package com.google.gerrit.httpd;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.httpd.RemoteUserUtil.PROXY_REMOTE_ADDRESS_ATTR;
 import static com.google.gerrit.httpd.RemoteUserUtil.extractUsername;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.mockito.Mockito.when;
 
+import com.google.common.base.Suppliers;
+import com.google.common.net.HttpHeaders;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.util.http.testutil.FakeHttpServletRequest;
+import com.google.inject.ProvisionException;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Set;
+import java.util.function.Supplier;
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
 
+@RunWith(MockitoJUnitRunner.class)
 public class RemoteUserUtilTest {
+  private static final String CUSTOM_LOGIN_HEADER = "MY_HEADER";
+  private static final String EXPECTED_USER = "user";
+  private static final String BASIC_AUTHENTICATION_USER_HEADER =
+      "Basic "
+          + Base64.getEncoder()
+              .encodeToString((EXPECTED_USER + ":pass").getBytes(StandardCharsets.UTF_8));
+
+  private Supplier<RemoteUserUtil> remoteUserUtil;
+
+  @Mock AuthConfig authConfigMock;
+
+  @Before
+  public void setup() {
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of());
+    remoteUserUtil = Suppliers.memoize(() -> new RemoteUserUtil(authConfigMock));
+  }
+
   @Test
   public void testExtractUsername() {
     assertThat(extractUsername(null)).isNull();
@@ -27,4 +61,165 @@
     assertThat(extractUsername("Basic dXNlcjpwYXNzd29yZA==")).isEqualTo("user");
     assertThat(extractUsername("Digest username=\"user\", realm=\"test\"")).isEqualTo("user");
   }
+
+  @Test
+  public void testExtractUserFromRequestWithCustomHeaderAllowedByDefault() throws Exception {
+    FakeHttpServletRequest fakeRequest = new FakeHttpServletRequest();
+    fakeRequest.addHeader(CUSTOM_LOGIN_HEADER, EXPECTED_USER);
+    assertThat(remoteUserUtil.get().getRemoteUser(fakeRequest, CUSTOM_LOGIN_HEADER))
+        .isEqualTo(EXPECTED_USER);
+  }
+
+  @Test
+  public void testExtractUserFromRequestWithAuthenticationHeaderAllowedByDefault()
+      throws Exception {
+    FakeHttpServletRequest fakeRequest = new FakeHttpServletRequest();
+    fakeRequest.addHeader(HttpHeaders.AUTHORIZATION, BASIC_AUTHENTICATION_USER_HEADER);
+    assertThat(remoteUserUtil.get().getRemoteUser(fakeRequest, HttpHeaders.AUTHORIZATION))
+        .isEqualTo(EXPECTED_USER);
+  }
+
+  @Test
+  public void testExtractUserFromRequestWithCustomHeaderAllowedUsingProxyExactIPv4Matching()
+      throws Exception {
+    String clientIP = "192.168.1.2";
+    String proxyId = "80.78.1.3";
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of(proxyId + "/32"));
+    FakeHttpServletRequest fakeRequest = newFakeHttpRequest(clientIP, EXPECTED_USER);
+    fakeRequest.setAttribute(PROXY_REMOTE_ADDRESS_ATTR, proxyId);
+    assertThat(remoteUserUtil.get().getRemoteUser(fakeRequest, CUSTOM_LOGIN_HEADER))
+        .isEqualTo(EXPECTED_USER);
+  }
+
+  @Test
+  public void testExtractUserFromRequestWithCustomHeaderAllowedWithExactIPv4Matching()
+      throws Exception {
+    String remoteIp = "192.168.1.2";
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of(remoteIp + "/32"));
+    assertThat(
+            remoteUserUtil
+                .get()
+                .getRemoteUser(newFakeHttpRequest(remoteIp, EXPECTED_USER), CUSTOM_LOGIN_HEADER))
+        .isEqualTo(EXPECTED_USER);
+  }
+
+  @Test
+  public void testExtractUserFromRequestWithAuthenticationHeaderAllowedWithExactIPv4Matching()
+      throws Exception {
+    String remoteIp = "192.168.1.2";
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of(remoteIp + "/32"));
+    assertThat(
+            remoteUserUtil
+                .get()
+                .getRemoteUser(
+                    newFakeAuthHttpRequest(remoteIp, BASIC_AUTHENTICATION_USER_HEADER),
+                    HttpHeaders.AUTHORIZATION))
+        .isEqualTo(EXPECTED_USER);
+  }
+
+  @Test
+  public void testExtractUserFromRequestWithCustomHeaderAllowedWithExactIPv4InAcceptedRange()
+      throws Exception {
+    when(authConfigMock.getTrustedProxyNetworks())
+        .thenReturn(Set.of("10.16.0.0/16", "192.168.1.0/24", "8.8.8.8/32"));
+    assertThat(
+            remoteUserUtil
+                .get()
+                .getRemoteUser(newFakeHttpRequest("10.16.5.1", EXPECTED_USER), CUSTOM_LOGIN_HEADER))
+        .isEqualTo(EXPECTED_USER);
+  }
+
+  @Test
+  public void
+      testExtractUserFromRequestWithAuthenticationHeaderAllowedWithExactIPv4InAcceptedRange()
+          throws Exception {
+    when(authConfigMock.getTrustedProxyNetworks())
+        .thenReturn(Set.of("10.16.0.0/16", "192.168.1.0/24", "8.8.8.8/32"));
+    assertThat(
+            remoteUserUtil
+                .get()
+                .getRemoteUser(
+                    newFakeAuthHttpRequest("10.16.5.1", BASIC_AUTHENTICATION_USER_HEADER),
+                    HttpHeaders.AUTHORIZATION))
+        .isEqualTo(EXPECTED_USER);
+  }
+
+  @Test
+  public void testExtractUserFromRequestWithCustomHeaderRejectedWithNonMatchingExactIPv4()
+      throws Exception {
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of("2.2.2.2/32"));
+    assertThat(
+            remoteUserUtil
+                .get()
+                .getRemoteUser(newFakeHttpRequest("1.1.1.1", EXPECTED_USER), CUSTOM_LOGIN_HEADER))
+        .isNull();
+  }
+
+  @Test
+  public void testExtractUserFromRequestWithAuthenticationHeaderRejectedWithNonMatchingExactIPv4()
+      throws Exception {
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of("2.2.2.2/32"));
+    assertThat(
+            remoteUserUtil
+                .get()
+                .getRemoteUser(
+                    newFakeAuthHttpRequest("1.1.1.1", BASIC_AUTHENTICATION_USER_HEADER),
+                    HttpHeaders.AUTHORIZATION))
+        .isNull();
+  }
+
+  @Test
+  public void testExtractUserFromRequestRejectedWithIPv6() throws Exception {
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of("255.255.255.255/32"));
+    assertThat(
+            remoteUserUtil
+                .get()
+                .getRemoteUser(
+                    newFakeHttpRequest("2001:0db8:85a3:0000:0000:8a2e:0370:7334", "user"),
+                    CUSTOM_LOGIN_HEADER))
+        .isNull();
+  }
+
+  @Test
+  public void testFailWhenUsingAnInvalidProxyNetworkCIDR() throws Exception {
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of("invalid-network"));
+    assertThrows(ProvisionException.class, () -> remoteUserUtil.get());
+  }
+
+  @Test
+  public void testFailWhenUsingSingleIPAsProxyNetworkCIDR() throws Exception {
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of("192.168.0.1"));
+    assertThrows(ProvisionException.class, () -> remoteUserUtil.get());
+  }
+
+  @Test
+  public void testFailWhenUsingIPv6AsProxyNetworkCIDR() throws Exception {
+    when(authConfigMock.getTrustedProxyNetworks()).thenReturn(Set.of("2000::/3"));
+    assertThrows(ProvisionException.class, () -> remoteUserUtil.get());
+  }
+
+  private static FakeHttpServletRequest newFakeHttpRequest(String remoteIp, String expectedUser) {
+    FakeHttpServletRequest fakeRequest =
+        new FakeHttpServletRequest() {
+          @Override
+          public String getRemoteAddr() {
+            return remoteIp;
+          }
+        };
+    fakeRequest.addHeader(CUSTOM_LOGIN_HEADER, expectedUser);
+    return fakeRequest;
+  }
+
+  private static FakeHttpServletRequest newFakeAuthHttpRequest(
+      String remoteIp, String basicAuthHeader) {
+    FakeHttpServletRequest fakeRequest =
+        new FakeHttpServletRequest() {
+          @Override
+          public String getRemoteAddr() {
+            return remoteIp;
+          }
+        };
+    fakeRequest.addHeader(HttpHeaders.AUTHORIZATION, basicAuthHeader);
+    return fakeRequest;
+  }
 }
diff --git a/javatests/com/google/gerrit/httpd/auth/container/HttpAuthFilterTest.java b/javatests/com/google/gerrit/httpd/auth/container/HttpAuthFilterTest.java
index a5f8349..32ae2ca 100644
--- a/javatests/com/google/gerrit/httpd/auth/container/HttpAuthFilterTest.java
+++ b/javatests/com/google/gerrit/httpd/auth/container/HttpAuthFilterTest.java
@@ -18,6 +18,7 @@
 import static org.mockito.Mockito.doReturn;
 
 import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.httpd.RemoteUserUtil;
 import com.google.gerrit.httpd.WebSession;
 import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
 import com.google.gerrit.server.config.AuthConfig;
@@ -37,13 +38,14 @@
   @Mock private DynamicItem<WebSession> webSession;
   @Mock private ExternalIdKeyFactory externalIdKeyFactory;
   @Mock private AuthConfig authConfig;
+  @Mock private RemoteUserUtil remoteUserUtil;
 
   @Test
   public void getRemoteDisplaynameShouldReturnDisplaynameHeaderWhenHeaderIsConfiguredAndSet()
       throws IOException {
     doReturn(DISPLAYNAME_HEADER).when(authConfig).getHttpDisplaynameHeader();
     HttpAuthFilter httpAuthFilter =
-        new HttpAuthFilter(webSession, authConfig, externalIdKeyFactory);
+        new HttpAuthFilter(webSession, authConfig, externalIdKeyFactory, remoteUserUtil);
 
     FakeHttpServletRequest req = new FakeHttpServletRequest();
     req.addHeader(DISPLAYNAME_HEADER, DISPLAYNAME);
@@ -56,7 +58,7 @@
       throws IOException {
     doReturn(DISPLAYNAME_HEADER).when(authConfig).getHttpDisplaynameHeader();
     HttpAuthFilter httpAuthFilter =
-        new HttpAuthFilter(webSession, authConfig, externalIdKeyFactory);
+        new HttpAuthFilter(webSession, authConfig, externalIdKeyFactory, remoteUserUtil);
 
     FakeHttpServletRequest req = new FakeHttpServletRequest();
 
@@ -68,7 +70,7 @@
       throws IOException {
     doReturn(DISPLAYNAME_HEADER).when(authConfig).getHttpDisplaynameHeader();
     HttpAuthFilter httpAuthFilter =
-        new HttpAuthFilter(webSession, authConfig, externalIdKeyFactory);
+        new HttpAuthFilter(webSession, authConfig, externalIdKeyFactory, remoteUserUtil);
 
     FakeHttpServletRequest req = new FakeHttpServletRequest();
     req.addHeader(DISPLAYNAME_HEADER, "");
diff --git a/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java b/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
index 925f9b2..46cd3f6b 100644
--- a/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
+++ b/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
@@ -146,9 +146,7 @@
     String requestedPath = "/c/project/+/123/4..6";
     assertThat(IndexHtmlUtil.computeBasePatchNum(requestedPath)).isEqualTo(4);
 
-    assertThat(
-            dynamicTemplateData(
-                gerritApi, requestedPath, "", serverApi.getInfo(), serverApi.getVersion()))
+    assertThat(dynamicTemplateData(gerritApi, requestedPath, ""))
         .containsAtLeast(
             "defaultChangeDetailHex", "8896394",
             "changeRequestsPath", "changes/project~123");
@@ -176,9 +174,7 @@
     String requestedPath = "/c/project/+/123";
     assertThat(IndexHtmlUtil.computeBasePatchNum(requestedPath)).isEqualTo(0);
 
-    assertThat(
-            dynamicTemplateData(
-                gerritApi, requestedPath, "", serverApi.getInfo(), serverApi.getVersion()))
+    assertThat(dynamicTemplateData(gerritApi, requestedPath, ""))
         .containsAtLeast(
             "defaultChangeDetailHex", "896394",
             "changeRequestsPath", "changes/project~123");
@@ -206,9 +202,7 @@
     String requestedPath = "/c/project/+/123";
     assertThat(IndexHtmlUtil.computeBasePatchNum(requestedPath)).isEqualTo(0);
 
-    assertThat(
-            dynamicTemplateData(
-                gerritApi, requestedPath, "", serverApi.getInfo(), serverApi.getVersion()))
+    assertThat(dynamicTemplateData(gerritApi, requestedPath, ""))
         .containsAtLeast(
             "defaultChangeDetailHex", "896394",
             "submitRequirementsHex", "1900000",
diff --git a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
index 9becbd6..3b7f439 100644
--- a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
+++ b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.httpd.raw;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.lenient;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -22,22 +23,173 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.accounts.AccountApi;
 import com.google.gerrit.extensions.api.accounts.Accounts;
 import com.google.gerrit.extensions.api.config.Config;
 import com.google.gerrit.extensions.api.config.Server;
 import com.google.gerrit.extensions.common.ServerInfo;
+import com.google.gerrit.extensions.config.CloneCommand;
+import com.google.gerrit.extensions.config.DownloadCommand;
+import com.google.gerrit.extensions.config.DownloadScheme;
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.GroupMembership;
+import com.google.gerrit.server.account.ListGroupMembership;
+import com.google.gerrit.server.config.ConfigResource;
 import com.google.gerrit.server.experiments.ConfigExperimentFeatures;
 import com.google.gerrit.server.experiments.ExperimentFeatures;
 import com.google.gerrit.server.experiments.ExperimentFeaturesConstants;
+import com.google.gerrit.server.restapi.config.GetServerInfo;
+import com.google.gerrit.server.util.ManualRequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
+import com.google.gerrit.testing.InMemoryModule;
 import com.google.gerrit.util.http.testutil.FakeHttpServletRequest;
 import com.google.gerrit.util.http.testutil.FakeHttpServletResponse;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
 
+@RunWith(MockitoJUnitRunner.class)
 public class IndexServletTest {
+  private static final String FAKE_USER1 = "user1";
+  private static final String FAKE_USER2 = "user2";
+  private static final FakeCurrentUser FAKE_CURRENT_USER1 = new FakeCurrentUser(FAKE_USER1);
+  private static final FakeCurrentUser FAKE_CURRENT_USER2 = new FakeCurrentUser(FAKE_USER2);
+
+  @Mock Server serverApi;
+
+  @Mock GerritApi gerritApi;
+
+  @Mock Config configApi;
+
+  @Mock Accounts accountsApi;
+
+  @Inject ThreadLocalRequestContext threadLocalRequestContext;
+
+  @Inject GetServerInfo getServerInfo;
+
+  private static class FakeCurrentUser extends CurrentUser {
+    private static final GroupMembership groups = new ListGroupMembership(List.of());
+
+    private final String username;
+
+    FakeCurrentUser(String name) {
+      username = name;
+    }
+
+    @Override
+    public GroupMembership getEffectiveGroups() {
+      return groups;
+    }
+
+    @Override
+    public Object getCacheKey() {
+      return username;
+    }
+
+    @Override
+    public Optional<String> getUserName() {
+      return Optional.ofNullable(username);
+    }
+
+    @Override
+    public boolean isIdentifiedUser() {
+      return true;
+    }
+  }
+
+  private static class FakeDownloadScheme extends DownloadScheme {
+    @Override
+    public String getUrl(String project) {
+      return "some-protocol://" + project;
+    }
+
+    @Override
+    public boolean isAuthRequired() {
+      return true;
+    }
+
+    @Override
+    public boolean isAuthSupported() {
+      return true;
+    }
+
+    @Override
+    public boolean isEnabled() {
+      return true;
+    }
+
+    @Override
+    public boolean isHidden() {
+      return false;
+    }
+  }
+
+  private static class FakeDownloadCommand extends DownloadCommand {
+
+    @Inject Provider<CurrentUser> currentUserProvider;
+
+    @Override
+    public String getCommand(DownloadScheme scheme, String project, String ref) {
+      CurrentUser currentUser = currentUserProvider.get();
+      String url = scheme.getUrl(project);
+      if (currentUser.isIdentifiedUser()) {
+        url = url + "/" + currentUser.getUserName().orElseThrow();
+      }
+      return String.format("fake git fetch %s %s", url, ref);
+    }
+  }
+
+  private static class FakeCloneCommand extends CloneCommand {
+
+    @Inject Provider<CurrentUser> currentUserProvider;
+
+    @Override
+    public String getCommand(DownloadScheme scheme, String project) {
+      CurrentUser currentUser = currentUserProvider.get();
+      String url = scheme.getUrl(project);
+      if (currentUser.isIdentifiedUser()) {
+        url = url + "/" + currentUser.getUserName().orElseThrow();
+      }
+      return String.format("fake git clone %s", url);
+    }
+  }
+
+  @Before
+  public void setup() throws RestApiException {
+    Injector injector =
+        Guice.createInjector(
+            new InMemoryModule() {
+              @Override
+              protected void configure() {
+                configure(false);
+                bind(GerritApi.class).toInstance(gerritApi);
+                bind(DownloadScheme.class)
+                    .annotatedWith(Exports.named("testscheme"))
+                    .to(FakeDownloadScheme.class);
+                bind(DownloadCommand.class)
+                    .annotatedWith(Exports.named("checkout"))
+                    .to(FakeDownloadCommand.class);
+                bind(CloneCommand.class)
+                    .annotatedWith(Exports.named("clone"))
+                    .to(FakeCloneCommand.class);
+              }
+            });
+    injector.injectMembers(this);
+  }
 
   @Test
   public void renderTemplate() throws Exception {
@@ -70,17 +222,9 @@
     serverConfig.setStringList(
         "experiments", null, "disabled", ImmutableList.of("DisabledFeature"));
     ExperimentFeatures experimentFeatures = new ConfigExperimentFeatures(serverConfig);
-    com.google.common.cache.Cache<
-            String, com.google.gerrit.server.config.ServerConfigCacheImpl.ServerConfigData>
-        serverConfigCache = com.google.common.cache.CacheBuilder.newBuilder().build();
     IndexServlet servlet =
         new IndexServlet(
-            testCanonicalUrl,
-            testCdnPath,
-            testFaviconURL,
-            gerritApi,
-            experimentFeatures,
-            serverConfigCache);
+            testCanonicalUrl, testCdnPath, testFaviconURL, gerritApi, experimentFeatures);
 
     FakeHttpServletResponse response = new FakeHttpServletResponse();
 
@@ -117,43 +261,75 @@
   }
 
   @Test
-  public void serverConfigIsCached() throws Exception {
-    Accounts accountsApi = mock(Accounts.class);
-    when(accountsApi.self()).thenThrow(new AuthException("user needs to be authenticated"));
+  public void downloadInfoIsTheSameForTwoAnonymousUsers() throws Exception {
+    try (ManualRequestContext ctx = mockGerritApi(new AnonymousUser())) {
 
-    Server serverApi = mock(Server.class);
-    when(serverApi.getVersion()).thenReturn("123");
-    when(serverApi.topMenus()).thenReturn(ImmutableList.of(), ImmutableList.of());
-    ServerInfo serverInfo = new ServerInfo();
-    serverInfo.defaultTheme = "my-default-theme";
-    when(serverApi.getInfo()).thenReturn(serverInfo);
+      IndexServlet servlet =
+          new IndexServlet(
+              null,
+              null,
+              null,
+              gerritApi,
+              new ConfigExperimentFeatures(new org.eclipse.jgit.lib.Config()));
 
-    Config configApi = mock(Config.class);
-    when(configApi.server()).thenReturn(serverApi);
+      FakeHttpServletResponse indexHtmlResponse1 = new FakeHttpServletResponse();
+      servlet.doGet(new FakeHttpServletRequest(), indexHtmlResponse1);
+      FakeHttpServletResponse indexHtmlResponse2 = new FakeHttpServletResponse();
+      servlet.doGet(new FakeHttpServletRequest(), indexHtmlResponse2);
 
-    GerritApi gerritApi = mock(GerritApi.class);
-    when(gerritApi.accounts()).thenReturn(accountsApi);
-    when(gerritApi.config()).thenReturn(configApi);
+      assertThat(indexHtmlResponse1.getActualBodyString())
+          .isEqualTo(indexHtmlResponse2.getActualBodyString());
+    }
+  }
 
-    com.google.common.cache.Cache<
-            String, com.google.gerrit.server.config.ServerConfigCacheImpl.ServerConfigData>
-        serverConfigCache = com.google.common.cache.CacheBuilder.newBuilder().build();
+  @Test
+  public void downloadInfoIsNotCachedForIdentifiedUsers() throws Exception {
+    FakeHttpServletResponse indexHtmlResponse1 = new FakeHttpServletResponse();
+    FakeHttpServletResponse indexHtmlResponse2 = new FakeHttpServletResponse();
 
     IndexServlet servlet =
         new IndexServlet(
-            "foo-url",
-            "bar-cdn",
-            "zaz-url",
+            null,
+            null,
+            null,
             gerritApi,
-            new ConfigExperimentFeatures(new org.eclipse.jgit.lib.Config()),
-            serverConfigCache);
+            new ConfigExperimentFeatures(new org.eclipse.jgit.lib.Config()));
 
-    servlet.doGet(new FakeHttpServletRequest(), new FakeHttpServletResponse());
-    servlet.doGet(new FakeHttpServletRequest(), new FakeHttpServletResponse());
+    try (ManualRequestContext ctx = mockGerritApi(FAKE_CURRENT_USER1)) {
+      servlet.doGet(new FakeHttpServletRequest(), indexHtmlResponse1);
+    }
 
-    verify(serverApi, times(1)).getInfo();
-    verify(serverApi, times(1)).getVersion();
-    // topMenus is no longer cached, so it should be called dynamically per user request
-    verify(serverApi, times(2)).topMenus();
+    try (ManualRequestContext ctx = mockGerritApi(FAKE_CURRENT_USER2)) {
+      servlet.doGet(new FakeHttpServletRequest(), indexHtmlResponse2);
+    }
+
+    String indexBodyUser1 = indexHtmlResponse1.getActualBodyString();
+    String indexBodyUser2 = indexHtmlResponse2.getActualBodyString();
+    assertThat(indexBodyUser1).contains(FAKE_USER1);
+    assertThat(indexBodyUser2).contains(FAKE_USER2);
+    assertThat(indexBodyUser1).isNotEqualTo(indexBodyUser2);
+    verify(serverApi, times(2)).getInfo();
+  }
+
+  private ManualRequestContext mockGerritApi(CurrentUser currentUser) throws RestApiException {
+    ManualRequestContext ctx = new ManualRequestContext(currentUser, threadLocalRequestContext);
+    if (currentUser.isIdentifiedUser()) {
+      AccountApi accountApi = mock(AccountApi.class);
+      lenient().when(accountsApi.self()).thenReturn(accountApi);
+    } else {
+      lenient()
+          .when(accountsApi.self())
+          .thenThrow(new AuthException("user needs to be authenticated"));
+    }
+
+    lenient().when(serverApi.getVersion()).thenReturn("123");
+    lenient().when(serverApi.topMenus()).thenReturn(ImmutableList.of(), ImmutableList.of());
+    lenient()
+        .when(serverApi.getInfo())
+        .thenAnswer((m) -> getServerInfo.apply(new ConfigResource()).value());
+    lenient().when(configApi.server()).thenReturn(serverApi);
+    lenient().when(gerritApi.accounts()).thenReturn(accountsApi);
+    lenient().when(gerritApi.config()).thenReturn(configApi);
+    return ctx;
   }
 }
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/javatests/com/google/gerrit/server/cache/h2/H2CacheFactoryTest.java b/javatests/com/google/gerrit/server/cache/h2/H2CacheFactoryTest.java
index fbd5890..9f55711 100644
--- a/javatests/com/google/gerrit/server/cache/h2/H2CacheFactoryTest.java
+++ b/javatests/com/google/gerrit/server/cache/h2/H2CacheFactoryTest.java
@@ -56,6 +56,7 @@
             DynamicMap.emptyMap(),
             null,
             null,
+            null,
             cacheDir,
             EnumSet.noneOf(CacheOptions.class),
             new AtomicBoolean(false));
diff --git a/javatests/com/google/gerrit/server/cache/h2/H2CacheTest.java b/javatests/com/google/gerrit/server/cache/h2/H2CacheTest.java
index cf8bdda..b4228b6 100644
--- a/javatests/com/google/gerrit/server/cache/h2/H2CacheTest.java
+++ b/javatests/com/google/gerrit/server/cache/h2/H2CacheTest.java
@@ -75,7 +75,9 @@
         refreshAfterWrite,
         true,
         true,
-        new AtomicBoolean(false));
+        new AtomicBoolean(false),
+        false,
+        null);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/mail/send/MailSoySauceModuleTest.java b/javatests/com/google/gerrit/server/mail/send/MailSoySauceModuleTest.java
index ed179a7..bd32241 100644
--- a/javatests/com/google/gerrit/server/mail/send/MailSoySauceModuleTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/MailSoySauceModuleTest.java
@@ -30,11 +30,11 @@
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Key;
+import com.google.inject.Provider;
 import com.google.inject.TypeLiteral;
 import com.google.inject.name.Names;
 import com.google.template.soy.jbcsrc.api.SoySauce;
 import java.nio.file.Path;
-import javax.inject.Provider;
 import org.eclipse.jgit.lib.Config;
 import org.junit.Test;
 
diff --git a/javatests/com/google/gerrit/testing/BUILD b/javatests/com/google/gerrit/testing/BUILD
index 136938a..ac3998eb 100644
--- a/javatests/com/google/gerrit/testing/BUILD
+++ b/javatests/com/google/gerrit/testing/BUILD
@@ -9,6 +9,7 @@
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:guava",
         "//lib:jgit",
+        "//lib/guice",
         "//lib/truth",
     ],
 )
diff --git a/javatests/com/google/gerrit/testing/GuiceErrorFormattingTest.java b/javatests/com/google/gerrit/testing/GuiceErrorFormattingTest.java
new file mode 100644
index 0000000..72e9da0
--- /dev/null
+++ b/javatests/com/google/gerrit/testing/GuiceErrorFormattingTest.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2026 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.testing;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import com.google.inject.ConfigurationException;
+import com.google.inject.Guice;
+import org.junit.Test;
+
+/**
+ * Regression test for the Guice bundled-ASM incompatibility with Java 25 class files (<a
+ * href="https://github.com/google/guice/issues/1926">guice#1926</a>).
+ *
+ * <p>When Guice formats the source location for a configuration error it reads the offending class
+ * with ASM. The ASM shaded into the default Guice jar cannot read Java 25 (class major version 69)
+ * bytecode: the read throws, Guice logs a warning, and the source silently degrades to {@code
+ * (Unknown Source)}. Building against the {@code classes} classifier plus an external OW2 ASM makes
+ * the read succeed, so the error names the real {@code File.java:line}.
+ *
+ * <p>The check below therefore asserts on the resolved source (present only when ASM works), not on
+ * the swallowed log line — otherwise it would pass with or without the fix.
+ */
+public class GuiceErrorFormattingTest {
+  @Test
+  public void configurationErrorResolvesSourceLocationOnJava25() {
+    ConfigurationException thrown =
+        assertThrows(
+            ConfigurationException.class,
+            () -> Guice.createInjector().getInstance(MissingBinding.class));
+
+    // Sanity: this is the missing-constructor error we set out to provoke.
+    assertThat(thrown).hasMessageThat().contains("No injectable constructor for type");
+
+    // The guard: source formatting must resolve the class's real file:line via ASM. With the
+    // bundled Guice ASM on Java 25 the read fails and the source degrades to "(Unknown Source)".
+    assertThat(thrown).hasMessageThat().contains("GuiceErrorFormattingTest.java:");
+    assertThat(thrown).hasMessageThat().doesNotContain("Unknown Source");
+  }
+
+  private static class MissingBinding {
+    @SuppressWarnings("UnusedMethod")
+    MissingBinding(String value) {
+      throw new AssertionError(value);
+    }
+  }
+}
diff --git a/javatests/com/google/gerrit/util/concurrent/ConcurrentBloomFilterTest.java b/javatests/com/google/gerrit/util/concurrent/ConcurrentBloomFilterTest.java
index 24b659a..eef5e11 100644
--- a/javatests/com/google/gerrit/util/concurrent/ConcurrentBloomFilterTest.java
+++ b/javatests/com/google/gerrit/util/concurrent/ConcurrentBloomFilterTest.java
@@ -312,7 +312,7 @@
 
   private static boolean await(CountDownLatch latch) {
     try {
-      return latch.await(100, TimeUnit.MILLISECONDS);
+      return latch.await(10, TimeUnit.SECONDS);
     } catch (InterruptedException e) {
       return false;
     }
@@ -320,7 +320,7 @@
 
   private static boolean get(Future<?> future) {
     try {
-      future.get(100, TimeUnit.MILLISECONDS);
+      future.get(10, TimeUnit.SECONDS);
       return true;
     } catch (Exception e) {
       return false;
diff --git a/lib/BUILD b/lib/BUILD
index 7255a4d..5c7efa2 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -1,4 +1,5 @@
 load("@rules_java//java:defs.bzl", "java_library")
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
 
 exports_files(glob([
     "LICENSE-*",
@@ -80,7 +81,7 @@
     name = "jgit-servlet",
     data = ["//lib:LICENSE-jgit"],
     visibility = ["//visibility:public"],
-    exports = ["@jgit//org.eclipse.jgit.http.server:jgit-servlet"],
+    exports = ["@jgit//org.eclipse.jgit.http.server.ee8:jgit-servlet-ee8"],
     runtime_deps = [":jgit"],
 )
 
@@ -210,6 +211,13 @@
 )
 
 java_library(
+    name = "jspecify",
+    data = ["//lib:LICENSE-Apache2.0"],
+    visibility = ["//visibility:public"],
+    exports = ["@external_deps//:org_jspecify_jspecify"],
+)
+
+java_library(
     name = "blame-cache",
     data = ["//lib:LICENSE-Apache2.0"],
     visibility = ["//visibility:public"],
diff --git a/lib/guice/BUILD b/lib/guice/BUILD
index 8e4272b..12151cd 100644
--- a/lib/guice/BUILD
+++ b/lib/guice/BUILD
@@ -15,7 +15,10 @@
     name = "guice-library",
     data = ["//lib:LICENSE-Apache2.0"],
     visibility = ["//visibility:public"],
-    exports = ["@external_deps//:com_google_inject_guice"],
+    exports = [
+        "//lib/ow2:ow2-asm",
+        "@external_deps//:com_google_inject_guice_classes",
+    ],
     runtime_deps = ["aopalliance"],
 )
 
diff --git a/lib/highlightjs/index.js b/lib/highlightjs/index.js
index 64c6f22..fdc40b4 100644
--- a/lib/highlightjs/index.js
+++ b/lib/highlightjs/index.js
@@ -30,4 +30,18 @@
 hljs.registerLanguage('vue', vue);
 hljs.registerLanguage('gn', gn);
 
+// Patch the Objective-C language definition to support C++14 digit separators.
+// TODO(upstream): Remove this workaround once highlight.js is upgraded to a
+// version that includes https://github.com/highlightjs/highlight.js/pull/4322
+const objc = hljs.getLanguage('objectivec');
+const cpp = hljs.getLanguage('cpp');
+if (objc && cpp) {
+  const cppNumberMode = cpp.contains.find(m => m.className === 'number' || m.scope === 'number');
+  const objcContains = objc.contains;
+  const objcNumIdx = objcContains.findIndex(m => m.scope === 'number' || m.className === 'number');
+  if (cppNumberMode && objcNumIdx !== -1) {
+    objcContains[objcNumIdx] = cppNumberMode;
+  }
+}
+
 export default hljs;
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..bf0f0ad 160000
--- a/modules/jgit
+++ b/modules/jgit
@@ -1 +1 @@
-Subproject commit 067521170e2a7308c6a1292a99900eadcee0b790
+Subproject commit bf0f0ad1cc2ce422604383272a4a4b8b3947b465
diff --git a/package.json b/package.json
index 97acc45..75a1a9f 100644
--- a/package.json
+++ b/package.json
@@ -66,11 +66,11 @@
     "test:single:coverage": "yarn --cwd=polygerrit-ui test:single:coverage",
     "safe_bazelisk": "if which bazelisk >/dev/null; then bazel_bin=bazelisk; else bazel_bin=bazel; fi && $bazel_bin",
     "eslint": "npm run safe_bazelisk test polygerrit-ui/app:lint_test",
-    "eslintfix": "npm run safe_bazelisk run polygerrit-ui/app:lint_bin -- -- --fix $(pwd)/polygerrit-ui/app",
-    "eslintfix:modified": "git diff --name-only --diff-filter=d | grep -E 'polygerrit-ui/app/.*\\.(js|ts)$' | sed 's|^polygerrit-ui/app/||' | xargs -r npm run safe_bazelisk run polygerrit-ui/app:lint_bin -- -- --fix",
+    "eslintfix": "eslint -c polygerrit-ui/app/eslint-bazel.config.js polygerrit-ui/app --fix --cache",
+    "eslintfix:modified": "git diff --name-only --diff-filter=d | grep -E 'polygerrit-ui/app/.*\\.(js|ts)$' | xargs -r eslint -c polygerrit-ui/app/eslint-bazel.config.js --fix",
     "litlint": "npm run safe_bazelisk run polygerrit-ui/app:lit_analysis",
     "litlintforCI": "lit-analyzer --strict --rules.no-unknown-property off --rules.no-unknown-tag-name off --rules.no-incompatible-type-binding off --rules.no-incompatible-property-type off --rules.no-invalid-tag-name off --rules.no-property-visibility-mismatch off --rules.no-unknown-attribute off **/elements/**/*.ts",
-    "lint": "eslint -c polygerrit-ui/app/eslint-bazel.config.js polygerrit-ui/app",
+    "lint": "eslint -c polygerrit-ui/app/eslint-bazel.config.js polygerrit-ui/app --cache",
     "gjf": "./tools/gjf.sh run"
   },
   "repository": {
diff --git a/plugins/BUILD b/plugins/BUILD
index b831b55..7af6fd2 100644
--- a/plugins/BUILD
+++ b/plugins/BUILD
@@ -91,12 +91,12 @@
     "//lib/guice:guice",
     "//lib/guice:guice-assistedinject",
     "//lib/guice:guice-servlet",
-    "//lib/guice:javax_inject",
     "//lib/httpcomponents:httpclient",
     "//lib/httpcomponents:httpcore",
     "//lib:jgit-servlet",
     "//lib:jgit",
     "//lib:jsr305",
+    "//lib:jspecify",
     "//lib/log:api",
     "//lib/log:log4j",
     "//lib/mina:sshd",
diff --git a/plugins/codemirror-editor b/plugins/codemirror-editor
index 35d2770..0b27d4f 160000
--- a/plugins/codemirror-editor
+++ b/plugins/codemirror-editor
@@ -1 +1 @@
-Subproject commit 35d277049e0a5af38e42684fcd21093f77f72748
+Subproject commit 0b27d4f966d89622b44d2c16b96379f37f08ff64
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/plugins/delete-project b/plugins/delete-project
index b892353..3603583 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit b8923537908122b610d263b480c2f8624126be26
+Subproject commit 3603583484c45e578aeff66885723dfd171932fa
diff --git a/plugins/external_plugin_deps.bzl b/plugins/external_plugin_deps.bzl
deleted file mode 100644
index c498979..0000000
--- a/plugins/external_plugin_deps.bzl
+++ /dev/null
@@ -1,5 +0,0 @@
-# Deprecation notice: This file is deprecated. Please migrate dependencies to
-# MODULE.bazel.
-
-def external_plugin_deps():
-    pass
diff --git a/plugins/gitiles b/plugins/gitiles
index ef5e73c..0e3ccb9 160000
--- a/plugins/gitiles
+++ b/plugins/gitiles
@@ -1 +1 @@
-Subproject commit ef5e73cbc0182e0cbb71ccd22143bf6b02736fdb
+Subproject commit 0e3ccb926a23b972d9f4472e3ba7d874fac481c8
diff --git a/plugins/package.json b/plugins/package.json
index 8faf6f9..9e4737b 100644
--- a/plugins/package.json
+++ b/plugins/package.json
@@ -31,9 +31,10 @@
     "@codemirror/state": "^6.6.0",
     "@codemirror/view": "^6.40.0",
     "@lezer/highlight": "^1.2.3",
-    "@gerritcodereview/typescript-api": "3.13.0",
+    "@gerritcodereview/typescript-api": "3.14.0",
     "@material/web": "^2.4.1",
     "@open-wc/testing": "^4.0.0",
+    "@types/sinon": "^17.0.4",
     "@web/dev-server-esbuild": "^1.0.4",
     "@web/test-runner": "^0.20.2",
     "lit": "^3.3.1",
diff --git a/plugins/pnpm-lock.yaml b/plugins/pnpm-lock.yaml
index 0f584e6..1c99c8c 100644
--- a/plugins/pnpm-lock.yaml
+++ b/plugins/pnpm-lock.yaml
@@ -90,8 +90,8 @@
         specifier: ^6.40.0
         version: 6.40.0
       '@gerritcodereview/typescript-api':
-        specifier: 3.13.0
-        version: 3.13.0
+        specifier: 3.14.0
+        version: 3.14.0
       '@lezer/highlight':
         specifier: ^1.2.3
         version: 1.2.3
@@ -101,6 +101,9 @@
       '@open-wc/testing':
         specifier: ^4.0.0
         version: 4.0.0
+      '@types/sinon':
+        specifier: ^17.0.4
+        version: 17.0.4
       '@web/dev-server-esbuild':
         specifier: ^1.0.4
         version: 1.0.4
@@ -379,8 +382,8 @@
   '@esm-bundle/chai@4.3.4-fix.0':
     resolution: {integrity: sha512-26SKdM4uvDWlY8/OOOxSB1AqQWeBosCX3wRYUZO7enTAj03CtVxIiCimYVG2WpULcyV51qapK4qTovwkUr5Mlw==}
 
-  '@gerritcodereview/typescript-api@3.13.0':
-    resolution: {integrity: sha512-1TvqARSNO9SXnnCzJKZVz9SFTr4hx1ZzBR0jyRZM6Xd4lLw3YwCfXozD9E8Pm4pfBWmN9WDkURboEG85p7ICag==}
+  '@gerritcodereview/typescript-api@3.14.0':
+    resolution: {integrity: sha512-GhYzh6h/bHUz2cU1pzHwlfWDeGSkGAlBulfvrUyG4N3tuA/jqUp5LSBDeeJHzXgmOSdNE7Qyravjh3wDd0REyg==}
 
   '@hapi/bourne@3.0.0':
     resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
@@ -2457,7 +2460,7 @@
     dependencies:
       '@types/chai': 4.3.20
 
-  '@gerritcodereview/typescript-api@3.13.0': {}
+  '@gerritcodereview/typescript-api@3.14.0': {}
 
   '@hapi/bourne@3.0.0': {}
 
diff --git a/plugins/replication b/plugins/replication
index 3e623c7..7e7c241 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 3e623c75033c815b97b5aa9b112dcc75c17dc018
+Subproject commit 7e7c2417c7a9cccd6e408955acf271c68c359bb3
diff --git a/plugins/yarn.lock b/plugins/yarn.lock
index 551be7b..26b3007 100644
--- a/plugins/yarn.lock
+++ b/plugins/yarn.lock
@@ -491,10 +491,10 @@
   dependencies:
     "@types/chai" "^4.2.12"
 
-"@gerritcodereview/typescript-api@3.13.0":
-  version "3.13.0"
-  resolved "https://registry.yarnpkg.com/@gerritcodereview/typescript-api/-/typescript-api-3.13.0.tgz#c7f5ac20f8b5b575424508eb1deb260a290ca0f1"
-  integrity sha512-1TvqARSNO9SXnnCzJKZVz9SFTr4hx1ZzBR0jyRZM6Xd4lLw3YwCfXozD9E8Pm4pfBWmN9WDkURboEG85p7ICag==
+"@gerritcodereview/typescript-api@3.14.0":
+  version "3.14.0"
+  resolved "https://registry.yarnpkg.com/@gerritcodereview/typescript-api/-/typescript-api-3.14.0.tgz#0ba1a06b68593e84e5a7525baf91521fdbcb2920"
+  integrity sha512-GhYzh6h/bHUz2cU1pzHwlfWDeGSkGAlBulfvrUyG4N3tuA/jqUp5LSBDeeJHzXgmOSdNE7Qyravjh3wDd0REyg==
 
 "@hapi/bourne@^3.0.0":
   version "3.0.0"
@@ -1170,7 +1170,7 @@
     "@types/chai" "*"
     "@types/sinon" "*"
 
-"@types/sinon@*":
+"@types/sinon@*", "@types/sinon@^17.0.4":
   version "17.0.4"
   resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-17.0.4.tgz#fd9a3e8e07eea1a3f4a6f82a972c899e5778f369"
   integrity sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==
diff --git a/polygerrit-ui/app/api/annotation.ts b/polygerrit-ui/app/api/annotation.ts
index 7cf200f..38c8141 100644
--- a/polygerrit-ui/app/api/annotation.ts
+++ b/polygerrit-ui/app/api/annotation.ts
@@ -5,6 +5,7 @@
  */
 import {
   CoverageRange,
+  DiffLayer,
   FileRange,
   GrDiff,
   TokenHighlightEventDetails,
@@ -39,6 +40,11 @@
   highlight?: TokenHighlightEventDetails
 ) => void;
 
+/**
+ * Factory function to create a DiffLayer.
+ */
+export type DiffLayerFactory = (details: DiffDetails) => DiffLayer;
+
 export declare interface AnnotationPluginApi {
   /**
    * The specified function will be called when a gr-diff component is built,
@@ -56,8 +62,11 @@
    *
    * The callback receives details of the diff itself and of the highlighted
    * token.
-   *
-   * TODO: Replace with a more general addDiffLayer() endpoint.
    */
   addTokenHoverListener(callback: TokenHoverListener): void;
+
+  /**
+   * Register a factory that creates a DiffLayer for each diff view.
+   */
+  addDiffLayer(factory: DiffLayerFactory): void;
 }
diff --git a/polygerrit-ui/app/api/diff.ts b/polygerrit-ui/app/api/diff.ts
index 50570f7..45ca607 100644
--- a/polygerrit-ui/app/api/diff.ts
+++ b/polygerrit-ui/app/api/diff.ts
@@ -385,11 +385,6 @@
   path?: string;
 }
 
-// TODO: Currently unused and not fired.
-export declare interface RenderProgressEventDetail {
-  linesRendered: number;
-}
-
 /**
  * The detail of the 'copy-info' event dispatched by gr-diff.
  */
diff --git a/polygerrit-ui/app/constants/reporting.ts b/polygerrit-ui/app/constants/reporting.ts
index a683f48..32e77ae 100644
--- a/polygerrit-ui/app/constants/reporting.ts
+++ b/polygerrit-ui/app/constants/reporting.ts
@@ -192,9 +192,9 @@
 
 /**
  * EventDetails to be passed to the reportInteraction method for AI agent
- * interactions.
+ * chat interactions.
  */
-export type AiAgentEventDetails = {
+export type AiAgentChatEventDetails = {
   agentId: string;
   conversationId: string;
   // Each agent response in a conversation is a turn.
@@ -206,3 +206,26 @@
   // Unique ID of the saved comment draft promoted from the suggestion.
   commentId?: string;
 };
+
+/**
+ * EventDetails to be passed to the reportInteraction method for AI agent
+ * check interactions.
+ */
+export type AiAgentCheckEventDetails = {
+  // The name of the check that was run.
+  checkName: string;
+  // The description from the check
+  checkDescription: string;
+  // The raw external id for the run result
+  externalId: string;
+  // Unique ID of the saved comment draft promoted from the suggestion.
+  commentId?: string;
+};
+
+/**
+ * EventDetails to be passed to the reportInteraction method for AI agent
+ * interactions.
+ */
+export type AiAgentEventDetails =
+  | AiAgentChatEventDetails
+  | AiAgentCheckEventDetails;
diff --git a/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog.ts b/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog.ts
index 418a735..f129503 100644
--- a/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog.ts
@@ -92,6 +92,8 @@
 
   @state() private promptSize = '';
 
+  @state() private opened = false;
+
   private readonly getChangeModel = resolve(this, changeModelToken);
 
   private readonly getCommentsModel = resolve(this, commentsModelToken);
@@ -314,6 +316,7 @@
   }
 
   override willUpdate(changedProperties: PropertyValues) {
+    if (!this.opened) return;
     if (
       changedProperties.has('patchContent') ||
       changedProperties.has('selectedTemplate') ||
@@ -327,6 +330,7 @@
   }
 
   open() {
+    this.opened = true;
     if (this.getNumParents() === 1) {
       this.loadPatchContent();
     }
@@ -437,6 +441,10 @@
   private handleCloseTap(e: Event) {
     e.preventDefault();
     e.stopPropagation();
+    this.opened = false;
+    this.patchContent = undefined;
+    this.promptContent = '';
+    this.promptSize = '';
     fire(this, 'close', {});
   }
 }
diff --git a/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog_test.ts b/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog_test.ts
index 8708dcf..bc4ef0f 100644
--- a/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog_test.ts
@@ -7,11 +7,18 @@
 import './gr-ai-prompt-dialog';
 import {assert, fixture, html} from '@open-wc/testing';
 import {GrAiPromptDialog} from './gr-ai-prompt-dialog';
-import {createParsedChange} from '../../../test/test-data-generators';
-import {CommitId, PatchSetNum} from '../../../api/rest-api';
+import {
+  createParsedChange,
+  createThread,
+} from '../../../test/test-data-generators';
+import {AccountInfo, CommitId, PatchSetNum} from '../../../api/rest-api';
 import {stubRestApi, waitUntil} from '../../../test/test-utils';
 import {testResolver} from '../../../test/common-test-setup';
 import {commentsModelToken} from '../../../models/comments/comments-model';
+import {
+  ChangeModel,
+  changeModelToken,
+} from '../../../models/change/change-model';
 import {of} from 'rxjs';
 
 suite('gr-ai-prompt-dialog test', () => {
@@ -28,6 +35,7 @@
 
     element = await fixture(html`<gr-ai-prompt-dialog></gr-ai-prompt-dialog>`);
     element.change = createParsedChange();
+    element.change.current_revision = 'abc' as CommitId;
     element.change.revisions['abc'].commit!.parents = [
       {
         commit: 'def' as CommitId,
@@ -35,9 +43,9 @@
       },
     ];
     element.patchNum = 1 as PatchSetNum;
-    element.patchContent = 'test code';
     element.selectedTemplate = 'PATCH_ONLY';
-    await element.updateComplete;
+    element.open();
+    await waitUntil(() => !!element.patchContent);
   });
 
   test('renders', async () => {
@@ -93,27 +101,42 @@
                  label="Context"
                  value="3"
                >
-                 <md-select-option md-menu-item="">
+                 <md-select-option
+                   md-menu-item=""
+                   tabindex="0"
+                 >
                    <div slot="headline">
                      3 lines (default)
                    </div>
                  </md-select-option>
-                 <md-select-option md-menu-item="">
+                 <md-select-option
+                   md-menu-item=""
+                   tabindex="-1"
+                 >
                    <div slot="headline">
                      10 lines
                    </div>
                  </md-select-option>
-                 <md-select-option md-menu-item="">
+                 <md-select-option
+                   md-menu-item=""
+                   tabindex="-1"
+                 >
                    <div slot="headline">
                      25 lines
                    </div>
                  </md-select-option>
-                 <md-select-option md-menu-item="">
+                 <md-select-option
+                   md-menu-item=""
+                   tabindex="-1"
+                 >
                    <div slot="headline">
                      50 lines
                    </div>
                  </md-select-option>
-                 <md-select-option md-menu-item="">
+                 <md-select-option
+                   md-menu-item=""
+                   tabindex="-1"
+                 >
                    <div slot="headline">
                      100 lines
                    </div>
@@ -188,8 +211,7 @@
     element.selectedTemplate = 'HELP_REVIEW';
     await element.updateComplete;
     assert.include(
-      // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      (element as any).promptContent,
+      Reflect.get(element, 'promptContent') as string,
       'You are a highly experienced code reviewer'
     );
   });
@@ -197,34 +219,30 @@
   test('renders resolve comments prompt', async () => {
     element.selectedTemplate = 'RESOLVE_COMMENTS';
     await element.updateComplete;
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    assert.include((element as any).promptContent, 'No unresolved comments.');
+    assert.include(
+      Reflect.get(element, 'promptContent') as string,
+      'No unresolved comments.'
+    );
   });
 
   test('renders resolve comments prompt with comments', async () => {
     element.threads = [
       {
-        comments: [
-          {
-            message: 'test comment',
-            author: {name: 'Tester'},
-            updated: '2025-01-01 10:00:00.000000000',
-            unresolved: true,
-          },
-        ],
+        ...createThread({
+          message: 'test comment',
+          author: {name: 'Tester'} as AccountInfo,
+          unresolved: true,
+        }),
         path: 'test.txt',
         line: 1,
-        rootId: '1',
       },
-      // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    ] as any[];
+    ];
     element.selectedTemplate = 'RESOLVE_COMMENTS';
     await element.updateComplete;
     const expected = `* File: test.txt (Line 1)
 Tester:
 test comment`;
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    assert.include((element as any).promptContent, expected);
+    assert.include(Reflect.get(element, 'promptContent') as string, expected);
   });
 
   test('preserves dollar signs in patch content', async () => {
@@ -236,4 +254,68 @@
     const promptContent = Reflect.get(element, 'promptContent') as string;
     assert.include(promptContent, expected);
   });
+
+  suite('eager loading prevention', () => {
+    let changeModel: ChangeModel;
+
+    setup(() => {
+      getPatchContentStub.resetHistory();
+      changeModel = testResolver(changeModelToken);
+    });
+
+    test('does not load patch content on initialization', async () => {
+      const change = createParsedChange();
+      change.revisions['abc'].commit!.parents = [
+        {
+          commit: 'def' as CommitId,
+          subject: 'Parent',
+        },
+      ];
+      Object.defineProperty(changeModel, 'change$', {
+        value: of(change),
+        writable: true,
+      });
+      Object.defineProperty(changeModel, 'patchNum$', {
+        value: of(1 as PatchSetNum),
+        writable: true,
+      });
+
+      const testElement = await fixture<GrAiPromptDialog>(
+        html`<gr-ai-prompt-dialog></gr-ai-prompt-dialog>`
+      );
+      await testElement.updateComplete;
+
+      assert.isFalse(getPatchContentStub.called);
+    });
+
+    test('loads patch content when open is called', async () => {
+      const change = createParsedChange();
+      change.revisions['abc'].commit!.parents = [
+        {
+          commit: 'def' as CommitId,
+          subject: 'Parent',
+        },
+      ];
+      Object.defineProperty(changeModel, 'change$', {
+        value: of(change),
+        writable: true,
+      });
+      Object.defineProperty(changeModel, 'patchNum$', {
+        value: of(1 as PatchSetNum),
+        writable: true,
+      });
+
+      const testElement = await fixture<GrAiPromptDialog>(
+        html`<gr-ai-prompt-dialog></gr-ai-prompt-dialog>`
+      );
+      await testElement.updateComplete;
+
+      assert.isFalse(getPatchContentStub.called);
+
+      testElement.open();
+      await testElement.updateComplete;
+
+      assert.isTrue(getPatchContentStub.called);
+    });
+  });
 });
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
index 5f274bc..b35a0ab 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
@@ -1213,6 +1213,15 @@
         continue;
       }
       const status = this.getLabelStatus(labelInfo);
+      if (status === LabelStatus.REJECT || status === LabelStatus.IMPOSSIBLE) {
+        return null;
+      }
+      if (
+        label === StandardLabels.PRESUBMIT_VERIFIED ||
+        label.toLowerCase().startsWith('presubmit')
+      ) {
+        continue;
+      }
       if (status === LabelStatus.NEED) {
         if (result) {
           // More than one label is missing, so check if Code Review can be
@@ -1221,11 +1230,6 @@
           break;
         }
         result = label;
-      } else if (
-        status === LabelStatus.REJECT ||
-        status === LabelStatus.IMPOSSIBLE
-      ) {
-        return null;
       }
     }
     // Allow the user to use quick approve to vote the max score on code review
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.ts b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.ts
index c571675..a8a31c6 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.ts
@@ -2561,6 +2561,57 @@
         );
         assert.isNotOk(approveButton);
       });
+
+      test('ignore presubmit labels for quick approve', async () => {
+        element.change = {
+          ...createChangeViewChange(),
+          current_revision: 'abc1234' as CommitId,
+          labels: {
+            'Code-Review': {
+              approved: createAccountWithId(1),
+              all: [{value: 2}],
+            },
+            'Presubmit-Verified': {
+              values: {'-1': '', ' 0': '', '+1': ''},
+            },
+          },
+          permitted_labels: {
+            'Presubmit-Verified': ['-1', ' 0', '+1'],
+          },
+        };
+        await element.updateComplete;
+        const approveButton = query(
+          element,
+          "gr-button[data-action-key='review']"
+        );
+        assert.isNotOk(approveButton);
+      });
+
+      test('rejected presubmit label blocks quick approve', async () => {
+        element.change = {
+          ...createChangeViewChange(),
+          current_revision: 'abc1234' as CommitId,
+          labels: {
+            'Code-Review': {
+              values: {'-2': '', '-1': '', ' 0': '', '+1': '', '+2': ''},
+            },
+            'Presubmit-Verified': {
+              rejected: createAccountWithId(2),
+              values: {'-1': '', ' 0': '', '+1': ''},
+            },
+          },
+          permitted_labels: {
+            'Code-Review': ['-2', '-1', ' 0', '+1', '+2'],
+            'Presubmit-Verified': ['-1', ' 0', '+1'],
+          },
+        };
+        await element.updateComplete;
+        const approveButton = query(
+          element,
+          "gr-button[data-action-key='review']"
+        );
+        assert.isNotOk(approveButton);
+      });
     });
 
     test('adds download revision action', async () => {
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
index 4439344..d9de5f4e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
@@ -579,6 +579,7 @@
       .statusOrCategory=${statusOrCategory}
       .text=${text}
       .links=${links}
+      .isAi=${!!run.isAiPowered}
       @click=${handler}
       @keydown=${(e: KeyboardEvent) => handleSpaceOrEnter(e, handler)}
     ></gr-checks-chip>`;
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_screenshot_test.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_screenshot_test.ts
index 1559a1c..045186e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_screenshot_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_screenshot_test.ts
@@ -52,11 +52,13 @@
       createRun({
         status: RunStatus.COMPLETED,
         checkName: 'info-check',
+        isAiPowered: true,
         results: [createCheckResult({category: Category.INFO})],
       }),
       createRun({
         status: RunStatus.COMPLETED,
         checkName: 'warning-check',
+        isAiPowered: true,
         results: [createCheckResult({category: Category.WARNING})],
       }),
       createRun({
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_test.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_test.ts
index 5a1e855..9744398 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_test.ts
@@ -264,6 +264,92 @@
     });
   });
 
+  suite('ai chips', () => {
+    test('detailed chip has isAi set based on run.isAiPowered', async () => {
+      element.runs = [
+        createRun({
+          checkName: 'AI Check',
+          status: RunStatus.COMPLETED,
+          isAiPowered: true,
+          results: [createCheckResult({category: Category.ERROR})],
+        }),
+        createRun({
+          checkName: 'Normal Check',
+          status: RunStatus.COMPLETED,
+          isAiPowered: false,
+          results: [createCheckResult({category: Category.ERROR})],
+        }),
+      ];
+      element.showChecksSummary = true;
+      await element.updateComplete;
+
+      const chips = queryAll<GrChecksChip>(element, 'gr-checks-chip');
+      assert.equal(chips.length, 2);
+      assert.isTrue(chips[0].isAi);
+      assert.equal(chips[0].text, 'AI Check');
+      assert.isFalse(chips[1].isAi);
+      assert.equal(chips[1].text, 'Normal Check');
+    });
+
+    test('collapsed chip does not have isAi=true even if run is AI powered', async () => {
+      element.runs = [
+        createRun({
+          status: RunStatus.COMPLETED,
+          isAiPowered: true,
+          results: [createCheckResult({category: Category.SUCCESS})],
+        }),
+        createRun({
+          status: RunStatus.COMPLETED,
+          isAiPowered: false,
+          results: [createCheckResult({category: Category.SUCCESS})],
+        }),
+        createRun({status: RunStatus.RUNNING}),
+      ];
+      element.showChecksSummary = true;
+      await element.updateComplete;
+
+      const chips = queryAll<GrChecksChip>(element, 'gr-checks-chip');
+      const successChip = [...chips].find(
+        c => c.statusOrCategory === Category.SUCCESS
+      );
+      assert.isDefined(successChip);
+      assert.isFalse(successChip.isAi);
+      assert.equal(successChip.text, '2');
+    });
+
+    test('plus-more chip does not have isAi=true even if overflow run is AI powered', async () => {
+      const runs: CheckRun[] = [];
+      for (let i = 0; i < 8; i++) {
+        runs.push(
+          createRun({
+            checkName: `Error ${i}`,
+            status: RunStatus.COMPLETED,
+            isAiPowered: false,
+            results: [createCheckResult({category: Category.ERROR})],
+          })
+        );
+      }
+      runs.push(
+        createRun({
+          checkName: 'Error AI',
+          status: RunStatus.COMPLETED,
+          isAiPowered: true,
+          results: [createCheckResult({category: Category.ERROR})],
+        })
+      );
+
+      element.runs = runs;
+      element.showChecksSummary = true;
+      await element.updateComplete;
+
+      const chips = queryAll<GrChecksChip>(element, 'gr-checks-chip');
+      assert.equal(chips.length, 8);
+      const plusMoreChip = chips[7];
+      assert.equal(plusMoreChip.text, '+ 2 more');
+      assert.isFalse(plusMoreChip.isAi);
+    });
+  });
+
   suite('flows summary', () => {
     test('renders', async () => {
       flowsModel.setState({
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip.ts
index 1875fa5..eedf452 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip.ts
@@ -28,6 +28,9 @@
   @property({type: Array})
   links: string[] = [];
 
+  @property({type: Boolean})
+  isAi = false;
+
   private readonly reporting = getAppContext().reportingService;
 
   static override get styles() {
@@ -79,6 +82,10 @@
         }
         gr-icon {
           font-size: var(--line-height-small);
+          --gr-icon-size: var(--line-height-small);
+        }
+        gr-icon.ai-sparkle {
+          margin-left: var(--spacing-xs);
         }
         .checksChip a gr-icon.launch {
           color: var(--link-color);
@@ -172,9 +179,14 @@
     // 15 is roughly the number of chars for the chip exceeding its 120px width.
     return html`
       ${this.text.length > 15
-        ? html` ${this.renderChip(chipClassFullLength, ariaLabel, icon)}`
+        ? html` ${this.renderChip(
+            chipClassFullLength,
+            ariaLabel,
+            icon,
+            this.isAi
+          )}`
         : ''}
-      ${this.renderChip(chipClass, ariaLabel, icon)}
+      ${this.renderChip(chipClass, ariaLabel, icon, this.isAi)}
     `;
   }
 
@@ -191,12 +203,18 @@
     return `${label} for check ${this.text}`;
   }
 
-  private renderChip(clazz: string, ariaLabel: string, icon: ChecksIcon) {
+  private renderChip(
+    clazz: string,
+    ariaLabel: string,
+    icon: ChecksIcon,
+    isAi: boolean
+  ) {
     return html`
       <div class=${clazz} role="link" tabindex="0" aria-label=${ariaLabel}>
         <gr-icon icon=${icon.name} ?filled=${!!icon.filled}></gr-icon>
         ${this.renderLinks()}
         <div class="text">${this.text}</div>
+        ${isAi ? html`<gr-icon icon="ai" class="ai-sparkle"></gr-icon>` : ''}
       </div>
     `;
   }
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip_test.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip_test.ts
index 6816609..c020980 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip_test.ts
@@ -85,4 +85,26 @@
       `
     );
   });
+
+  test('renders AI icon', async () => {
+    element.text = 'AI Check';
+    element.statusOrCategory = Category.ERROR;
+    element.isAi = true;
+    await element.updateComplete;
+    assert.shadowDom.equal(
+      element,
+      /* HTML */ `
+        <div
+          aria-label="error for check AI Check"
+          class="checksChip error font-small"
+          role="link"
+          tabindex="0"
+        >
+          <gr-icon icon="error" filled></gr-icon>
+          <div class="text">AI Check</div>
+          <gr-icon icon="ai" class="ai-sparkle"></gr-icon>
+        </div>
+      `
+    );
+  });
 });
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..bad9f1e 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
@@ -981,7 +981,7 @@
           flex: 1;
           overflow-x: hidden;
         }
-        .relatedChanges {
+        .commitAside {
           flex: 0 1 auto;
           overflow: hidden;
           padding: var(--spacing-l) 0;
@@ -1005,6 +1005,16 @@
           margin: var(--spacing-l) 0;
           padding: 0 var(--spacing-l);
         }
+        .commitAside gr-endpoint-decorator[name='change-view-commit-aside'] {
+          display: none;
+        }
+        .commitAside
+          gr-endpoint-decorator[name='change-view-commit-aside']:has(
+            :not(gr-endpoint-param):not([hidden])
+          ) {
+          display: block;
+          margin-bottom: var(--spacing-l);
+        }
         .showOnEdit {
           display: none;
         }
@@ -1055,10 +1065,10 @@
           position: relative;
         }
         @media screen and (max-width: 75em) {
-          .relatedChanges {
+          .commitAside {
             padding: 0;
           }
-          .relatedChanges gr-related-changes-list {
+          .commitAside gr-related-changes-list {
             padding-top: var(--spacing-l);
           }
           #commitAndRelated {
@@ -1320,7 +1330,7 @@
       {
         label: 'URL and title',
         shortcut: 'r',
-        value: `${changeURL}: ${this.change?.subject}`,
+        value: `${changeURL} - ${this.change?.subject}`,
       },
       {
         label: 'Markdown',
@@ -1425,7 +1435,13 @@
               </gr-endpoint-param>
             </gr-endpoint-decorator>
           </div>
-          <div class="relatedChanges">
+          <div class="commitAside">
+            <gr-endpoint-decorator name="change-view-commit-aside">
+              <gr-endpoint-param name="change" .value=${this.change}>
+              </gr-endpoint-param>
+              <gr-endpoint-param name="revision" .value=${this.revision}>
+              </gr-endpoint-param>
+            </gr-endpoint-decorator>
             <gr-related-changes-list></gr-related-changes-list>
           </div>
           <div class="emptySpace"></div>
@@ -1923,7 +1939,7 @@
     const fileIndex = this.fileList.files.findIndex(f => f.__path === path);
     if (fileIndex !== -1) {
       this.fileList.fileCursor.setCursorAtIndex(fileIndex, true);
-      const isExpanded = this.fileList.expandedFiles.some(f => f.path === path);
+      const isExpanded = this.fileList.expandedFiles.has(path);
       if (!isExpanded) {
         this.fileList.toggleFileExpandedByIndex(fileIndex);
         await this.fileList.updateComplete;
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
index 26b6358..7e11970 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
@@ -355,7 +355,12 @@
                           </gr-endpoint-param>
                         </gr-endpoint-decorator>
                       </div>
-                      <div class="relatedChanges">
+                      <div class="commitAside">
+                        <gr-endpoint-decorator name="change-view-commit-aside">
+                          <gr-endpoint-param name="change"> </gr-endpoint-param>
+                          <gr-endpoint-param name="revision">
+                          </gr-endpoint-param>
+                        </gr-endpoint-decorator>
                         <gr-related-changes-list> </gr-related-changes-list>
                       </div>
                       <div class="emptySpace"></div>
@@ -1364,6 +1369,91 @@
     });
   });
 
+  suite('change-view-commit-aside visibility', () => {
+    let decorator: HTMLElement;
+
+    setup(async () => {
+      element.change = {...createChangeViewChange(), labels: {}};
+      element.revision = createRevision();
+      await element.updateComplete;
+      decorator = element.shadowRoot!.querySelector(
+        'gr-endpoint-decorator[name="change-view-commit-aside"]'
+      )!;
+    });
+
+    test('hidden by default', () => {
+      assert.equal(getComputedStyle(decorator).display, 'none');
+    });
+
+    test('hidden if plugin component is hidden', async () => {
+      const promise = mockPromise();
+      window.Gerrit.install(
+        promise.resolve,
+        '0.1',
+        'http://some/plugins/url.js'
+      );
+      const plugin = (await promise) as PluginApi;
+
+      const dummyTagName = 'dummy-aside-component-hidden';
+      if (!customElements.get(dummyTagName)) {
+        customElements.define(
+          dummyTagName,
+          class extends HTMLElement {
+            connectedCallback() {
+              this.setAttribute('hidden', '');
+            }
+          }
+        );
+      }
+
+      plugin.registerCustomComponent('change-view-commit-aside', dummyTagName);
+
+      await new Promise<void>(resolve => {
+        const observer = new MutationObserver(() => {
+          if (decorator.querySelector(dummyTagName)) {
+            observer.disconnect();
+            resolve();
+          }
+        });
+        observer.observe(decorator, {childList: true});
+      });
+
+      await element.updateComplete;
+      assert.equal(getComputedStyle(decorator).display, 'none');
+    });
+
+    test('visible if plugin component is visible', async () => {
+      const promise = mockPromise();
+      window.Gerrit.install(
+        promise.resolve,
+        '0.1',
+        'http://some/plugins/url.js'
+      );
+      const plugin = (await promise) as PluginApi;
+
+      const dummyTagName = 'dummy-aside-component-visible';
+      if (!customElements.get(dummyTagName)) {
+        customElements.define(dummyTagName, class extends HTMLElement {});
+      }
+
+      plugin.registerCustomComponent('change-view-commit-aside', dummyTagName);
+
+      await new Promise<void>(resolve => {
+        const observer = new MutationObserver(() => {
+          if (decorator.querySelector(dummyTagName)) {
+            observer.disconnect();
+            resolve();
+          }
+        });
+        observer.observe(decorator, {childList: true});
+      });
+
+      await element.updateComplete;
+      assert.equal(getComputedStyle(decorator).display, 'block');
+      assert.notEqual(getComputedStyle(decorator).marginBottom, '0px');
+    });
+  });
+
   test('handleToggleStar called when star is tapped', async () => {
     element.change = {
       ...createChangeViewChange(),
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.ts b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.ts
index 95df2fac..f808b1c 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.ts
@@ -168,6 +168,7 @@
           .commands=${this.computeDownloadCommands()}
           .schemes=${this.schemes}
           .selectedScheme=${this.selectedScheme}
+          .disableAutoSelect=${true}
           show-keyboard-shortcut-tooltips
           @selected-scheme-changed=${(e: BindValueChangeEvent) => {
             this.selectedScheme = e.detail.value;
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.ts b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.ts
index c107c20..438e151 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.ts
@@ -122,6 +122,7 @@
       </section>
       <section class="hidden">
         <gr-download-commands
+          disable-auto-select=""
           id="downloadCommands"
           show-keyboard-shortcut-tooltips=""
         >
@@ -234,6 +235,14 @@
       await element.updateComplete;
     });
 
+    test('passes disableAutoSelect to gr-download-commands', () => {
+      const commands = queryAndAssert<GrDownloadCommands>(
+        element,
+        '#downloadCommands'
+      );
+      assert.isTrue(commands.disableAutoSelect);
+    });
+
     test('focuses on first copy link', async () => {
       const focusStub = sinon.stub(
         queryAndAssert<GrDownloadCommands>(element, '#downloadCommands'),
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index dc28545..f255cb4 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -269,7 +269,7 @@
 
   // Private but used in tests.
   @state()
-  expandedFiles: PatchSetFile[] = [];
+  expandedFiles: Set<string> = new Set();
 
   // Private but used in tests.
   @state()
@@ -1976,29 +1976,26 @@
   }
 
   // private but used in test
-  toggleFileExpanded(file: PatchSetFile) {
+  toggleFileExpanded(path: string) {
     // Is the path in the list of expanded diffs? If so, remove it, otherwise
     // add it to the list.
-    const indexInExpanded = this.expandedFiles.findIndex(
-      f => f.path === file.path
-    );
-    if (indexInExpanded === -1) {
+    const newExpandedFiles = new Set(this.expandedFiles);
+    if (!newExpandedFiles.has(path)) {
       this.reporting.reportInteraction(Interaction.FILE_LIST_DIFF_EXPANDED);
-      this.expandedFiles = this.expandedFiles.concat([file]);
+      newExpandedFiles.add(path);
     } else {
       this.reporting.reportInteraction(Interaction.FILE_LIST_DIFF_COLLAPSED);
-      this.expandedFiles = this.expandedFiles.filter(
-        (_val, idx) => idx !== indexInExpanded
-      );
+      newExpandedFiles.delete(path);
     }
-    const indexInAll = this.files.findIndex(f => f.__path === file.path);
+    this.expandedFiles = newExpandedFiles;
+    const indexInAll = this.files.findIndex(f => f.__path === path);
     this.shadowRoot!.querySelectorAll(`.${FILE_ROW_CLASS}`)[
       indexInAll
     ].scrollIntoView({block: 'nearest'});
   }
 
   toggleFileExpandedByIndex(index: number) {
-    this.toggleFileExpanded(this.computePatchSetFile(this.files[index]));
+    this.toggleFileExpanded(this.files[index].__path);
   }
 
   // Private but used in tests.
@@ -2007,23 +2004,22 @@
       return;
     }
     // Re-render all expanded diffs sequentially.
-    this.renderInOrder(this.expandedFiles, this.diffs);
+    this.renderInOrder([...this.expandedFiles], this.diffs);
   }
 
   expandAllDiffs() {
-    const newFiles = this.files
-      .slice(0, this.numFilesShown)
-      // TODO(b/419187980): Refactor expandedFiles to use a Set for efficiency.
-      .filter(file => !this.expandedFiles.some(f => f.path === file.__path))
-      .map(file => this.computePatchSetFile(file));
+    const newExpandedFiles = new Set(this.expandedFiles);
+    this.files.slice(0, this.numFilesShown).forEach(file => {
+      newExpandedFiles.add(file.__path);
+    });
 
     this.reporting.reportInteraction(Interaction.FILE_LIST_ALL_DIFFS_EXPANDED);
-    this.expandedFiles = newFiles.concat(this.expandedFiles);
+    this.expandedFiles = newExpandedFiles;
   }
 
   collapseAllDiffs() {
     this.reporting.reportInteraction(Interaction.FILE_LIST_ALL_DIFFS_COLLAPSED);
-    this.expandedFiles = [];
+    this.expandedFiles = new Set();
   }
 
   /**
@@ -2119,8 +2115,8 @@
     this.fileActionClick(e, file => this.reviewFile(file.path));
   }
 
-  private expandedClick(e: MouseEvent | KeyboardEvent) {
-    this.fileActionClick(e, file => this.toggleFileExpanded(file));
+  expandedClick(e: MouseEvent | KeyboardEvent) {
+    this.fileActionClick(e, file => this.toggleFileExpanded(file.path));
   }
 
   /**
@@ -2152,7 +2148,7 @@
 
     e.preventDefault();
     this.fileCursor.setCursor(fileRow.element);
-    this.toggleFileExpanded(file);
+    this.toggleFileExpanded(path);
   }
 
   private getFileRowFromEvent(e: Event): FileRow | null {
@@ -2424,7 +2420,7 @@
   }
 
   async filesChanged() {
-    if (this.expandedFiles.length > 0) this.expandedFiles = [];
+    if (this.expandedFiles.size > 0) this.expandedFiles = new Set();
     await this.updateCleanlyMergedPaths();
     if (!this.files || this.files.length === 0) return;
     await this.updateComplete;
@@ -2490,18 +2486,18 @@
     return val ? 'true' : 'false';
   }
 
-  private isFileExpanded(path: string | undefined) {
-    return this.expandedFiles.some(f => f.path === path);
+  isFileExpanded(path: string | undefined) {
+    return path !== undefined && this.expandedFiles.has(path);
   }
 
-  private isFileExpandedStr(path: string | undefined) {
+  isFileExpandedStr(path: string | undefined) {
     return this.booleanToString(this.isFileExpanded(path));
   }
 
   private computeExpandedFiles(): FilesExpandedState {
-    if (this.expandedFiles.length === 0) {
+    if (this.expandedFiles.size === 0) {
       return FilesExpandedState.NONE;
-    } else if (this.expandedFiles.length === this.files.length) {
+    } else if (this.expandedFiles.size === this.files.length) {
       return FilesExpandedState.ALL;
     }
     return FilesExpandedState.SOME;
@@ -2516,18 +2512,21 @@
    * @param newFiles The new files that have been added.
    * Private but used in tests.
    */
-  async expandedFilesChanged(oldFiles: Array<PatchSetFile>) {
+  async expandedFilesChanged(oldFiles?: Set<string>) {
     this.filesExpanded = this.computeExpandedFiles();
 
-    const newFiles = this.expandedFiles.filter(
-      file => (oldFiles ?? []).findIndex(f => f.path === file.path) === -1
-    );
+    const newPaths: string[] = [];
+    for (const path of this.expandedFiles) {
+      if (!oldFiles || !oldFiles.has(path)) {
+        newPaths.push(path);
+      }
+    }
 
     // Required so that the newly created diff view is included in this.diffs.
     await this.updateComplete;
 
-    if (newFiles.length) {
-      await this.renderInOrder(newFiles, this.diffs);
+    if (newPaths.length) {
+      await this.renderInOrder(newPaths, this.diffs);
     }
     this.updateDiffCursor();
     this.diffCursor?.reInitAndUpdateStops();
@@ -2542,11 +2541,10 @@
    *
    * @param initialCount The total number of paths in the pass.
    */
-  async renderInOrder(files: PatchSetFile[], diffElements: GrDiffHost[]) {
+  async renderInOrder(paths: string[], diffElements: GrDiffHost[]) {
     this.reporting.time(Timing.FILE_EXPAND_ALL);
 
-    for (const file of files) {
-      const path = file.path;
+    for (const path of paths) {
       const diffElem = this.findDiffByPath(path, diffElements);
       if (!diffElem) {
         this.reporting.error(
@@ -2558,8 +2556,7 @@
       diffElem.prefetchDiff();
     }
 
-    await asyncForeach(files, async (file, cancel) => {
-      const path = file.path;
+    await asyncForeach(paths, async (path, cancel) => {
       this.cancelForEachDiff = cancel;
 
       const diffElem = this.findDiffByPath(path, diffElements);
@@ -2582,7 +2579,7 @@
       if (
         this.loggedIn &&
         !this.diffPrefs.manual_review &&
-        files.length === 1
+        paths.length === 1
       ) {
         await this.reviewFile(path, true);
       }
@@ -2591,7 +2588,7 @@
 
     this.cancelForEachDiff = undefined;
     this.reporting.timeEnd(Timing.FILE_EXPAND_ALL, {
-      count: files.length,
+      count: paths.length,
       height: this.clientHeight,
     });
     /*
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
index be49bfa..4c6bf52 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
@@ -1067,41 +1067,41 @@
         element.fileCursor.setCursorAtIndex(0);
         await element.updateComplete;
         assert.equal(element.diffs.length, 0);
-        assert.equal(element.expandedFiles.length, 0);
+        assert.equal(element.expandedFiles.size, 0);
 
         pressKey(element, 'i');
         await element.updateComplete;
         assert.equal(element.diffs.length, 1);
         assert.equal(element.diffs[0].path, paths[0]);
-        assert.equal(element.expandedFiles.length, 1);
-        assert.equal(element.expandedFiles[0].path, paths[0]);
+        assert.equal(element.expandedFiles.size, 1);
+        assert.isTrue(element.expandedFiles.has(paths[0]));
 
         pressKey(element, 'i');
         await element.updateComplete;
         assert.equal(element.diffs.length, 0);
-        assert.equal(element.expandedFiles.length, 0);
+        assert.equal(element.expandedFiles.size, 0);
 
         element.fileCursor.setCursorAtIndex(1);
         pressKey(element, 'i');
         await element.updateComplete;
         assert.equal(element.diffs.length, 1);
         assert.equal(element.diffs[0].path, paths[1]);
-        assert.equal(element.expandedFiles.length, 1);
-        assert.equal(element.expandedFiles[0].path, paths[1]);
+        assert.equal(element.expandedFiles.size, 1);
+        assert.isTrue(element.expandedFiles.has(paths[1]));
 
         pressKey(element, 'I');
         await element.updateComplete;
         assert.equal(element.diffs.length, paths.length);
-        assert.equal(element.expandedFiles.length, paths.length);
+        assert.equal(element.expandedFiles.size, paths.length);
         for (const diff of element.diffs) {
-          assert.isTrue(element.expandedFiles.some(f => f.path === diff.path));
+          assert.isTrue(element.expandedFiles.has(diff.path!));
         }
         // since _expandedFilesChanged is stubbed
         element.filesExpanded = FilesExpandedState.ALL;
         pressKey(element, 'I');
         await element.updateComplete;
         assert.equal(element.diffs.length, 0);
-        assert.equal(element.expandedFiles.length, 0);
+        assert.equal(element.expandedFiles.size, 0);
       });
 
       test('r key sets reviewed flag', async () => {
@@ -1340,10 +1340,7 @@
       await element.updateComplete;
 
       assert.equal(showHideCheck!.getAttribute('aria-checked'), 'true');
-      assert.notEqual(
-        element.expandedFiles.findIndex(f => f.path === 'myfile.txt'),
-        -1
-      );
+      assert.isTrue(element.expandedFiles.has('myfile.txt'));
     });
 
     test('diff mode correctly toggles the diffs', async () => {
@@ -1413,8 +1410,8 @@
         queryAndAssert<GrIcon>(element, 'gr-icon').icon,
         'expand_more'
       );
-      assert.equal(element.expandedFiles.length, 0);
-      element.toggleFileExpanded({path});
+      assert.equal(element.expandedFiles.size, 0);
+      element.toggleFileExpanded(path);
       await element.updateComplete;
       // Wait for expandedFilesChanged to finish.
       await waitEventLoop();
@@ -1425,8 +1422,8 @@
       );
 
       assert.equal(renderSpy.callCount, 1);
-      assert.isTrue(element.expandedFiles.some(f => f.path === path));
-      element.toggleFileExpanded({path});
+      assert.isTrue(element.expandedFiles.has(path));
+      element.toggleFileExpanded(path);
       await element.updateComplete;
       // Wait for expandedFilesChanged to finish.
       await waitEventLoop();
@@ -1436,7 +1433,7 @@
         'expand_more'
       );
       assert.equal(renderSpy.callCount, 1);
-      assert.isFalse(element.expandedFiles.some(f => f.path === path));
+      assert.isFalse(element.expandedFiles.has(path));
     });
 
     test('expandAllDiffs and collapseAllDiffs', async () => {
@@ -1459,7 +1456,7 @@
       await element.updateComplete;
       // Wait for expandedFilesChanged to finish.
       await waitEventLoop();
-      assert.equal(element.expandedFiles.length, 0);
+      assert.equal(element.expandedFiles.size, 0);
       assert.equal(element.filesExpanded, FilesExpandedState.NONE);
     });
 
@@ -1490,7 +1487,7 @@
         },
       ];
       sinon.stub(element, 'diffs').get(() => diffs);
-      element.expandedFiles = element.expandedFiles.concat([{path}]);
+      element.expandedFiles = new Set([path]);
       await element.updateComplete;
       await waitEventLoop();
       await promise;
@@ -1500,12 +1497,12 @@
       element.files = [normalize({}, 'foo.bar'), normalize({}, 'baz.bar')];
       await element.updateComplete;
       assert.equal(element.filesExpanded, FilesExpandedState.NONE);
-      element.expandedFiles.push({path: 'baz.bar'});
-      element.expandedFilesChanged([{path: 'baz.bar'}]);
+      element.expandedFiles.add('baz.bar');
+      element.expandedFilesChanged(new Set(['baz.bar']));
       await element.updateComplete;
       assert.equal(element.filesExpanded, FilesExpandedState.SOME);
-      element.expandedFiles.push({path: 'foo.bar'});
-      element.expandedFilesChanged([{path: 'foo.bar'}]);
+      element.expandedFiles.add('foo.bar');
+      element.expandedFilesChanged(new Set(['baz.bar', 'foo.bar']));
       await element.updateComplete;
       assert.equal(element.filesExpanded, FilesExpandedState.ALL);
       element.collapseAllDiffs();
@@ -1552,10 +1549,7 @@
         },
         // eslint-disable-next-line @typescript-eslint/no-explicit-any
       ] as any;
-      await element.renderInOrder(
-        [{path: 'p2'}, {path: 'p1'}, {path: 'p0'}],
-        diffs
-      );
+      await element.renderInOrder(['p2', 'p1', 'p0'], diffs);
       await element.updateComplete;
       assert.isFalse(reviewStub.called);
     });
@@ -1574,7 +1568,7 @@
         },
         // eslint-disable-next-line @typescript-eslint/no-explicit-any
       ] as any;
-      await element.renderInOrder([{path: 'p2'}], diffs);
+      await element.renderInOrder(['p2'], diffs);
       await element.updateComplete;
       assert.equal(reviewStub.callCount, 1);
     });
@@ -1610,11 +1604,11 @@
         // eslint-disable-next-line @typescript-eslint/no-explicit-any
       ] as any;
 
-      await element.renderInOrder([{path: 'p'}], diffs);
+      await element.renderInOrder(['p'], diffs);
       await element.updateComplete;
       assert.isFalse(reviewStub.called);
       delete element.diffPrefs.manual_review;
-      await element.renderInOrder([{path: 'p'}], diffs);
+      await element.renderInOrder(['p'], diffs);
       await element.updateComplete;
       // Wait for renderInOrder to finish
       await waitEventLoop();
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.ts b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.ts
index 03c79a8..65fc242 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.ts
@@ -22,6 +22,7 @@
   VotingRangeInfo,
 } from '../../../types/common';
 import {GrMessage, MessageAnchorTapDetail} from '../gr-message/gr-message';
+import {isServiceUser} from '../../../utils/account-util';
 import {getVotingRange} from '../../../utils/label-util';
 import {
   FormattedReviewerUpdateInfo,
@@ -225,10 +226,28 @@
  * Autogenerated messages are unimportant, if there is a message with the same
  * tag and a higher revision number.
  */
+function isReviewerUpdateMessage(message: CombinedMessage): boolean {
+  return (
+    (message as FormattedReviewerUpdateInfo).type === 'REVIEWER_UPDATE' ||
+    message.tag === MessageTag.TAG_REVIEWER_UPDATE
+  );
+}
+
 function computeIsImportant(
   message: CombinedMessage,
   allMessages: CombinedMessage[]
 ) {
+  const author = message.author;
+  const realAuthor =
+    (message as ChangeMessageInfo).real_author ??
+    (message as FormattedReviewerUpdateInfo).realAuthor;
+  if (
+    (isServiceUser(author) || isServiceUser(realAuthor)) &&
+    isReviewerUpdateMessage(message)
+  ) {
+    return false;
+  }
+
   if (!message.tag) return true;
 
   const hasSameTag = (m: CombinedMessage) => m.tag === message.tag;
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts
index 66a1951..8ec1bde 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.ts
@@ -7,7 +7,7 @@
 import '../../../test/common-test-setup';
 import './gr-messages-list';
 import {CombinedMessage, GrMessagesList, TEST_ONLY} from './gr-messages-list';
-import {MessageTag} from '../../../constants/constants';
+import {AccountTag, MessageTag} from '../../../constants/constants';
 import {
   query,
   queryAll,
@@ -59,7 +59,7 @@
   };
 };
 
-const randomMessage = function (params?: ChangeMessageInfo) {
+const randomMessage = function (params?: Partial<ChangeMessageInfo>) {
   params = params || ({} as ChangeMessageInfo);
   const author1 = {
     _account_id: 1115495 as AccountId,
@@ -488,6 +488,68 @@
       assert.isFalse(TEST_ONLY.computeIsImportant(m3, [m1, m2, m3]));
     });
 
+    test('isImportant service user reviewer update vs other messages', () => {
+      const reviewerUpdateFromBot = {
+        ...randomMessage(),
+        author: {
+          _account_id: 123 as AccountId,
+          tags: [AccountTag.SERVICE_USER],
+        },
+        type: 'REVIEWER_UPDATE' as const,
+        tag: MessageTag.TAG_REVIEWER_UPDATE as ReviewInputTag,
+      };
+      const reviewerUpdateWithRealAuthorBot = {
+        ...randomMessage(),
+        author: {
+          _account_id: 456 as AccountId,
+        },
+        real_author: {
+          _account_id: 123 as AccountId,
+          tags: [AccountTag.SERVICE_USER],
+        },
+        type: 'REVIEWER_UPDATE' as const,
+        tag: MessageTag.TAG_REVIEWER_UPDATE as ReviewInputTag,
+      };
+      const formattedReviewerUpdateWithRealAuthorBot = {
+        author: {
+          _account_id: 456 as AccountId,
+        },
+        realAuthor: {
+          _account_id: 123 as AccountId,
+          tags: [AccountTag.SERVICE_USER],
+        },
+        date: '2020-01-01 00:00:00.000000000' as Timestamp,
+        type: 'REVIEWER_UPDATE' as const,
+        tag: MessageTag.TAG_REVIEWER_UPDATE as const,
+        updates: [],
+      };
+      const commentFromBot = randomMessage({
+        author: {
+          _account_id: 123 as AccountId,
+          tags: [AccountTag.SERVICE_USER],
+        },
+        message: 'Build succeeded: 10 tests passed',
+      });
+      assert.isFalse(
+        TEST_ONLY.computeIsImportant(reviewerUpdateFromBot, [
+          reviewerUpdateFromBot,
+        ])
+      );
+      assert.isFalse(
+        TEST_ONLY.computeIsImportant(reviewerUpdateWithRealAuthorBot, [
+          reviewerUpdateWithRealAuthorBot,
+        ])
+      );
+      assert.isFalse(
+        TEST_ONLY.computeIsImportant(formattedReviewerUpdateWithRealAuthorBot, [
+          formattedReviewerUpdateWithRealAuthorBot,
+        ])
+      );
+      assert.isTrue(
+        TEST_ONLY.computeIsImportant(commentFromBot, [commentFromBot])
+      );
+    });
+
     test('isImportant is evaluated after tag update', async () => {
       const m1 = randomMessage({
         ...randomMessage(),
diff --git a/polygerrit-ui/app/elements/chat-panel/gemini-message_test.ts b/polygerrit-ui/app/elements/chat-panel/gemini-message_test.ts
index 2c7ae69..5618b06 100644
--- a/polygerrit-ui/app/elements/chat-panel/gemini-message_test.ts
+++ b/polygerrit-ui/app/elements/chat-panel/gemini-message_test.ts
@@ -26,7 +26,7 @@
 import {chatProvider, createChange} from '../../test/test-data-generators';
 import {ParsedChangeInfo} from '../../types/types';
 import {CommentsModel} from '../../models/comments/comments-model';
-import {AiAgentEventDetails, Interaction} from '../../constants/reporting';
+import {AiAgentChatEventDetails, Interaction} from '../../constants/reporting';
 import {getAppContext} from '../../services/app-context';
 
 suite('gemini-message tests', () => {
@@ -260,7 +260,7 @@
       reportStub.firstCall.args[0],
       Interaction.AI_AGENT_SUGGESTIONS_SHOWN
     );
-    const details = reportStub.firstCall.args[1] as AiAgentEventDetails;
+    const details = reportStub.firstCall.args[1] as AiAgentChatEventDetails;
     assert.equal(details.conversationId, 'test-conversation-id');
     assert.equal(details.agentId, 'custom-agent-id');
     assert.equal(details.commentCount, 1);
@@ -308,7 +308,7 @@
       .find(c => c.args[0] === Interaction.AI_AGENT_SUGGESTION_TO_COMMENT);
     assert.isOk(call, 'Expected AI_AGENT_SUGGESTION_TO_COMMENT to be reported');
 
-    const details = call.args[1] as AiAgentEventDetails;
+    const details = call.args[1] as AiAgentChatEventDetails;
     assert.equal(details.conversationId, 'test-conversation-id');
     assert.equal(details.agentId, 'custom-agent-id');
     assert.equal(details.commentId, 'test-comment-id');
@@ -352,7 +352,7 @@
       'Expected AI_AGENT_SUGGESTION_COPY_BUTTON_CLICKED to be reported'
     );
 
-    const details = call.args[1] as AiAgentEventDetails;
+    const details = call.args[1] as AiAgentChatEventDetails;
     assert.equal(details.conversationId, 'test-conversation-id');
     assert.equal(details.agentId, 'custom-agent-id');
   });
diff --git a/polygerrit-ui/app/elements/chat-panel/splash-page.ts b/polygerrit-ui/app/elements/chat-panel/splash-page.ts
index f44c838..dc69086 100644
--- a/polygerrit-ui/app/elements/chat-panel/splash-page.ts
+++ b/polygerrit-ui/app/elements/chat-panel/splash-page.ts
@@ -10,6 +10,7 @@
 import '@material/web/progress/circular-progress.js';
 import './gemini-message';
 import './splash-page-action';
+import '../plugins/gr-endpoint-decorator/gr-endpoint-decorator';
 
 import {css, html, LitElement} from 'lit';
 import {customElement, property, state} from 'lit/decorators.js';
@@ -20,6 +21,12 @@
 import {chatModelToken, Turn} from '../../models/chat/chat-model';
 import {resolve} from '../../models/dependency';
 import {userModelToken} from '../../models/user/user-model';
+import {
+  CheckRun,
+  checksModelToken,
+  RunResult,
+} from '../../models/checks/checks-model';
+import {changeModelToken} from '../../models/change/change-model';
 import {AccountDetailInfo, ServerInfo} from '../../types/common';
 import {subscribe} from '../lit/subscription-controller';
 import {getDisplayName} from '../../utils/display-name-util';
@@ -46,10 +53,20 @@
 
   @property({type: Boolean}) isChangePrivate = false;
 
+  @state() runs: readonly CheckRun[] = [];
+
+  @state() results: readonly RunResult[] = [];
+
+  @state() changeNum?: number;
+
   private readonly getChatModel = resolve(this, chatModelToken);
 
   private readonly getUserModel = resolve(this, userModelToken);
 
+  private readonly getChecksModel = resolve(this, checksModelToken);
+
+  private readonly getChangeModel = resolve(this, changeModelToken);
+
   constructor() {
     super();
     subscribe(
@@ -87,6 +104,21 @@
       () => this.getUserModel().account$,
       x => (this.account = x)
     );
+    subscribe(
+      this,
+      () => this.getChecksModel().allRunsSelectedPatchset$,
+      x => (this.runs = x ?? [])
+    );
+    subscribe(
+      this,
+      () => this.getChecksModel().allResultsSelected$,
+      x => (this.results = x ?? [])
+    );
+    subscribe(
+      this,
+      () => this.getChangeModel().changeNum$,
+      x => (this.changeNum = x)
+    );
   }
 
   private get currentTurn(): Turn | undefined {
@@ -255,8 +287,19 @@
       `;
     }
     return html`
-      ${this.renderBackgroundRequest()} ${this.renderCustomActions()}
-      ${this.renderActions()}
+      ${this.renderBackgroundRequest()}
+      <gr-endpoint-decorator name="chat-panel-splash-extra">
+        <gr-endpoint-param name="runs" .value=${this.runs}></gr-endpoint-param>
+        <gr-endpoint-param
+          name="results"
+          .value=${this.results}
+        ></gr-endpoint-param>
+        <gr-endpoint-param
+          name="changeNum"
+          .value=${this.changeNum}
+        ></gr-endpoint-param>
+      </gr-endpoint-decorator>
+      ${this.renderCustomActions()} ${this.renderActions()}
     `;
   }
 
diff --git a/polygerrit-ui/app/elements/chat-panel/splash-page_test.ts b/polygerrit-ui/app/elements/chat-panel/splash-page_test.ts
index 825eb01..28099c2 100644
--- a/polygerrit-ui/app/elements/chat-panel/splash-page_test.ts
+++ b/polygerrit-ui/app/elements/chat-panel/splash-page_test.ts
@@ -53,6 +53,11 @@
         <div class="splash-container">
           <h1 class="splash-greeting">Hello,</h1>
           <p class="splash-question">How can I help you today?</p>
+          <gr-endpoint-decorator name="chat-panel-splash-extra">
+            <gr-endpoint-param name="runs"></gr-endpoint-param>
+            <gr-endpoint-param name="results"></gr-endpoint-param>
+            <gr-endpoint-param name="changeNum"></gr-endpoint-param>
+          </gr-endpoint-decorator>
           <div class="action-container-title suggested-actions-title">
             Capabilities
           </div>
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-chip-for-label.ts b/polygerrit-ui/app/elements/checks/gr-checks-chip-for-label.ts
index ddf37ab..74af4d8 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-chip-for-label.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-chip-for-label.ts
@@ -75,10 +75,12 @@
     if (runs.length === 1 && runs[0].statusLink) {
       links.push(runs[0].statusLink);
     }
+    const isAi = runs.some(run => run.isAiPowered);
     return html`<gr-checks-chip
       .text=${`${runsCount}`}
       .links=${links}
       .statusOrCategory=${category}
+      .isAi=${isAi}
       @click=${() => {
         fireShowTab(this, Tab.CHECKS, false, {
           checksTab: {
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-chip-for-label_test.ts b/polygerrit-ui/app/elements/checks/gr-checks-chip-for-label_test.ts
index cac448a..641f2f8 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-chip-for-label_test.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-chip-for-label_test.ts
@@ -58,4 +58,22 @@
       </div>`
     );
   });
+
+  test('sets isAi to true when check run is AI powered', async () => {
+    element.runs = [
+      createRun({
+        labelName: 'Verified',
+        isAiPowered: true,
+        results: [
+          createCheckResult({
+            category: Category.ERROR,
+          }),
+        ],
+      }),
+    ];
+    await element.updateComplete;
+
+    const checksChip = queryAndAssert<GrChecksChip>(element, 'gr-checks-chip');
+    assert.isTrue(checksChip.isAi);
+  });
 });
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
index a9a4147..15f4bd4 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
@@ -228,7 +228,20 @@
     );
   }
 
+  override willUpdate(changedProperties: PropertyValues) {
+    super.willUpdate(changedProperties);
+    if (changedProperties.has('run') && this.run) {
+      if (
+        this.run.status === RunStatus.RUNNING ||
+        this.run.status === RunStatus.SCHEDULED
+      ) {
+        this.shouldRender = true;
+      }
+    }
+  }
+
   override firstUpdated() {
+    if (this.shouldRender) return;
     assertIsDefined(this.chipElement, 'chip element');
     whenVisible(this.chipElement, () => (this.shouldRender = true), 200);
   }
@@ -346,8 +359,10 @@
     if (this.run.status !== RunStatus.RUNNING) return;
     if (!this.run.finishedTimestamp) return;
     const now = new Date();
-    if (this.run.finishedTimestamp.getTime() < now.getTime()) return;
-    const eta = durationString(new Date(), this.run.finishedTimestamp);
+    const finished = new Date(this.run.finishedTimestamp);
+    if (isNaN(finished.getTime())) return;
+    if (finished.getTime() < now.getTime()) return;
+    const eta = durationString(new Date(), finished);
     return html`<span class="eta">ETA: ${eta}</span>`;
   }
 
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-runs_test.ts b/polygerrit-ui/app/elements/checks/gr-checks-runs_test.ts
index 88a1b0b..7fe60a9 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-runs_test.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-runs_test.ts
@@ -199,6 +199,29 @@
     );
   });
 
+  test('renders running check immediately', async () => {
+    element.run = {
+      ...checkRun0,
+      status: RunStatus.RUNNING,
+    };
+    await element.updateComplete;
+    assert.isTrue(element.shouldRender);
+  });
+
+  test('renders running check with number finishedTimestamp without crashing', async () => {
+    element.run = {
+      ...checkRun0,
+      status: RunStatus.RUNNING,
+      // 10 seconds in the future
+      finishedTimestamp: (new Date().getTime() + 10000) as unknown as Date,
+    };
+    await element.updateComplete;
+    assert.isTrue(element.shouldRender);
+    const eta = element.shadowRoot?.querySelector('.eta');
+    assert.isOk(eta);
+    assert.include(eta?.textContent, 'ETA:');
+  });
+
   test('renders checkRun0', async () => {
     element.shouldRender = true;
     element.run = checkRun0;
diff --git a/polygerrit-ui/app/elements/checks/gr-diff-check-result_test.ts b/polygerrit-ui/app/elements/checks/gr-diff-check-result_test.ts
index c28df76..71085f2 100644
--- a/polygerrit-ui/app/elements/checks/gr-diff-check-result_test.ts
+++ b/polygerrit-ui/app/elements/checks/gr-diff-check-result_test.ts
@@ -155,6 +155,7 @@
         category: 'ERROR',
         summary: 'Test Summary',
         message: 'Test Message',
+        isAiPowered: true,
         externalId: JSON.stringify({
           agentId: 'test-agent',
           conversationId: 'test-conv',
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
index bfdde07..a9284c5 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
@@ -29,7 +29,11 @@
 import {subscribe} from '../../lit/subscription-controller';
 import {assert} from '../../../utils/common-util';
 import {resolve} from '../../../models/dependency';
-import {createChangeUrl} from '../../../models/views/change';
+import {
+  changeViewModelToken,
+  createApplyFixUrl,
+} from '../../../models/views/change';
+
 import {GrDialog} from '../../shared/gr-dialog/gr-dialog';
 import {userModelToken} from '../../../models/user/user-model';
 import {modalStyles} from '../../../styles/gr-modal-styles';
@@ -114,6 +118,8 @@
 
   private readonly getNavigation = resolve(this, navigationToken);
 
+  private readonly getViewModel = resolve(this, changeViewModelToken);
+
   private readonly reporting = getAppContext().reportingService;
 
   private readonly syntaxLayer = new GrSyntaxLayerWorker(
@@ -481,16 +487,22 @@
       });
     }
     if (res?.ok) {
+      const currentChildView = this.getViewModel().getState()?.childView;
+      const filePath =
+        fixSuggestion.replacements[0]?.path ??
+        this.currentPreviews[0]?.filepath;
       this.getNavigation().setUrl(
-        createChangeUrl({
+        createApplyFixUrl({
           change,
-          patchNum: EDIT,
           basePatchNum: patchNum as BasePatchSetNum,
           forceReload: !this.hasEdit,
+          filePath,
+          currentChildView,
         })
       );
       this.close(true);
     }
+
     this.isApplyFixLoading = false;
   }
 }
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts
index 1d35430..21ce8ed 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts
@@ -14,12 +14,15 @@
 import {GrApplyFixDialog} from './gr-apply-fix-dialog';
 import {PatchSetNum, PatchSetNumber} from '../../../types/common';
 import {
+  createChangeViewState,
+  createDiffViewState,
   createFixSuggestionInfo,
   createParsedChange,
   createRange,
   createRevisions,
   getCurrentRevision,
 } from '../../../test/test-data-generators';
+import {changeViewModelToken} from '../../../models/views/change';
 import {createDefaultDiffPrefs} from '../../../constants/constants';
 import {OpenFixPreviewEventDetail} from '../../../types/events';
 import {GrButton} from '../../shared/gr-button/gr-button';
@@ -227,4 +230,70 @@
       '/c/test-project/+/42/2..edit?forceReload=true'
     );
   });
+
+  suite('handleApplyFix navigation', () => {
+    setup(() => {
+      stubRestApi('applyFixSuggestion').returns(
+        Promise.resolve(new Response(null, {status: 200}))
+      );
+    });
+
+    test('navigates to createDiffUrl when in Diff View', async () => {
+      testResolver(changeViewModelToken).setState(createDiffViewState());
+      const fixDetail: OpenFixPreviewEventDetail = {
+        patchNum: 2 as PatchSetNum,
+        fixSuggestions: [
+          {
+            ...createFixSuggestionInfo('fix_1'),
+            replacements: [
+              {
+                path: 'file1.txt',
+                replacement: 'new content',
+                range: createRange(),
+              },
+            ],
+          },
+        ],
+        onCloseFixPreviewCallbacks: [],
+      };
+      await open(fixDetail);
+
+      await element.handleApplyFix(new CustomEvent('confirm'));
+
+      assert.isTrue(setUrlStub.calledOnce);
+      assert.equal(
+        setUrlStub.lastCall.firstArg,
+        '/c/test-project/+/42/2..edit/file1.txt?forceReload=true'
+      );
+    });
+
+    test('navigates to createChangeUrl when in Change View', async () => {
+      testResolver(changeViewModelToken).setState(createChangeViewState());
+      const fixDetail: OpenFixPreviewEventDetail = {
+        patchNum: 2 as PatchSetNum,
+        fixSuggestions: [
+          {
+            ...createFixSuggestionInfo('fix_1'),
+            replacements: [
+              {
+                path: 'file1.txt',
+                replacement: 'new content',
+                range: createRange(),
+              },
+            ],
+          },
+        ],
+        onCloseFixPreviewCallbacks: [],
+      };
+      await open(fixDetail);
+
+      await element.handleApplyFix(new CustomEvent('confirm'));
+
+      assert.isTrue(setUrlStub.calledOnce);
+      assert.equal(
+        setUrlStub.lastCall.firstArg,
+        '/c/test-project/+/42/2..edit?forceReload=true'
+      );
+    });
+  });
 });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
index 907c764..0a6538b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
@@ -35,6 +35,7 @@
   PARENT,
   PatchRange,
   PatchSetNum,
+  PreferencesInfo,
   RepoName,
   RevisionPatchSetNum,
 } from '../../../types/common';
@@ -71,6 +72,7 @@
   RangeSelectedEventDetail,
   RenderPreferences,
 } from '../../../api/diff';
+import {DiffDetails} from '../../../api/annotation';
 import {resolve} from '../../../models/dependency';
 import {browserModelToken} from '../../../models/browser/browser-model';
 import {commentsModelToken} from '../../../models/comments/comments-model';
@@ -287,6 +289,10 @@
   @state()
   private layers: DiffLayer[] = [];
 
+  private layersComputedWithPlugins = false;
+
+  private enableTokenHighlight?: boolean;
+
   @state()
   private renderPrefs: RenderPreferences = {
     num_lines_rendered_at_once: 128,
@@ -376,6 +382,15 @@
         this.prefs = diffPreferences;
       }
     );
+    subscribe(
+      this,
+      () => this.getPluginLoader().pluginsModel.pluginsLoaded$,
+      async pluginsLoaded => {
+        if (pluginsLoaded) {
+          await this.computeLayersWithPlugins();
+        }
+      }
+    );
   }
 
   override connectedCallback() {
@@ -446,6 +461,18 @@
     }
   }
 
+  override updated(changedProperties: PropertyValues) {
+    super.updated(changedProperties);
+    const pathChanged = changedProperties.has('path');
+    const diffElementChanged =
+      this._layersComputedForDiffElement !== this.diffElement;
+    const pluginsLoaded =
+      this.getPluginLoader().pluginsModel.getState().pluginsLoaded;
+    if ((pathChanged || diffElementChanged) && pluginsLoaded) {
+      this.computeLayersWithPlugins();
+    }
+  }
+
   async waitForReloadToRender(): Promise<void> {
     await this.updateComplete;
     if (this.reloadPromise) {
@@ -523,13 +550,45 @@
     );
   }
 
+  private _layersComputedForPath?: string;
+
+  private _layersComputedForDiffElement?: GrDiff;
+
+  private calculateEnableTokenHighlight(prefs?: PreferencesInfo): boolean {
+    return !prefs?.disable_token_highlighting;
+  }
+
+  private async computeLayersWithPlugins(): Promise<void> {
+    if (
+      this._layersComputedForPath !== this.path ||
+      this._layersComputedForDiffElement !== this.diffElement
+    ) {
+      this.layersComputedWithPlugins = false;
+      this._layersComputedForPath = this.path;
+      this._layersComputedForDiffElement = this.diffElement;
+    }
+
+    if (!this.path || !this.diffElement || this.layersComputedWithPlugins) {
+      return;
+    }
+    if (this.enableTokenHighlight === undefined) {
+      const prefs = await this.restApiService.getPreferences();
+      this.enableTokenHighlight = this.calculateEnableTokenHighlight(prefs);
+    }
+    this.layers = this.getLayers(this.enableTokenHighlight);
+    this.layersComputedWithPlugins = true;
+  }
+
   async initLayers() {
     const preferencesPromise = this.restApiService.getPreferences();
     const prefs = await preferencesPromise;
-    const enableTokenHighlight = !prefs?.disable_token_highlighting;
+    this.enableTokenHighlight = this.calculateEnableTokenHighlight(prefs);
 
     assertIsDefined(this.path, 'path');
-    this.layers = this.getLayers(enableTokenHighlight);
+    this.layers = this.getLayers(this.enableTokenHighlight);
+    this.layersComputedWithPlugins =
+      !!this.diffElement &&
+      this.getPluginLoader().pluginsModel.getState().pluginsLoaded;
     this.coverageRanges = [];
     // We kick off fetching the data here, but we don't return the promise,
     // so awaiting initLayers() will not wait for coverage data to be
@@ -668,6 +727,26 @@
     };
   }
 
+  private get diffDetails(): DiffDetails | undefined {
+    if (
+      !this.change ||
+      !this.patchRange ||
+      !this.file ||
+      !this.path ||
+      !this.diffElement
+    ) {
+      return undefined;
+    }
+    return {
+      change: this.change,
+      basePatchNum: this.patchRange.basePatchNum,
+      patchNum: this.patchRange.patchNum,
+      fileRange: this.file,
+      path: this.path,
+      diffElement: this.diffElement,
+    };
+  }
+
   private getLayers(enableTokenHighlight: boolean): DiffLayer[] {
     const layers = [];
     if (enableTokenHighlight) {
@@ -690,12 +769,29 @@
         })
       );
     }
+    // Add layers from plugins
+    const details = this.diffDetails;
+    if (details) {
+      for (const plugin of this.getPluginLoader().pluginsModel.getState()
+        .diffLayerPlugins) {
+        try {
+          layers.push(plugin.factory(details));
+        } catch (e) {
+          console.error(
+            `Error creating diff layer from plugin ${plugin.pluginName}:`,
+            e
+          );
+        }
+      }
+    }
     layers.push(this.syntaxLayer);
     return layers;
   }
 
   clear() {
     this.layers = [];
+    this.layersComputedWithPlugins = false;
+    this.enableTokenHighlight = undefined;
   }
 
   /**
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts
index 00a627b..958d2fb 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts
@@ -1474,4 +1474,75 @@
       });
     });
   });
+
+  suite('computeLayersWithPlugins and token highlighting', () => {
+    let getPreferencesStub: sinon.SinonStub;
+    setup(() => {
+      getPreferencesStub = stubRestApi('getPreferences');
+    });
+    test('initLayers sets enableTokenHighlight correctly', async () => {
+      getPreferencesStub.returns(
+        Promise.resolve({disable_token_highlighting: true})
+      );
+      await element.initLayers();
+      assert.isFalse(element['enableTokenHighlight']);
+      getPreferencesStub.returns(
+        Promise.resolve({disable_token_highlighting: false})
+      );
+      await element.initLayers();
+      assert.isTrue(element['enableTokenHighlight']);
+      getPreferencesStub.returns(Promise.resolve(undefined));
+      await element.initLayers();
+      assert.isTrue(element['enableTokenHighlight']);
+    });
+    test('clear resets enableTokenHighlight', async () => {
+      getPreferencesStub.returns(
+        Promise.resolve({disable_token_highlighting: true})
+      );
+      await element.initLayers();
+      assert.isFalse(element['enableTokenHighlight']);
+      element.clear();
+      assert.isUndefined(element['enableTokenHighlight']);
+    });
+    test('computeLayersWithPlugins sets enableTokenHighlight if undefined', async () => {
+      getPreferencesStub.returns(
+        Promise.resolve({disable_token_highlighting: true})
+      );
+      element['enableTokenHighlight'] = undefined;
+      element['layersComputedWithPlugins'] = false;
+
+      await element['computeLayersWithPlugins']();
+
+      assert.isFalse(element['enableTokenHighlight']);
+      assert.isTrue(element['layersComputedWithPlugins']);
+      assert.isTrue(getPreferencesStub.calledOnce);
+    });
+    test('computeLayersWithPlugins does not fetch preferences if enableTokenHighlight is already set', async () => {
+      getPreferencesStub.returns(
+        Promise.resolve({disable_token_highlighting: true})
+      );
+      element['enableTokenHighlight'] = true;
+      element['layersComputedWithPlugins'] = false;
+
+      await element['computeLayersWithPlugins']();
+
+      assert.isTrue(element['enableTokenHighlight']);
+      assert.isTrue(element['layersComputedWithPlugins']);
+      assert.isFalse(getPreferencesStub.called);
+    });
+    test('pluginsLoaded triggers computeLayersWithPlugins', async () => {
+      // @ts-expect-error
+      const computeSpy = sinon.spy(element, 'computeLayersWithPlugins');
+
+      const pluginsModel = testResolver(pluginLoaderToken).pluginsModel;
+      pluginsModel.updateState({pluginsLoaded: false});
+      await element.updateComplete;
+
+      pluginsModel.updateState({pluginsLoaded: true});
+
+      await new Promise(resolve => setTimeout(resolve, 0));
+
+      assert.isTrue(computeSpy.called);
+    });
+  });
 });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
index 93bf666..ed56ad3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
@@ -89,6 +89,7 @@
 import {classMap} from 'lit/directives/class-map.js';
 import {when} from 'lit/directives/when.js';
 import {styleMap} from 'lit/directives/style-map.js';
+import {keyed} from 'lit/directives/keyed.js';
 import {
   ChangeChildView,
   changeViewModelToken,
@@ -1027,70 +1028,74 @@
     // Always renders the 0x0px .sidebarAnchor div for scroll measurements.
     return html`
       <div class="sidebarAnchor">
-        ${when(
-          this.shownSidebar !== undefined,
-          () => html`
-            <div
-              class="sidebarContents"
-              style=${styleMap({height: `${this.sidebarHeight}px`})}
-            >
-              <gr-endpoint-decorator
-                name=${`sidebarContent-${this.shownSidebar}`}
+        ${when(this.shownSidebar !== undefined, () =>
+          keyed(
+            this.shownSidebar,
+            html`
+              <div
+                class="sidebarContents"
+                style=${styleMap({height: `${this.sidebarHeight}px`})}
               >
-                <gr-endpoint-param
-                  name="change"
-                  .value=${this.change}
-                ></gr-endpoint-param>
-                <gr-endpoint-param
-                  name="path"
-                  .value=${this.path}
-                ></gr-endpoint-param>
-                <!-- current diff path and, in case of rename, previous path -->
-                <gr-endpoint-param
-                  name="fileRange"
-                  .value=${this.getFileRange()}
-                ></gr-endpoint-param>
-                <gr-endpoint-param
-                  name="basePatchNum"
-                  .value=${this.basePatchNum}
-                ></gr-endpoint-param>
-                <gr-endpoint-param
-                  name="patchNum"
-                  .value=${this.patchNum}
-                ></gr-endpoint-param>
-                <gr-endpoint-param
-                  name="content"
-                  .value=${this.diff}
-                ></gr-endpoint-param>
-                <gr-endpoint-param
-                  name="cursor"
-                  .value=${this.cursor}
-                ></gr-endpoint-param>
-                <gr-endpoint-param
-                  name="diff"
-                  .value=${this.diffHost?.diffElement}
-                ></gr-endpoint-param>
-                <gr-endpoint-param
-                  name="comments"
-                  .value=${this.commentsForPath}
-                ></gr-endpoint-param>
-                <gr-endpoint-param
-                  name="onClose"
-                  .value=${(pluginName: string) => {
-                    // Only close the sidebar if that particular sidebar is
-                    // still open. An async onClose callback should not close a
-                    // different sidebar.
-                    if (this.shownSidebar !== pluginName) return;
-                    this.shownSidebar = undefined;
-                    this.getUserModel().updatePreferences({
-                      diff_page_sidebar: 'NONE',
-                    });
-                  }}
+                <gr-endpoint-decorator
+                  name=${`sidebarContent-${this.shownSidebar}`}
                 >
-                </gr-endpoint-param>
-              </gr-endpoint-decorator>
-            </div>
-          `
+                  <gr-endpoint-param
+                    name="change"
+                    .value=${this.change}
+                  ></gr-endpoint-param>
+                  <gr-endpoint-param
+                    name="path"
+                    .value=${this.path}
+                  ></gr-endpoint-param>
+                  <!-- current diff path and, in case of rename, previous path -->
+                  <gr-endpoint-param
+                    name="fileRange"
+                    .value=${this.getFileRange()}
+                  ></gr-endpoint-param>
+                  <gr-endpoint-param
+                    name="basePatchNum"
+                    .value=${this.basePatchNum}
+                  ></gr-endpoint-param>
+                  <gr-endpoint-param
+                    name="patchNum"
+                    .value=${this.patchNum}
+                  ></gr-endpoint-param>
+                  <gr-endpoint-param
+                    name="content"
+                    .value=${this.diff}
+                  ></gr-endpoint-param>
+                  <gr-endpoint-param
+                    name="cursor"
+                    .value=${this.cursor}
+                  ></gr-endpoint-param>
+                  <gr-endpoint-param
+                    name="diff"
+                    .value=${this.diffHost?.diffElement}
+                  ></gr-endpoint-param>
+                  <gr-endpoint-param
+                    name="comments"
+                    .value=${this.commentsForPath}
+                  ></gr-endpoint-param>
+                  <gr-endpoint-param
+                    name="onClose"
+                    .value=${(pluginName: string) => {
+                      // Only close the sidebar if that particular sidebar is
+                      // still open. An async onClose callback should not close a
+                      // different sidebar.
+                      if (this.shownSidebar !== pluginName) {
+                        return;
+                      }
+                      this.shownSidebar = undefined;
+                      this.getUserModel().updatePreferences({
+                        diff_page_sidebar: 'NONE',
+                      });
+                    }}
+                  >
+                  </gr-endpoint-param>
+                </gr-endpoint-decorator>
+              </div>
+            `
+          )
         )}
       </div>
     `;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
index b66542e..d1e2071 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
@@ -2261,5 +2261,34 @@
         'Button should be hidden for image diffs'
       );
     });
+
+    suite('sidebar', () => {
+      test('switching sidebars dismounts old component and mounts new one', async () => {
+        // @ts-expect-error: accessing private property shownSidebar for testing
+        element.shownSidebar = 'sidebar-a';
+        await element.updateComplete;
+        const oldDecorator = element.shadowRoot?.querySelector(
+          '.sidebarContents gr-endpoint-decorator'
+        );
+        assert.isNotNull(oldDecorator);
+        assert.equal(
+          oldDecorator?.getAttribute('name'),
+          'sidebarContent-sidebar-a'
+        );
+
+        // @ts-expect-error: accessing private property shownSidebar for testing
+        element.shownSidebar = 'sidebar-b';
+        await element.updateComplete;
+        const newDecorator = element.shadowRoot?.querySelector(
+          '.sidebarContents gr-endpoint-decorator'
+        );
+        assert.isNotNull(newDecorator);
+        assert.equal(
+          newDecorator?.getAttribute('name'),
+          'sidebarContent-sidebar-b'
+        );
+        assert.notEqual(oldDecorator, newDecorator);
+      });
+    });
   });
 });
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
index 7184e00..778a17c 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
@@ -6,6 +6,7 @@
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator';
 import '../../plugins/gr-endpoint-param/gr-endpoint-param';
 import '../../shared/gr-button/gr-button';
+import '../../shared/gr-dialog/gr-dialog';
 import '../../shared/gr-editable-label/gr-editable-label';
 import '../../shared/gr-tooltip-content/gr-tooltip-content';
 import '../gr-default-editor/gr-default-editor';
@@ -28,6 +29,7 @@
 import {changeIsAbandoned, changeIsMerged} from '../../../utils/change-util';
 import {Modifier} from '../../../utils/dom-util';
 import {sharedStyles} from '../../../styles/shared-styles';
+import {modalStyles} from '../../../styles/gr-modal-styles';
 import {css, html, LitElement, nothing, PropertyValues} from 'lit';
 import {customElement, query, state} from 'lit/decorators.js';
 import {subscribe} from '../../lit/subscription-controller';
@@ -66,6 +68,9 @@
   @query('#editPreferencesDialog')
   editPreferencesDialog?: GrEditPreferencesDialog;
 
+  @query('#confirmCloseModal')
+  private confirmCloseModal?: HTMLDialogElement;
+
   @state() viewState?: ChangeViewState;
 
   // private but used in test
@@ -171,6 +176,7 @@
   static override get styles() {
     return [
       sharedStyles,
+      modalStyles,
       css`
         :host {
           background-color: var(--view-background-color);
@@ -291,6 +297,18 @@
         @has-edit-pref-change-saved=${this.handleEditPrefChangeSaved}
       >
       </gr-edit-preferences-dialog>
+      <dialog id="confirmCloseModal" tabindex="-1">
+        <gr-dialog
+          id="confirmCloseDialog"
+          confirm-label="Discard"
+          cancel-label="Keep Editing"
+          @confirm=${this.handleConfirmClose}
+          @cancel=${this.handleCancelClose}
+        >
+          <div class="header" slot="header">Discard unsaved changes?</div>
+          <div class="main" slot="main">Your modifications will be lost.</div>
+        </gr-dialog>
+      </dialog>
     `;
   }
 
@@ -507,8 +525,11 @@
 
   // private but used in test
   handleCloseTap = () => {
-    // TODO(kaspern): Add a confirm dialog if there are unsaved changes.
-    this.viewEditInChangeView();
+    if ((this.content ?? '') !== this.newContent) {
+      this.confirmCloseModal?.showModal();
+    } else {
+      this.viewEditInChangeView();
+    }
   };
 
   private handleSaveTap = () => {
@@ -610,6 +631,16 @@
     // We have to fire a reload so the change takes effect within a plugin.
     fireReload(this);
   }
+
+  private handleConfirmClose = () => {
+    this.getStorage().eraseEditableContentItem(this.storageKey);
+    this.confirmCloseModal?.close();
+    this.viewEditInChangeView();
+  };
+
+  private handleCancelClose = () => {
+    this.confirmCloseModal?.close();
+  };
 }
 
 declare global {
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_screenshot_test.ts b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_screenshot_test.ts
new file mode 100644
index 0000000..ec1bd6a
--- /dev/null
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_screenshot_test.ts
@@ -0,0 +1,61 @@
+/**
+ * @license
+ * Copyright 2026 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import '../../../test/common-test-setup';
+import './gr-editor-view';
+import {GrEditorView} from './gr-editor-view';
+import {fixture, html} from '@open-wc/testing';
+// Until https://github.com/modernweb-dev/web/issues/2804 is fixed
+// @ts-ignore
+import {visualDiff} from '@web/test-runner-visual-regression';
+import {
+  query,
+  stubRestApi,
+  visualDiffDarkTheme,
+} from '../../../test/test-utils';
+import {createEditViewState} from '../../../test/test-data-generators';
+import {NumericChangeId, RevisionPatchSetNum} from '../../../types/common';
+import {GrButton} from '../../shared/gr-button/gr-button';
+
+suite('gr-editor-view screenshot tests', () => {
+  let element: GrEditorView;
+
+  setup(async () => {
+    stubRestApi('getFileContent').resolves({
+      ok: true,
+      type: 'text/javascript',
+      content: 'original content',
+    });
+    element = await fixture<GrEditorView>(
+      html`<gr-editor-view></gr-editor-view>`
+    );
+    element.viewState = {
+      ...createEditViewState(),
+      changeNum: 42 as NumericChangeId,
+      patchNum: 1 as RevisionPatchSetNum,
+      editView: {path: 'foo/bar.baz'},
+    };
+    element.latestPatchsetNumber = 1 as RevisionPatchSetNum;
+    element.content = 'original content';
+    element.newContent = 'original content';
+    await element.updateComplete;
+  });
+
+  test('editor view', async () => {
+    await visualDiff(element, 'gr-editor-view-normal');
+    await visualDiffDarkTheme(element, 'gr-editor-view-normal');
+  });
+
+  test('cancel modal open', async () => {
+    element.newContent = 'modified content';
+    await element.updateComplete;
+
+    query<GrButton>(element, '#close')!.click();
+    await element.updateComplete;
+
+    await visualDiff(element, 'gr-editor-view-cancel-modal');
+    await visualDiffDarkTheme(element, 'gr-editor-view-cancel-modal');
+  });
+});
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts
index 690180c..090c521 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts
@@ -127,6 +127,17 @@
         </div>
         <gr-edit-preferences-dialog id="editPreferencesDialog">
         </gr-edit-preferences-dialog>
+        <dialog id="confirmCloseModal" tabindex="-1">
+          <gr-dialog
+            cancel-label="Keep Editing"
+            confirm-label="Discard"
+            id="confirmCloseDialog"
+            role="dialog"
+          >
+            <div class="header" slot="header">Discard unsaved changes?</div>
+            <div class="main" slot="main">Your modifications will be lost.</div>
+          </gr-dialog>
+        </dialog>
         <div class="textareaWrapper">
           <gr-endpoint-decorator id="editorEndpoint" name="editor">
             <gr-endpoint-param name="fileContent"> </gr-endpoint-param>
@@ -216,6 +227,11 @@
     const newText = 'file text changed';
 
     setup(async () => {
+      stubRestApi('getFileContent').resolves({
+        ok: true,
+        type: 'text/javascript',
+        content: originalText,
+      });
       element.viewState = {...createEditViewState()};
       element.content = originalText;
       element.newContent = originalText;
@@ -351,17 +367,89 @@
       });
     });
 
-    test('file modification and close', async () => {
-      const closeSpy = sinon.spy(element, 'handleCloseTap');
+    test('file modification and close show dialog, then discard', async () => {
+      storageService.setEditableContentItem(
+        element.storageKey,
+        'cached content'
+      );
+      assert.equal(
+        storageService.getEditableContentItem(element.storageKey)?.message,
+        'cached content'
+      );
+
       element.newContent = newText;
       await element.updateComplete;
 
-      assert.isFalse(
-        query<GrButton>(element, '#save')!.hasAttribute('disabled')
-      );
+      const dialog = query<HTMLDialogElement>(element, '#confirmCloseModal')!;
+      assert.isFalse(dialog.hasAttribute('open'));
 
       query<GrButton>(element, '#close')!.click();
-      assert.isTrue(closeSpy.called);
+      assert.isFalse(saveFileStub.called);
+      assert.isFalse(navigateStub.called);
+      assert.isTrue(dialog.hasAttribute('open'));
+
+      query(element, '#confirmCloseDialog')!.dispatchEvent(
+        new CustomEvent('confirm')
+      );
+      await element.updateComplete;
+
+      assert.isFalse(dialog.hasAttribute('open'));
+      assert.isTrue(navigateStub.called);
+      assert.isNull(storageService.getEditableContentItem(element.storageKey));
+    });
+
+    test('file modification and close show dialog, then keep editing', async () => {
+      element.newContent = newText;
+      await element.updateComplete;
+
+      const dialog = query<HTMLDialogElement>(element, '#confirmCloseModal')!;
+      assert.isFalse(dialog.hasAttribute('open'));
+
+      query<GrButton>(element, '#close')!.click();
+      assert.isFalse(saveFileStub.called);
+      assert.isFalse(navigateStub.called);
+      assert.isTrue(dialog.hasAttribute('open'));
+
+      query(element, '#confirmCloseDialog')!.dispatchEvent(
+        new CustomEvent('cancel')
+      );
+      await element.updateComplete;
+
+      assert.isFalse(dialog.hasAttribute('open'));
+      assert.isFalse(navigateStub.called);
+    });
+
+    test('render open dialog', async () => {
+      element.newContent = newText;
+      await element.updateComplete;
+
+      query<GrButton>(element, '#close')!.click();
+      await element.updateComplete;
+
+      const dialog = query<HTMLDialogElement>(element, '#confirmCloseModal')!;
+      assert.isTrue(dialog.hasAttribute('open'));
+
+      assert.lightDom.equal(
+        dialog,
+        /* HTML */ `
+          <gr-dialog
+            cancel-label="Keep Editing"
+            confirm-label="Discard"
+            id="confirmCloseDialog"
+            role="dialog"
+          >
+            <div class="header" slot="header">Discard unsaved changes?</div>
+            <div class="main" slot="main">Your modifications will be lost.</div>
+          </gr-dialog>
+        `
+      );
+    });
+
+    test('close when content is not modified', async () => {
+      element.newContent = originalText;
+      await element.updateComplete;
+
+      query<GrButton>(element, '#close')!.click();
       assert.isFalse(saveFileStub.called);
       assert.isTrue(navigateStub.called);
     });
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
index ab60a72..3020bff 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
@@ -355,6 +355,7 @@
       sharedStyles,
       css`
         :host {
+          display: block;
           font-family: var(--font-family);
           font-size: var(--font-size-normal);
           font-weight: var(--font-weight-normal);
@@ -379,7 +380,7 @@
           padding: var(--spacing-s) var(--spacing-m);
         }
         .comment-box {
-          width: 80ch;
+          width: var(--gr-comment-thread-width, 80ch);
           max-width: 100%;
           background-color: var(--comment-background-color);
           color: var(--comment-text-color);
@@ -431,7 +432,7 @@
           border: 1px solid var(--border-color);
           flex-grow: 1;
           flex-shrink: 1;
-          max-width: 1200px;
+          max-width: var(--gr-comment-thread-diff-max-width, 1200px);
         }
         .view-diff-button {
           margin: var(--spacing-s) var(--spacing-m);
@@ -722,10 +723,19 @@
   }
 
   override firstUpdated() {
+    const lastComment = this.thread ? this.getLastComment() : undefined;
+    const isNewDraft =
+      isDraft(lastComment) && (lastComment?.message ?? '') === '';
     if (this.shouldScrollIntoView) {
       whenRendered(this, () => {
         this.expandCollapseComments(false);
-        this.commentBox?.focus();
+        // Because of the non-deterministic order of focus events firing from
+        // the JS event loop, focusing the comment box on a new draft can result
+        // in the draft comment not being focused, which means the user has to
+        // click into it to start typing.
+        if (!isNewDraft) {
+          this.commentBox?.focus();
+        }
         // The delay is a hack because we don't know exactly when to
         // scroll the comment into center.
         // TODO: Find a better solution without a setTimeout
@@ -735,9 +745,9 @@
         }, 500);
       });
     }
-    if (this.thread && isDraft(this.getFirstComment())) {
-      const msg = this.getFirstComment()?.message ?? '';
-      if (msg.length === 0) this.editDraft();
+    // Focus the draft comment input to avoid the user having to click into it.
+    if (isNewDraft) {
+      this.editDraft();
     }
   }
 
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_screenshot_test.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_screenshot_test.ts
index df3cade..9731c4e8 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_screenshot_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_screenshot_test.ts
@@ -113,4 +113,16 @@
     await visualDiff(element, 'gr-comment-thread-with-ai');
     await visualDiffDarkTheme(element, 'gr-comment-thread-with-ai');
   });
+
+  test('unresolved inline code review', async () => {
+    // Simulate inline context by setting custom properties
+    element.style.setProperty('--gr-comment-thread-width', '100%');
+    element.style.setProperty('--gr-comment-thread-diff-max-width', '100%');
+
+    element.thread = createThread(c1, {...c2, unresolved: true});
+    await element.updateComplete;
+
+    await visualDiff(element, 'gr-comment-thread-unresolved-inline');
+    await visualDiffDarkTheme(element, 'gr-comment-thread-unresolved-inline');
+  });
 });
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
index 28ebd79..8b5e166 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
@@ -173,6 +173,60 @@
     );
   });
 
+  test('focuses commentBox when it is NOT a new draft', async () => {
+    const thread = createThread(c1);
+    const element = await fixture<GrCommentThread>(html`
+      <gr-comment-thread
+        .thread=${thread}
+        .shouldScrollIntoView=${true}
+      ></gr-comment-thread>
+    `);
+    await element.updateComplete;
+    await new Promise(resolve => setTimeout(resolve, 0));
+
+    const commentBox = queryAndAssert<HTMLElement>(element, '.comment-box');
+    assert.equal(element.shadowRoot?.activeElement, commentBox);
+  });
+
+  test('does not focus commentBox when it IS a new draft', async () => {
+    const thread = createThread(createNewDraft({message: ''}));
+    const element = await fixture<GrCommentThread>(html`
+      <gr-comment-thread
+        .thread=${thread}
+        .shouldScrollIntoView=${true}
+      ></gr-comment-thread>
+    `);
+    await element.updateComplete;
+    await new Promise(resolve => setTimeout(resolve, 0));
+
+    const commentBox = queryAndAssert<HTMLElement>(element, '.comment-box');
+    assert.notEqual(element.shadowRoot?.activeElement, commentBox);
+  });
+
+  test('comment box spans 100% of container width', async () => {
+    const wrapper = await fixture(html`
+      <div style="width: 500px;">
+        <gr-comment-thread></gr-comment-thread>
+      </div>
+    `);
+    const commentThread = wrapper.querySelector(
+      'gr-comment-thread'
+    ) as GrCommentThread;
+    commentThread.thread = createThread(c1);
+    await commentThread.updateComplete;
+
+    const commentBox = queryAndAssert<HTMLElement>(
+      commentThread,
+      '.comment-box'
+    );
+    const computedWidth = getComputedStyle(commentBox).width;
+    assert.isTrue(
+      computedWidth.endsWith('px') &&
+        Math.abs(parseFloat(computedWidth) - 500) < 30,
+      `Expected comment box width to span container width, but got: ${computedWidth}`
+    );
+  });
+
   test('renders with actions resolved', async () => {
     element.thread = createThread(c1, c2);
     await element.updateComplete;
@@ -494,6 +548,85 @@
       // The draft should be discarded completely
       assert.equal(draftElement.messageText, '');
     });
+
+    test('handle Quote with multi-line message', async () => {
+      stubAdd.restore();
+      stubAdd = sinon
+        .stub(testResolver(commentsModelToken), 'addNewDraft')
+        .callsFake(draft => {
+          const newDraft = {
+            ...draft,
+            id: 'new-draft' as UrlEncodedCommentId,
+            __draft: true,
+          };
+          if (element.thread) {
+            element.thread = {
+              ...element.thread,
+              comments: [...element.thread.comments, newDraft],
+            };
+          }
+          return Promise.resolve(newDraft);
+        });
+
+      element.thread = createThread(c1, {
+        ...c2,
+        message: 'first line\nsecond line\nthird line',
+        unresolved: true,
+      });
+      await element.updateComplete;
+
+      queryAndAssert<GrButton>(element, '#quoteBtn').click();
+      assert.isTrue(stubAdd.called);
+      assert.equal(stubAdd.lastCall.firstArg.in_reply_to, c2.id);
+      await element.updateComplete;
+
+      const draftElement = queryAndAssert<GrComment>(
+        element,
+        'gr-comment.draft'
+      );
+      await draftElement.updateComplete;
+      await waitUntil(
+        () =>
+          draftElement.messageText ===
+          '> first line\n> second line\n> third line\n\n'
+      );
+      assert.equal(
+        draftElement.messageText,
+        '> first line\n> second line\n> third line\n\n'
+      );
+    });
+
+    test('handle reply-to-comment event from child comment', async () => {
+      element.thread = createThread(c1, {...c2, unresolved: true});
+      await element.updateComplete;
+
+      const commentEl = queryAndAssert<GrComment>(element, 'gr-comment');
+      commentEl.dispatchEvent(
+        new CustomEvent('reply-to-comment', {
+          detail: {
+            content: 'custom response',
+            userWantsToEdit: true,
+            unresolved: true,
+          },
+          bubbles: true,
+          composed: true,
+        })
+      );
+
+      assert.isTrue(stubAdd.called);
+      assert.equal(stubAdd.lastCall.firstArg.in_reply_to, c2.id);
+      assert.equal(stubAdd.lastCall.firstArg.unresolved, true);
+    });
+
+    test('reply sets in_reply_to to the last comment id in thread', async () => {
+      element.thread = createThread(c1, c2);
+      await element.updateComplete;
+
+      queryAndAssert<GrButton>(element, '#replyBtn').click();
+      assert.isTrue(stubAdd.called);
+      const newDraft = stubAdd.lastCall.firstArg;
+      assert.equal(newDraft.in_reply_to, c2.id);
+    });
   });
 
   test('comments are sorted correctly', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
index 418ddb0..24ca939 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -1381,11 +1381,9 @@
   override updated(changed: PropertyValues) {
     if (changed.has('editing')) {
       if (this.editing && !this.permanentEditingMode) {
-        // Note that this is a bit fragile, because we are relying on the
-        // comment to become visible soonish. If that does not happen, then we
-        // will be waiting indefinitely and grab focus at some point in the
-        // distant future.
-        whenVisible(this, () => this.textarea?.putCursorAtEnd());
+        this.focusTextarea().catch(() => {
+          // Ignore error since failure to focus is non-fatal.
+        });
       }
     }
     if (changed.has('changeNum') || changed.has('comment')) {
@@ -1400,6 +1398,27 @@
     }
   }
 
+  private async focusTextarea(): Promise<void> {
+    await this.updateComplete;
+    if (!this.textarea) {
+      return;
+    }
+    await this.textarea.updateComplete;
+    if (this.isVisible()) {
+      this.textarea.putCursorAtEnd();
+    } else {
+      // Note that this is a bit fragile, because we are relying on the
+      // comment to become visible soonish. If that does not happen, then we
+      // will be waiting indefinitely and grab focus at some point in the
+      // distant future.
+      whenVisible(this, () => this.textarea?.putCursorAtEnd());
+    }
+  }
+
+  private isVisible(): boolean {
+    return this.offsetWidth > 0 || this.offsetHeight > 0;
+  }
+
   override willUpdate(changed: PropertyValues) {
     this.firstWillUpdate();
     if (changed.has('comment')) {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts
index 58a64bd..b437a74 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts
@@ -54,6 +54,7 @@
 import {suggestionsServiceToken} from '../../../services/suggestions/suggestions-service';
 import {AutocompletionContext} from '../../../utils/autocomplete-cache';
 import {FixSuggestionInfo} from '../../../api/rest-api';
+import {GrSuggestionTextarea} from '../gr-suggestion-textarea/gr-suggestion-textarea';
 
 suite('gr-comment tests', () => {
   let element: GrComment;
@@ -533,6 +534,24 @@
       assert.isTrue(element.isSaveDisabled());
     });
 
+    test('focuses textarea when editing is set to true', async () => {
+      const spy = sinon.spy(GrSuggestionTextarea.prototype, 'putCursorAtEnd');
+      try {
+        element.comment = createDraft();
+        element.editing = false;
+        await element.updateComplete;
+
+        element.editing = true;
+        await element.updateComplete;
+        // focusTextarea is async, wait for it to complete.
+        await new Promise(resolve => setTimeout(resolve, 0));
+
+        assert.isTrue(spy.called);
+      } finally {
+        spy.restore();
+      }
+    });
+
     test('ctrl+s saves comment', async () => {
       const spy = sinon.stub(element, 'save');
       element.messageText = 'is that the horse from horsing around??';
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts
index 0ee4aa3..bfc6462 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts
@@ -52,6 +52,9 @@
   @property({type: Boolean, reflect: true})
   hideInput = false;
 
+  @property({type: Boolean, attribute: 'disable-auto-select', reflect: true})
+  disableAutoSelect = false;
+
   @property({type: String})
   label?: string;
 
@@ -216,6 +219,7 @@
   }
 
   private handleInputClick(e: MouseEvent) {
+    if (this.disableAutoSelect) return;
     e.preventDefault();
     const rootTarget = e.composedPath()[0];
     (rootTarget as HTMLInputElement).select();
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.ts b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.ts
index b50ecfd..acc1180 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.ts
@@ -128,6 +128,22 @@
     assert.equal(mdOutlinedTextField.selectionEnd, element.text!.length - 1);
   });
 
+  test('handleInputClick with disableAutoSelect', async () => {
+    element.disableAutoSelect = true;
+    await element.updateComplete;
+
+    const mdOutlinedTextField = queryAndAssert<MdOutlinedTextField>(
+      element,
+      'md-outlined-text-field'
+    );
+    mdOutlinedTextField.selectionStart = 0;
+    mdOutlinedTextField.selectionEnd = 0;
+
+    mdOutlinedTextField.click();
+    assert.equal(mdOutlinedTextField.selectionStart, 0);
+    assert.equal(mdOutlinedTextField.selectionEnd, 0);
+  });
+
   test('hideInput', async () => {
     const mdOutlinedTextField = queryAndAssert<MdOutlinedTextField>(
       element,
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.ts b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.ts
index 9a7cabc..30defc5 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.ts
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.ts
@@ -54,6 +54,9 @@
   @property({type: Boolean, attribute: 'show-keyboard-shortcut-tooltips'})
   showKeyboardShortcutTooltips = false;
 
+  @property({type: Boolean, attribute: 'disable-auto-select', reflect: true})
+  disableAutoSelect = false;
+
   // Private but used in tests.
   readonly getUserModel = resolve(this, userModelToken);
 
@@ -171,6 +174,7 @@
         .label=${command.title}
         .command=${command.command}
         .tooltip=${this.computeTooltip(index)}
+        .disableAutoSelect=${this.disableAutoSelect}
       ></gr-shell-command>
     `;
   }
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.ts b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.ts
index 05011f8f..3f3b4bc 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.ts
@@ -97,6 +97,16 @@
       assert.isTrue(focusStub.called);
     });
 
+    test('passes disableAutoSelect to gr-shell-command', async () => {
+      element.disableAutoSelect = true;
+      await element.updateComplete;
+      const shellCommand = queryAndAssert<GrShellCommand>(
+        element,
+        'gr-shell-command'
+      );
+      assert.isTrue(shellCommand.disableAutoSelect);
+    });
+
     test('element visibility', async () => {
       assert.isFalse(isHidden(queryAndAssert(element, 'md-tabs')));
       assert.isFalse(isHidden(queryAndAssert(element, '.commands')));
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.ts b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.ts
index 6c2661f..1261e29 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.ts
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.ts
@@ -337,7 +337,6 @@
       this.value = this.inputText || '';
     }
     this.editing = false;
-    // TODO: This event seems to be unused (no listener). Remove?
     fire(this, 'changed', this.value);
   }
 
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts
index be5c2ee..47b1620 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts
@@ -3,6 +3,7 @@
  * Copyright 2022 Google LLC
  * SPDX-License-Identifier: Apache-2.0
  */
+
 import * as sinon from 'sinon';
 import '../../../test/common-test-setup';
 import {assert, fixture, html} from '@open-wc/testing';
@@ -63,7 +64,7 @@
         suffix: '$3',
       },
     });
-    self.CANONICAL_PATH = 'http://localhost';
+    self.CANONICAL_PATH = '';
     element = (
       await fixture(
         wrapInProvider(
@@ -128,7 +129,7 @@
         /* HTML */ `
           <gr-endpoint-decorator name="formatted-text-endpoint">
             <pre class="plaintext">
-          FOO<a href="a.b.c" rel="noopener noreferrer" target="_blank">foo</a>
+          FOO<a href="a.b.c">foo</a>
         </pre>
           </gr-endpoint-decorator>
         `
@@ -159,10 +160,10 @@
           <gr-endpoint-decorator name="formatted-text-endpoint">
             <pre class="plaintext">
             Start:
-            <a href="bug/123" rel="noopener noreferrer" target="_blank">
+            <a href="bug/123">
               bug/123
             </a>
-            <a href="bug/456" rel="noopener noreferrer" target="_blank">
+            <a href="bug/456">
               bug/456
             </a>
           </pre>
@@ -200,13 +201,7 @@
               LinkRewriteMe
             </a>
             text with complex link: A
-            <a
-              href="http://localhost/page?id=12"
-              rel="noopener noreferrer"
-              target="_blank"
-            >
-              Link 12
-            </a>
+            <a href="/page?id=12">Link 12</a>
           </pre>
           </gr-endpoint-decorator>
         `
@@ -372,13 +367,7 @@
                 <p>text without a link: NotA Link 15 cats</p>
                 <p>
                   text with complex link: A
-                  <a
-                    href="http://localhost/page?id=12"
-                    rel="noopener noreferrer"
-                    target="_blank"
-                  >
-                    Link 12
-                  </a>
+                  <a href="/page?id=12">Link 12</a>
                 </p>
               </div>
             </gr-marked-element>
@@ -420,13 +409,7 @@
           </a>
         text without a link: NotA Link 15 cats
         text with complex link: A
-          <a
-            href="http://localhost/page?id=12"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            Link 12
-          </a>
+          <a href="/page?id=12">Link 12</a>
         </pre>
           </gr-endpoint-decorator>
         `
@@ -953,6 +936,45 @@
       await checkLinking('google.com.blah/path', false);
     });
 
+    test('reproduce b/519426997: linkification in loose list', async () => {
+      element.content = '1. A Link 1234\n\n2. aaa';
+      await element.updateComplete;
+      await new Promise<void>(resolve => {
+        const listener = () => {
+          element.removeEventListener('marked-render-complete', listener);
+          resolve();
+        };
+        element.addEventListener('marked-render-complete', listener);
+        setTimeout(() => {
+          element.removeEventListener('marked-render-complete', listener);
+          resolve();
+        }, 100);
+      });
+
+      assert.shadowDom.equal(
+        element,
+        /* HTML */ `
+          <gr-endpoint-decorator name="formatted-text-endpoint">
+            <gr-marked-element>
+              <div slot="markdown-html" class="markdown-html">
+                <ol>
+                  <li>
+                    <p>
+                      A
+                      <a href="/page?id=1234">Link 1234</a>
+                    </p>
+                  </li>
+                  <li>
+                    <p>aaa</p>
+                  </li>
+                </ol>
+              </div>
+            </gr-marked-element>
+          </gr-endpoint-decorator>
+        `
+      );
+    });
+
     suite('user suggest fix', () => {
       setup(async () => {
         const flagsService = getAppContext().flagsService;
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.ts b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.ts
index 0250d82..5abf4fc 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.ts
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.ts
@@ -6,6 +6,7 @@
 import {
   AnnotationPluginApi,
   CoverageProvider,
+  DiffLayerFactory,
   TokenHoverListener,
 } from '../../../api/annotation';
 import {PluginApi} from '../../../api/plugin';
@@ -36,4 +37,12 @@
       listener,
     });
   }
+
+  addDiffLayer(factory: DiffLayerFactory): void {
+    this.reporting.trackApi(this.plugin, 'annotation', 'addDiffLayer');
+    this.pluginsModel.diffLayerRegister({
+      pluginName: this.plugin.getPluginName(),
+      factory,
+    });
+  }
 }
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts
index 6ee3bf3..145be54 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts
@@ -217,6 +217,11 @@
   //   only skip the check if the caller wants to prosess status themselves.
   reportServerError?: boolean;
   isHighPriority?: boolean;
+  /**
+   * If true, the request will be routed through the readScheduler even if it
+   * uses a write method (PUT, POST, DELETE).
+   */
+  useReadScheduler?: boolean;
 }
 
 export interface FetchOptionsInit {
@@ -264,12 +269,20 @@
     private readonly writeScheduler: Scheduler<Response>
   ) {}
 
+  private isWrite(method: string, useReadScheduler?: boolean): boolean {
+    return (
+      (method === 'PUT' || method === 'POST' || method === 'DELETE') &&
+      !useReadScheduler
+    );
+  }
+
   private schedule(
     method: string,
     task: Task<Response>,
-    name?: string
+    name?: string,
+    useReadScheduler?: boolean
   ): Promise<Response> {
-    if (method === 'PUT' || method === 'POST' || method === 'DELETE') {
+    if (this.isWrite(method, useReadScheduler)) {
       return this.writeScheduler.schedule(task, name);
     } else {
       return this.readScheduler.schedule(task, name);
@@ -284,8 +297,7 @@
     const method = req.fetchOptions?.method ?? HttpMethod.GET;
     const startTime = Date.now();
 
-    const isWrite =
-      method === 'PUT' || method === 'POST' || method === 'DELETE';
+    const isWrite = this.isWrite(method, req.useReadScheduler);
     const origin = req.fetchOptions?.headers?.get(REQUEST_ORIGIN_HEADER);
     const pluginName = origin?.startsWith('plugin:') ? origin : undefined;
     const requestName = `${method} - ${
@@ -330,7 +342,9 @@
     };
 
     const resPromise = (
-      req.isHighPriority ? task() : this.schedule(method, task, requestName)
+      req.isHighPriority
+        ? task()
+        : this.schedule(method, task, requestName, req.useReadScheduler)
     ).catch((err: unknown) => {
       if (err instanceof RetryError) {
         return err.payload;
@@ -414,6 +428,9 @@
       fetchOptions: req.fetchOptions,
       anonymizedUrl: req.reportUrlAsIs ? urlWithParams : req.anonymizedUrl,
     };
+    if (req.useReadScheduler !== undefined) {
+      fetchReq.useReadScheduler = req.useReadScheduler;
+    }
 
     let resp: Response;
     try {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.ts
index 92feb24..ea79611 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.ts
@@ -130,6 +130,33 @@
       });
       assert.equal(readScheduler.scheduled.length, 0);
       await assertWriteRequest();
+      const res = await promise;
+      assert.equal(await res.text(), 'Yay');
+    });
+
+    test('POST are sent to writeScheduler', async () => {
+      const promise = helper.fetch({
+        fetchOptions: {
+          method: HttpMethod.POST,
+        },
+        url: '/dummy/url',
+      });
+      assert.equal(readScheduler.scheduled.length, 0);
+      await assertWriteRequest();
+      const res = await promise;
+      assert.equal(await res.text(), 'Yay');
+    });
+
+    test('POST with useReadScheduler are sent to readScheduler', async () => {
+      const promise = helper.fetch({
+        fetchOptions: {
+          method: HttpMethod.POST,
+        },
+        url: '/changes/123/revisions/1/fix:preview',
+        useReadScheduler: true,
+      });
+      assert.equal(writeScheduler.scheduled.length, 0);
+      await assertReadRequest();
       const res: Response = await promise;
       assert.equal(await res.text(), 'Yay');
     });
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.ts b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.ts
index 0c9be74..54aecb3 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.ts
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.ts
@@ -27,6 +27,9 @@
   @property({type: String})
   tooltip = '';
 
+  @property({type: Boolean, attribute: 'disable-auto-select', reflect: true})
+  disableAutoSelect = false;
+
   static override get styles() {
     return [
       sharedStyles,
@@ -73,6 +76,7 @@
           .text=${this.command}
           hasTooltip
           buttonTitle=${this.tooltip}
+          .disableAutoSelect=${this.disableAutoSelect}
         ></gr-copy-clipboard>
       </div>`;
   }
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.ts b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.ts
index 1e0ff11..8fb66f5 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.ts
@@ -44,4 +44,14 @@
     await element.focusOnCopy();
     assert.isTrue(focusStub.called);
   });
+
+  test('passes disableAutoSelect to gr-copy-clipboard', async () => {
+    element.disableAutoSelect = true;
+    await element.updateComplete;
+    const copyClipboard = queryAndAssert<GrCopyClipboard>(
+      element,
+      'gr-copy-clipboard'
+    );
+    assert.isTrue(copyClipboard.disableAutoSelect);
+  });
 });
diff --git a/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview.ts b/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview.ts
index 72e0905..0b46706 100644
--- a/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview.ts
+++ b/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview.ts
@@ -30,7 +30,11 @@
 import {navigationToken} from '../../core/gr-navigation/gr-navigation';
 import {fire, fireError} from '../../../utils/event-util';
 import {Timing} from '../../../constants/reporting';
-import {createChangeUrl} from '../../../models/views/change';
+import {
+  changeViewModelToken,
+  createApplyFixUrl,
+} from '../../../models/views/change';
+
 import {getFileExtension} from '../../../utils/file-util';
 import {throwingErrorCallback} from '../gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
 import {ReportSource} from '../../../services/suggestions/suggestions-service';
@@ -128,6 +132,8 @@
 
   private readonly getNavigation = resolve(this, navigationToken);
 
+  private readonly getViewModel = resolve(this, changeViewModelToken);
+
   private readonly syntaxLayer = new GrSyntaxLayerWorker(
     resolve(this, highlightServiceToken),
     () => getAppContext().reportingService
@@ -391,16 +397,23 @@
     // basePatchNum is from comment patchset and comment cannot be created
     // in EDIT. RevisionPatchset without EDIT is PatchSetNumber
     if (res?.ok && basePatchNum !== undefined && basePatchNum !== EDIT) {
+      const currentChildView = this.getViewModel().getState()?.childView;
+      const filePath =
+        fixSuggestion.replacements[0]?.path ?? this.preview?.filepath;
       this.getNavigation().setUrl(
-        createChangeUrl({
+        createApplyFixUrl({
           changeNum,
           repo: this.repo!,
-          patchNum: EDIT,
           basePatchNum: basePatchNum as PatchSetNumber,
           forceReload: !this.hasEdit,
+          filePath,
+          currentChildView,
         })
       );
-      fire(this, 'reload-diff', {path: fixSuggestion.replacements[0].path});
+
+      if (filePath) {
+        fire(this, 'reload-diff', {path: filePath});
+      }
       fire(this, 'apply-user-suggestion', {
         fixSuggestion: fixSuggestion.description.includes(
           ReportSource.GET_AI_FIX_FOR_COMMENT
diff --git a/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview_test.ts b/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview_test.ts
index 742900f..da0220c 100644
--- a/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview_test.ts
@@ -17,8 +17,21 @@
 } from '../../../test/test-data-generators';
 import {getAppContext} from '../../../services/app-context';
 import {GrSuggestionDiffPreview} from './gr-suggestion-diff-preview';
-import {stubFlags} from '../../../test/test-utils';
-import {NumericChangeId, RevisionPatchSetNum} from '../../../api/rest-api';
+import * as sinon from 'sinon';
+import {navigationToken} from '../../core/gr-navigation/gr-navigation';
+import {stubFlags, stubRestApi} from '../../../test/test-utils';
+import {
+  NumericChangeId,
+  RepoName,
+  RevisionPatchSetNum,
+} from '../../../api/rest-api';
+import {changeViewModelToken} from '../../../models/views/change';
+import {
+  createChangeViewState,
+  createDiffViewState,
+  createRange,
+} from '../../../test/test-data-generators';
+import {testResolver} from '../../../test/common-test-setup';
 
 suite('gr-suggestion-diff-preview tests', () => {
   let element: GrSuggestionDiffPreview;
@@ -121,4 +134,53 @@
       {ignoreAttributes: ['style']}
     );
   });
+
+  suite('applyFix navigation', () => {
+    let setUrlStub: sinon.SinonStub;
+
+    setup(() => {
+      setUrlStub = sinon.stub(testResolver(navigationToken), 'setUrl');
+      stubRestApi('applyFixSuggestion').returns(
+        Promise.resolve(new Response(null, {status: 200}))
+      );
+      element.changeNum = 42 as NumericChangeId;
+      element.repo = 'test-project' as RepoName;
+      element.patchSet = 1 as RevisionPatchSetNum;
+
+      element.fixSuggestionInfo = {
+        ...createFixSuggestionInfo(),
+        replacements: [
+          {
+            path: 'foo/bar.ts',
+            replacement: 'new content',
+            range: createRange(),
+          },
+        ],
+      };
+    });
+
+    test('navigates to createDiffUrl when in Diff View', async () => {
+      testResolver(changeViewModelToken).setState(createDiffViewState());
+
+      await element.applyFix();
+
+      assert.isTrue(setUrlStub.calledOnce);
+      assert.equal(
+        setUrlStub.lastCall.firstArg,
+        '/c/test-project/+/42/1..edit/foo/bar.ts?forceReload=true'
+      );
+    });
+
+    test('navigates to createChangeUrl when in Change View', async () => {
+      testResolver(changeViewModelToken).setState(createChangeViewState());
+
+      await element.applyFix();
+
+      assert.isTrue(setUrlStub.calledOnce);
+      assert.equal(
+        setUrlStub.lastCall.firstArg,
+        '/c/test-project/+/42/1..edit?forceReload=true'
+      );
+    });
+  });
 });
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-styles.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-styles.ts
index 894187c..dd41bec 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-styles.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-styles.ts
@@ -346,6 +346,10 @@
     display: block;
     margin-top: var(--spacing-xs);
   }
+  ::slotted(.comment-thread) {
+    --gr-comment-thread-width: 100%;
+    --gr-comment-thread-diff-max-width: 100%;
+  }
   gr-diff-row td.content div.contentText {
     background-color: var(--view-background-color);
   }
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/polygerrit-ui/app/models/checks/checks-util.ts b/polygerrit-ui/app/models/checks/checks-util.ts
index f380422..239059b 100644
--- a/polygerrit-ui/app/models/checks/checks-util.ts
+++ b/polygerrit-ui/app/models/checks/checks-util.ts
@@ -23,7 +23,12 @@
 import {DraftInfo, FixSuggestionInfo} from '../../types/common';
 import {OpenFixPreviewEventDetail} from '../../types/events';
 import {isDefined} from '../../types/types';
-import {AiAgentEventDetails, Interaction} from '../../constants/reporting';
+import {
+  AiAgentChatEventDetails,
+  AiAgentCheckEventDetails,
+  AiAgentEventDetails,
+  Interaction,
+} from '../../constants/reporting';
 import {ReportingService as Reporting} from '../../services/gr-reporting/gr-reporting';
 import {createNew, PROVIDED_FIX_ID} from '../../utils/comment-util';
 import {assert, assertIsDefined, assertNever} from '../../utils/common-util';
@@ -597,10 +602,10 @@
   return hasMessage || hasMultipleLinks || hasPointers || hasFixes;
 }
 
-function getAiAgentEventDetails(
+function getAiAgentChatEventDetails(
   runResult: RunResult,
   commentId?: string
-): AiAgentEventDetails | undefined {
+): AiAgentChatEventDetails | undefined {
   const externalId = runResult.externalId;
   if (!externalId) return;
   // Use JSON.parse. We expect agentId, conversationId, turnIndex.
@@ -620,7 +625,7 @@
     }
     /* eslint-disable object-shorthand */
     // prettier-ignore
-    const eventDetails: AiAgentEventDetails = {
+    const eventDetails: AiAgentChatEventDetails = {
       'agentId': agentId,
       'conversationId': conversationId,
       'turnIndex': Number(turnIndex),
@@ -634,6 +639,36 @@
   }
 }
 
+function getAiAgentCheckEventDetails(
+  runResult: RunResult,
+  commentId?: string
+): AiAgentCheckEventDetails | undefined {
+  if (!runResult.externalId) return;
+  if (!runResult.isAiPowered) return;
+  if (!runResult.checkName) return;
+  if (!runResult.checkDescription) return;
+
+  /* eslint-disable object-shorthand */
+  // prettier-ignore
+  const eventDetails: AiAgentCheckEventDetails = {
+    'checkName': runResult.checkName,
+    'checkDescription': runResult.checkDescription,
+    'externalId': runResult.externalId,
+    'commentId': commentId,
+  };
+  return eventDetails;
+  /* eslint-enable object-shorthand */
+}
+
+function getAiAgentEventDetails(
+  runResult: RunResult,
+  commentId?: string
+): AiAgentEventDetails | undefined {
+  const chatDetails = getAiAgentChatEventDetails(runResult, commentId);
+  if (chatDetails) return chatDetails;
+  return getAiAgentCheckEventDetails(runResult, commentId);
+}
+
 /**
  * Reports a "Get AI Fix" click interaction on a Code Review Agent check.
  */
diff --git a/polygerrit-ui/app/models/checks/checks-util_test.ts b/polygerrit-ui/app/models/checks/checks-util_test.ts
index 978a844..7359381 100644
--- a/polygerrit-ui/app/models/checks/checks-util_test.ts
+++ b/polygerrit-ui/app/models/checks/checks-util_test.ts
@@ -264,6 +264,9 @@
       } as unknown as ReportingService;
       const runResult = {
         ...createRunResult(),
+        isAiPowered: true,
+        checkName: 'test-check-name',
+        checkDescription: 'test-description',
         externalId: JSON.stringify({
           agentId: 'test-agent',
           conversationId: 'test-conv',
@@ -294,6 +297,9 @@
       } as unknown as ReportingService;
       const runResult = {
         ...createRunResult(),
+        isAiPowered: true,
+        checkName: 'test-check-name',
+        checkDescription: 'test-description',
         externalId: JSON.stringify({
           agentId: 'test-agent',
           conversationId: 'test-conv',
@@ -321,7 +327,10 @@
       const reporting = {
         reportInteraction: reportInteractionStub,
       } as unknown as ReportingService;
-      const runResult = createRunResult();
+      const runResult = {
+        ...createRunResult(),
+        isAiPowered: true,
+      };
 
       reportAiAgentGetAIFix(reporting, runResult);
       assert.isFalse(reportInteractionStub.called);
@@ -330,29 +339,86 @@
       assert.isFalse(reportInteractionStub.called);
     });
 
-    test('does not report if externalId is invalid JSON', () => {
+    test('does not report if isAiPowered is false or missing', () => {
       const reporting = {
         reportInteraction: reportInteractionStub,
       } as unknown as ReportingService;
       const runResult = {
         ...createRunResult(),
-        externalId: 'invalid-json',
+        isAiPowered: false,
+        externalId: 'some-id',
       };
 
       reportAiAgentGetAIFix(reporting, runResult);
       assert.isFalse(reportInteractionStub.called);
+
+      const runResultMissing = {
+        ...createRunResult(),
+        isAiPowered: undefined,
+        externalId: 'some-id',
+      };
+
+      reportAiAgentCommentDraft(reporting, runResultMissing);
+      assert.isFalse(reportInteractionStub.called);
     });
 
-    test('does not report if externalId is missing required fields', () => {
+    test('reports check event details if externalId is plain string (not chat JSON)', () => {
       const reporting = {
         reportInteraction: reportInteractionStub,
       } as unknown as ReportingService;
       const runResult = {
         ...createRunResult(),
+        isAiPowered: true,
+        checkName: 'test-check-name',
+        checkDescription: 'test-description',
+        externalId: 'plain-string-external-id',
+      };
+
+      reportAiAgentGetAIFix(reporting, runResult);
+      assert.isTrue(reportInteractionStub.calledOnce);
+      assert.deepEqual(reportInteractionStub.lastCall.args[1], {
+        checkName: 'test-check-name',
+        checkDescription: 'test-description',
+        externalId: 'plain-string-external-id',
+        commentId: undefined,
+      });
+    });
+
+    test('reports check event details if externalId is JSON but missing chat fields', () => {
+      const reporting = {
+        reportInteraction: reportInteractionStub,
+      } as unknown as ReportingService;
+      const runResult = {
+        ...createRunResult(),
+        isAiPowered: true,
+        checkName: 'test-check-name',
+        checkDescription: 'test-description',
         externalId: JSON.stringify({agentId: 'test-agent'}),
       };
 
       reportAiAgentGetAIFix(reporting, runResult);
+      assert.isTrue(reportInteractionStub.calledOnce);
+      assert.deepEqual(reportInteractionStub.lastCall.args[1], {
+        checkName: 'test-check-name',
+        checkDescription: 'test-description',
+        externalId: runResult.externalId,
+        commentId: undefined,
+      });
+    });
+
+    test('does not report check event if checkDescription is missing', () => {
+      const reporting = {
+        reportInteraction: reportInteractionStub,
+      } as unknown as ReportingService;
+      const runResult = {
+        ...createRunResult(),
+        isAiPowered: true,
+        checkName: 'test-check-name',
+        checkDescription: undefined,
+        externalId: 'plain-string-external-id',
+      };
+
+      reportAiAgentGetAIFix(reporting, runResult);
       assert.isFalse(reportInteractionStub.called);
     });
   });
diff --git a/polygerrit-ui/app/models/flows/flows-model.ts b/polygerrit-ui/app/models/flows/flows-model.ts
index 300a1b7..83d0c74 100644
--- a/polygerrit-ui/app/models/flows/flows-model.ts
+++ b/polygerrit-ui/app/models/flows/flows-model.ts
@@ -31,12 +31,26 @@
 
 export const SUBMIT_ACTION_NAME = 'submit';
 
+/**
+ * Matches the base change path prefix up to the change number (e.g. `/c/project/+/123`),
+ * ignoring any trailing patchsets, diff ranges, comment IDs, or file paths.
+ *
+ * @see RoutePattern.CHANGE in `gr-router.ts` for corresponding route pattern.
+ */
+const CHANGE_PREFIX_PATTERN = /^(.*?\/(?:c\/.+?\/\+)\/\d+)/;
+
 export function getSubmitCondition() {
   return getChangePrefix() + ' is is:submittable';
 }
 
+/**
+ * Returns the change URL prefix (e.g. `http://host/c/project/+/123`), stripping
+ * patchset numbers, diff ranges, comment IDs, and file paths from pathname.
+ */
 export function getChangePrefix() {
-  return window.location.origin + window.location.pathname;
+  const match = window.location.pathname.match(CHANGE_PREFIX_PATTERN);
+  const pathname = match ? match[1] : window.location.pathname;
+  return window.location.origin + pathname;
 }
 
 export class FlowsModel extends Model<FlowsState> {
diff --git a/polygerrit-ui/app/models/flows/flows-model_test.ts b/polygerrit-ui/app/models/flows/flows-model_test.ts
index 2dfe5a7..18c9538 100644
--- a/polygerrit-ui/app/models/flows/flows-model_test.ts
+++ b/polygerrit-ui/app/models/flows/flows-model_test.ts
@@ -200,4 +200,69 @@
     await waitUntil(() => !flowsModel.hasAutosubmitFlowAlready());
     assert.isFalse(flowsModel.hasAutosubmitFlowAlready());
   });
+
+  suite('getChangePrefix', () => {
+    let originalPath: string;
+
+    setup(() => {
+      originalPath = window.location.pathname;
+    });
+
+    teardown(() => {
+      window.history.replaceState(null, '', originalPath);
+    });
+
+    test('strips patchset, diff range, and file path subpaths', () => {
+      const origin = window.location.origin;
+
+      window.history.replaceState(null, '', '/c/my-repo/+/123');
+      assert.equal(getChangePrefix(), `${origin}/c/my-repo/+/123`);
+
+      window.history.replaceState(null, '', '/c/my-repo/+/123/4');
+      assert.equal(getChangePrefix(), `${origin}/c/my-repo/+/123`);
+
+      window.history.replaceState(null, '', '/c/my-repo/+/123/1..4');
+      assert.equal(getChangePrefix(), `${origin}/c/my-repo/+/123`);
+
+      window.history.replaceState(
+        null,
+        '',
+        '/c/my-repo/+/123/1..4/src/file.ts'
+      );
+      assert.equal(getChangePrefix(), `${origin}/c/my-repo/+/123`);
+    });
+  });
+
+  test('hasAutosubmitFlowAlready detects flows when navigating between patchsets/diff ranges', async () => {
+    const originalPath = window.location.pathname;
+    try {
+      window.history.replaceState(null, '', '/c/my-repo/+/123/4..5/src/foo.ts');
+      stubRestApi('getIfFlowsIsEnabled').resolves({enabled: true});
+      const expectedPrefix = `${window.location.origin}/c/my-repo/+/123`;
+      stubRestApi('listFlows').resolves([
+        createFlow({
+          uuid: 'flow1',
+          stages: [
+            {
+              expression: {
+                condition: `${expectedPrefix} is is:submittable`,
+                action: {name: SUBMIT_ACTION_NAME},
+              },
+              state: FlowStageState.DONE,
+            },
+          ],
+        }),
+      ]);
+
+      changeModel.updateStateChange({
+        ...createParsedChange(),
+        _number: 123 as NumericChangeId,
+      });
+      await waitUntil(() => flowsModel.getState().flows.length > 0);
+
+      assert.isTrue(flowsModel.hasAutosubmitFlowAlready());
+    } finally {
+      window.history.replaceState(null, '', originalPath);
+    }
+  });
 });
diff --git a/polygerrit-ui/app/models/plugins/plugins-model.ts b/polygerrit-ui/app/models/plugins/plugins-model.ts
index 8ed7cfe..9074c21 100644
--- a/polygerrit-ui/app/models/plugins/plugins-model.ts
+++ b/polygerrit-ui/app/models/plugins/plugins-model.ts
@@ -12,7 +12,11 @@
 } from '../../api/checks';
 import {Model} from '../base/model';
 import {select} from '../../utils/observable-util';
-import {CoverageProvider, TokenHoverListener} from '../../api/annotation';
+import {
+  CoverageProvider,
+  DiffLayerFactory,
+  TokenHoverListener,
+} from '../../api/annotation';
 import {SuggestionsProvider} from '../../api/suggestions';
 import {ChangeUpdatesPublisher} from '../../api/change-updates';
 import {AiCodeReviewProvider} from '../../api/ai-code-review';
@@ -59,6 +63,11 @@
   listener: TokenHoverListener;
 }
 
+export interface DiffLayerPlugin {
+  pluginName: string;
+  factory: DiffLayerFactory;
+}
+
 export interface ChecksUpdate {
   pluginName: string;
   run: CheckRun;
@@ -111,6 +120,11 @@
    * annotationApi().addTokenHoverListener().
    */
   tokenHighlightPlugins: TokenHoverListenerPlugin[];
+
+  /**
+   * List of plugins that have registered a diff layer factory.
+   */
+  diffLayerPlugins: DiffLayerPlugin[];
 }
 
 export class PluginsModel extends Model<PluginsState> {
@@ -154,6 +168,11 @@
     state => state.suggestionsPlugins
   );
 
+  public diffLayerPlugins$ = select(
+    this.state$,
+    state => state.diffLayerPlugins
+  );
+
   public pluginsLoaded$ = select(this.state$, state => state.pluginsLoaded);
 
   constructor() {
@@ -167,6 +186,7 @@
       flowsAutosubmitPlugins: [],
       suggestionsPlugins: [],
       tokenHighlightPlugins: [],
+      diffLayerPlugins: [],
     });
   }
 
@@ -302,6 +322,22 @@
     this.setState(nextState);
   }
 
+  diffLayerRegister(plugin: DiffLayerPlugin) {
+    const nextState = {...this.getState()};
+    nextState.diffLayerPlugins = [...nextState.diffLayerPlugins];
+    const alreadyRegistered = nextState.diffLayerPlugins.some(
+      p => p.pluginName === plugin.pluginName
+    );
+    if (alreadyRegistered) {
+      console.warn(
+        `${plugin.pluginName} tried to register twice as a diff layer provider. Ignored.`
+      );
+      return;
+    }
+    nextState.diffLayerPlugins.push(plugin);
+    this.setState(nextState);
+  }
+
   checksUpdate(update: ChecksUpdate) {
     const plugins = this.getState().checksPlugins;
     const plugin = plugins.find(p => p.pluginName === update.pluginName);
diff --git a/polygerrit-ui/app/models/plugins/plugins-model_test.ts b/polygerrit-ui/app/models/plugins/plugins-model_test.ts
index 7aabae7..bdcb8b1 100644
--- a/polygerrit-ui/app/models/plugins/plugins-model_test.ts
+++ b/polygerrit-ui/app/models/plugins/plugins-model_test.ts
@@ -4,9 +4,15 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 import '../../test/common-test-setup';
+import * as sinon from 'sinon';
 import './plugins-model';
 import {ChecksApiConfig, ChecksProvider, ResponseCode} from '../../api/checks';
-import {ChecksPlugin, ChecksUpdate, PluginsModel} from './plugins-model';
+import {
+  ChecksPlugin,
+  ChecksUpdate,
+  DiffLayerPlugin,
+  PluginsModel,
+} from './plugins-model';
 import {createRun, createRunResult} from '../../test/test-data-generators';
 import {assert} from '@open-wc/testing';
 
@@ -29,6 +35,7 @@
 suite('plugins-model tests', () => {
   let model: PluginsModel;
   let checksPlugins: ChecksPlugin[] = [];
+  let diffLayerPlugins: DiffLayerPlugin[] = [];
   const register = function () {
     model.checksRegister({
       pluginName: PLUGIN_NAME,
@@ -41,6 +48,7 @@
     model = new PluginsModel();
     model.state$.subscribe(s => {
       checksPlugins = s.checksPlugins;
+      diffLayerPlugins = s.diffLayerPlugins;
     });
   });
 
@@ -81,4 +89,33 @@
 
     assert.equal(update?.pluginName, PLUGIN_NAME);
   });
+
+  test('diffLayerRegister', async () => {
+    assert.isFalse(diffLayerPlugins.some(p => p.pluginName === PLUGIN_NAME));
+
+    const factory = () => {
+      return {
+        annotate: () => {},
+      };
+    };
+    model.diffLayerRegister({
+      pluginName: PLUGIN_NAME,
+      factory,
+    });
+
+    assert.isTrue(diffLayerPlugins.some(p => p.pluginName === PLUGIN_NAME));
+    assert.equal(
+      diffLayerPlugins.find(p => p.pluginName === PLUGIN_NAME)?.factory,
+      factory
+    );
+
+    // Try to register again
+    const consoleWarnStub = sinon.stub(console, 'warn');
+    model.diffLayerRegister({
+      pluginName: PLUGIN_NAME,
+      factory,
+    });
+    assert.isTrue(consoleWarnStub.calledOnce);
+    consoleWarnStub.restore();
+  });
 });
diff --git a/polygerrit-ui/app/models/views/change.ts b/polygerrit-ui/app/models/views/change.ts
index 03c944f..0ee5e99 100644
--- a/polygerrit-ui/app/models/views/change.ts
+++ b/polygerrit-ui/app/models/views/change.ts
@@ -253,6 +253,9 @@
   ) {
     params.push(`checksPatchset=${state.checksPatchset}`);
   }
+  if (state.forceReload) {
+    params.push('forceReload=true');
+  }
   if (params.length > 0) {
     queryParams = '?' + params.join('&');
   }
@@ -285,6 +288,26 @@
   return `${createChangeUrlCommon(state)}${path},edit${suffix}`;
 }
 
+export function createApplyFixUrl(
+  obj: (CreateChangeUrlObject | Omit<ChangeViewState, 'view' | 'childView'>) & {
+    filePath?: string;
+    currentChildView?: ChangeChildView;
+  }
+): string {
+  const {filePath, currentChildView, ...restObj} = obj;
+  if (currentChildView === ChangeChildView.DIFF && filePath) {
+    return createDiffUrl({
+      ...restObj,
+      patchNum: EDIT,
+      diffView: {path: filePath},
+    });
+  }
+  return createChangeUrl({
+    ...restObj,
+    patchNum: EDIT,
+  });
+}
+
 /**
  * The shared part of creating a change URL between OVERVIEW, DIFF and EDIT
  * child views.
diff --git a/polygerrit-ui/app/models/views/change_test.ts b/polygerrit-ui/app/models/views/change_test.ts
index e05adb6..8bebe21 100644
--- a/polygerrit-ui/app/models/views/change_test.ts
+++ b/polygerrit-ui/app/models/views/change_test.ts
@@ -6,10 +6,13 @@
 import {assert} from '@open-wc/testing';
 import {
   BasePatchSetNum,
+  EDIT,
+  NumericChangeId,
   PatchSetNumber,
   RepoName,
   RevisionPatchSetNum,
 } from '../../api/rest-api';
+
 import '../../test/common-test-setup';
 import {
   createChangeViewState,
@@ -17,7 +20,9 @@
   createEditViewState,
 } from '../../test/test-data-generators';
 import {
+  ChangeChildView,
   ChangeViewState,
+  createApplyFixUrl,
   createChangeUrl,
   createDiffUrl,
   createEditUrl,
@@ -114,6 +119,14 @@
       );
     });
 
+    test('forceReload', () => {
+      params.forceReload = true;
+      assert.equal(
+        createDiffUrl(params),
+        '/c/test-project/+/42/12/x%252By/path.cpp?forceReload=true'
+      );
+    });
+
     test('base patchset', () => {
       params.basePatchNum = 6 as BasePatchSetNum;
       assert.equal(
@@ -186,4 +199,36 @@
     assert.equal(createEditUrl(params).substring(0, 5), '/base');
     window.CANONICAL_PATH = undefined;
   });
+
+  suite('createApplyFixUrl', () => {
+    test('Diff View context', () => {
+      assert.equal(
+        createApplyFixUrl({
+          changeNum: 42 as NumericChangeId,
+          repo: 'test-project' as RepoName,
+          patchNum: EDIT,
+          basePatchNum: 1 as BasePatchSetNum,
+          forceReload: true,
+          filePath: 'foo/bar.ts',
+          currentChildView: ChangeChildView.DIFF,
+        }),
+        '/c/test-project/+/42/1..edit/foo/bar.ts?forceReload=true'
+      );
+    });
+
+    test('Overview context', () => {
+      assert.equal(
+        createApplyFixUrl({
+          changeNum: 42 as NumericChangeId,
+          repo: 'test-project' as RepoName,
+          patchNum: EDIT,
+          basePatchNum: 1 as BasePatchSetNum,
+          forceReload: true,
+          filePath: 'foo/bar.ts',
+          currentChildView: ChangeChildView.OVERVIEW,
+        }),
+        '/c/test-project/+/42/1..edit?forceReload=true'
+      );
+    });
+  });
 });
diff --git a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
index 92a9c02..51edcfc 100644
--- a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
+++ b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
@@ -2620,6 +2620,7 @@
           }),
           url: `${url}/fix:preview`,
           anonymizedUrl: `${ANONYMIZED_REVISION_BASE_URL}/fix:preview`,
+          useReadScheduler: true,
         })) as FilePathToDiffInfoMap | undefined;
 
         if (response === undefined) {
diff --git a/polygerrit-ui/app/types/events.ts b/polygerrit-ui/app/types/events.ts
index 52a4794..2a2ec16 100644
--- a/polygerrit-ui/app/types/events.ts
+++ b/polygerrit-ui/app/types/events.ts
@@ -110,7 +110,6 @@
 
 export type ChangeEvent = InputEvent;
 
-// TODO: This event seems to be unused (no listener). Remove?
 export type ChangedEvent = CustomEvent<string | undefined>;
 
 export interface ChangeMessageDeletedEventDetail {
diff --git a/polygerrit-ui/app/utils/comment-util.ts b/polygerrit-ui/app/utils/comment-util.ts
index 4b0c1ab..ef08cf0 100644
--- a/polygerrit-ui/app/utils/comment-util.ts
+++ b/polygerrit-ui/app/utils/comment-util.ts
@@ -96,15 +96,6 @@
   const path1 = c1.path ?? '';
   const path2 = c2.path ?? '';
   if (path1 !== path2) {
-    // TODO: Why is this logic not part of specialFilePathCompare()?
-    // '/PATCHSET' will not come before '/COMMIT' when sorting
-    // alphabetically so move it to the front explicitly
-    if (path1 === SpecialFilePath.PATCHSET_LEVEL_COMMENTS) {
-      return -1;
-    }
-    if (path2 === SpecialFilePath.PATCHSET_LEVEL_COMMENTS) {
-      return 1;
-    }
     return specialFilePathCompare(path1, path2);
   }
 
@@ -211,7 +202,7 @@
     path: replyingTo.path,
     patch_set: replyingTo.patch_set,
     side: replyingTo.side,
-    line: replyingTo.line,
+    line: replyingTo.range ? replyingTo.range.end_line : replyingTo.line,
     range: replyingTo.range,
     parent: replyingTo.parent,
     in_reply_to: replyingTo.id,
diff --git a/polygerrit-ui/app/utils/comment-util_test.ts b/polygerrit-ui/app/utils/comment-util_test.ts
index e8fe606..abda42c 100644
--- a/polygerrit-ui/app/utils/comment-util_test.ts
+++ b/polygerrit-ui/app/utils/comment-util_test.ts
@@ -8,6 +8,7 @@
   computeDisplayLine,
   createCommentThreads,
   createNew,
+  createNewReply,
   createUserFixSuggestion,
   getContentInCommentRange,
   getMentionedThreads,
@@ -748,4 +749,36 @@
       assert.equal(computeDisplayLine({}), '');
     });
   });
+
+  suite('createNewReply', () => {
+    test('standard comment reply (no range)', () => {
+      const replyingTo = {
+        ...createComment(),
+        id: 'parent_id' as UrlEncodedCommentId,
+        line: 5,
+      };
+      const reply = createNewReply(replyingTo, 'reply message', false);
+      assert.equal(reply.line, 5);
+      assert.isUndefined(reply.range);
+      assert.equal(reply.in_reply_to, 'parent_id');
+    });
+
+    test('reply to comment with range (mismatched line)', () => {
+      const replyingTo = {
+        ...createComment(),
+        id: 'parent_id' as UrlEncodedCommentId,
+        line: 1,
+        range: {
+          start_line: 1,
+          start_character: 0,
+          end_line: 2,
+          end_character: 5,
+        },
+      };
+      const reply = createNewReply(replyingTo, 'reply message', false);
+      assert.equal(reply.line, 2); // Should match range.end_line, not replyingTo.line
+      assert.deepEqual(reply.range, replyingTo.range);
+      assert.equal(reply.in_reply_to, 'parent_id');
+    });
+  });
 });
diff --git a/polygerrit-ui/app/utils/link-util.ts b/polygerrit-ui/app/utils/link-util.ts
index 2f12bd7..86f9f55 100644
--- a/polygerrit-ui/app/utils/link-util.ts
+++ b/polygerrit-ui/app/utils/link-util.ts
@@ -4,7 +4,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 import {CommentLinkInfo, CommentLinks} from '../types/common';
-import {getBaseUrl} from './url-util';
+import {getBaseUrl, sameOrigin} from './url-util';
 
 /**
  * Finds links within the base string and convert them to HTML. Config-based
@@ -115,15 +115,23 @@
   const replacementHref = rewrite.link.startsWith('/')
     ? `${getBaseUrl()}${rewrite.link}`
     : rewrite.link;
-  const regexp = new RegExp(rewrite.match, 'g');
-  return matchedText.replace(
-    regexp,
-    createLinkTemplate(
-      replacementHref,
-      rewrite.text ?? '$&',
-      rewrite.prefix,
-      rewrite.suffix
-    )
+  // Use regexp without 'g' flag for replacement to avoid lastIndex issues
+  // when running replace multiple times.
+  const regexp = new RegExp(rewrite.match);
+  const resolvedHref = matchedText.replace(regexp, replacementHref);
+  const resolvedText = matchedText.replace(regexp, rewrite.text ?? '$&');
+  const resolvedPrefix = rewrite.prefix
+    ? matchedText.replace(regexp, rewrite.prefix)
+    : undefined;
+  const resolvedSuffix = rewrite.suffix
+    ? matchedText.replace(regexp, rewrite.suffix)
+    : undefined;
+
+  return createLinkTemplate(
+    resolvedHref,
+    resolvedText,
+    resolvedPrefix,
+    resolvedSuffix
   );
 }
 
@@ -133,9 +141,10 @@
   prefix?: string,
   suffix?: string
 ) {
-  return `${
-    prefix ?? ''
-  }<a href="${href}" rel="noopener noreferrer" target="_blank">${displayText}</a>${
+  const attributes = sameOrigin(href)
+    ? ''
+    : ' rel="noopener noreferrer" target="_blank"';
+  return `${prefix ?? ''}<a href="${href}"${attributes}>${displayText}</a>${
     suffix ?? ''
   }`;
 }
diff --git a/polygerrit-ui/app/utils/link-util_test.ts b/polygerrit-ui/app/utils/link-util_test.ts
index 7b09320..085d204 100644
--- a/polygerrit-ui/app/utils/link-util_test.ts
+++ b/polygerrit-ui/app/utils/link-util_test.ts
@@ -12,16 +12,20 @@
     return `<a href="${href}" rel="noopener noreferrer" target="_blank">${text}</a>`;
   }
 
+  function internalLink(text: string, href: string) {
+    return `<a href="${href}">${text}</a>`;
+  }
+
   suite('link rewrites', () => {
     test('without text', () => {
       assert.equal(
         linkifyUrlsAndApplyRewrite('foo', {
           fooLinkWithoutText: {
             match: 'foo',
-            link: 'foo.gov',
+            link: 'http://foo.gov',
           },
         }),
-        link('foo', 'foo.gov')
+        link('foo', 'http://foo.gov')
       );
     });
 
@@ -30,11 +34,11 @@
         linkifyUrlsAndApplyRewrite('foo', {
           fooLinkWithText: {
             match: 'foo',
-            link: 'foo.gov',
+            link: 'http://foo.gov',
             text: 'foo site',
           },
         }),
-        link('foo site', 'foo.gov')
+        link('foo site', 'http://foo.gov')
       );
     });
 
@@ -43,13 +47,13 @@
         linkifyUrlsAndApplyRewrite('there are 12 foos here', {
           fooLinkWithText: {
             match: '(.*)(bug|foo)s(.*)',
-            link: '$2.gov',
+            link: 'http://$2.gov',
             text: '$2 list',
             prefix: '$1on the ',
             suffix: '$3',
           },
         }),
-        `there are 12 on the ${link('foo list', 'foo.gov')} here`
+        `there are 12 on the ${link('foo list', 'http://foo.gov')} here`
       );
     });
 
@@ -58,10 +62,10 @@
         linkifyUrlsAndApplyRewrite('foo foo', {
           foo: {
             match: 'foo',
-            link: 'foo.gov',
+            link: 'http://foo.gov',
           },
         }),
-        `${link('foo', 'foo.gov')} ${link('foo', 'foo.gov')}`
+        `${link('foo', 'http://foo.gov')} ${link('foo', 'http://foo.gov')}`
       );
     });
   });
@@ -71,18 +75,18 @@
       linkifyUrlsAndApplyRewrite('foobarbaz', {
         foo: {
           match: 'foo',
-          link: 'foo.gov',
+          link: 'http://foo.gov',
         },
         foobarbaz: {
           match: 'foobarbaz',
-          link: 'foobarbaz.gov',
+          link: 'http://foobarbaz.gov',
         },
         foobar: {
           match: 'foobar',
-          link: 'foobar.gov',
+          link: 'http://foobar.gov',
         },
       }),
-      link('foobarbaz', 'foobarbaz.gov')
+      link('foobarbaz', 'http://foobarbaz.gov')
     );
   });
 
@@ -91,18 +95,18 @@
       linkifyUrlsAndApplyRewrite('foobarbaz', {
         foo: {
           match: 'baz',
-          link: 'Baz.gov',
+          link: 'http://Baz.gov',
         },
         foobarbaz: {
           match: 'foobarbaz',
-          link: 'FooBarBaz.gov',
+          link: 'http://FooBarBaz.gov',
         },
         foobar: {
           match: 'barbaz',
-          link: 'BarBaz.gov',
+          link: 'http://BarBaz.gov',
         },
       }),
-      link('foobarbaz', 'FooBarBaz.gov')
+      link('foobarbaz', 'http://FooBarBaz.gov')
     );
   });
 
@@ -111,18 +115,18 @@
       linkifyUrlsAndApplyRewrite('foobarbaz', {
         foo: {
           match: 'foo',
-          link: 'FOO',
+          link: 'http://FOO',
         },
         oobarba: {
           match: 'oobarba',
-          link: 'OOBARBA',
+          link: 'http://OOBARBA',
         },
         baz: {
           match: 'baz',
-          link: 'BAZ',
+          link: 'http://BAZ',
         },
       }),
-      `${link('foo', 'FOO')}bar${link('baz', 'BAZ')}`
+      `${link('foo', 'http://FOO')}bar${link('baz', 'http://BAZ')}`
     );
   });
 
@@ -148,10 +152,10 @@
           text: 'bug/$4',
         },
       }),
-      `bugs: ${link('bug/123', 'bug/123')} ${link('bug/234', 'bug/234')} ${link(
-        'bug/345',
-        'bug/345'
-      )}`
+      `bugs: ${internalLink('bug/123', 'bug/123')} ${internalLink(
+        'bug/234',
+        'bug/234'
+      )} ${internalLink('bug/345', 'bug/345')}`
     );
   });
 });
diff --git a/polygerrit-ui/app/utils/path-list-util.ts b/polygerrit-ui/app/utils/path-list-util.ts
index 5c523b3..31e17c9 100644
--- a/polygerrit-ui/app/utils/path-list-util.ts
+++ b/polygerrit-ui/app/utils/path-list-util.ts
@@ -8,6 +8,14 @@
 import {hasOwnProperty} from './common-util';
 
 export function specialFilePathCompare(a: string, b: string) {
+  // Patchset-level comments always go first.
+  if (a === SpecialFilePath.PATCHSET_LEVEL_COMMENTS) {
+    return -1;
+  }
+  if (b === SpecialFilePath.PATCHSET_LEVEL_COMMENTS) {
+    return 1;
+  }
+
   // The commit message always goes first.
   if (a === SpecialFilePath.COMMIT_MESSAGE) {
     return -1;
diff --git a/polygerrit-ui/app/utils/path-list-util_test.ts b/polygerrit-ui/app/utils/path-list-util_test.ts
index cdd8182..a7c1e53 100644
--- a/polygerrit-ui/app/utils/path-list-util_test.ts
+++ b/polygerrit-ui/app/utils/path-list-util_test.ts
@@ -21,12 +21,14 @@
     const testFiles = [
       '/a.h',
       '/MERGE_LIST',
+      SpecialFilePath.PATCHSET_LEVEL_COMMENTS,
       '/a.cpp',
       '/COMMIT_MSG',
       '/asdasd',
       '/mrPeanutbutter.py',
     ];
     assert.deepEqual(testFiles.sort(specialFilePathCompare), [
+      SpecialFilePath.PATCHSET_LEVEL_COMMENTS,
       '/COMMIT_MSG',
       '/MERGE_LIST',
       '/a.h',
diff --git a/polygerrit-ui/polygerrit.MODULE.bazel b/polygerrit-ui/polygerrit.MODULE.bazel
index 60a0c9b..d839cc8 100644
--- a/polygerrit-ui/polygerrit.MODULE.bazel
+++ b/polygerrit-ui/polygerrit.MODULE.bazel
@@ -52,6 +52,10 @@
 use_repo(node, "nodejs_linux_amd64")
 use_repo(node, "nodejs_windows_amd64")
 
+pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm")
+pnpm.pnpm(pnpm_version = "10.33.3")
+use_repo(pnpm, "pnpm")
+
 npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm")
 npm.npm_translate_lock(
     name = "npm",
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-review-prompt-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-review-prompt-dark.png
index 7bbdc5b..436031d 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-review-prompt-dark.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-review-prompt-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-review-prompt.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-review-prompt.png
index ff592ae..9aca5a8 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-review-prompt.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-review-prompt.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-chips-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-chips-dark.png
index 782a764..1cd5d8d 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-chips-dark.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-chips-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-chips.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-chips.png
index 1368d3d..569b3bb 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-chips.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-chips.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-comment-thread-unresolved-inline-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-comment-thread-unresolved-inline-dark.png
new file mode 100644
index 0000000..60eb357
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-comment-thread-unresolved-inline-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-comment-thread-unresolved-inline.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-comment-thread-unresolved-inline.png
new file mode 100644
index 0000000..3cde291
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-comment-thread-unresolved-inline.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-cancel-modal-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-cancel-modal-dark.png
new file mode 100644
index 0000000..fe699cb
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-cancel-modal-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-cancel-modal.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-cancel-modal.png
new file mode 100644
index 0000000..d6df16b
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-cancel-modal.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-normal-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-normal-dark.png
new file mode 100644
index 0000000..dc9928d
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-normal-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-normal.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-normal.png
new file mode 100644
index 0000000..c4b94d4
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-editor-view-normal.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-user-suggestion-fix-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-user-suggestion-fix-dark.png
index 42b693d..0e0b230 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-user-suggestion-fix-dark.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-user-suggestion-fix-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-user-suggestion-fix.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-user-suggestion-fix.png
index 6e4437b..3ae5bc2 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-user-suggestion-fix.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-user-suggestion-fix.png
Binary files differ
diff --git a/polygerrit-ui/web-test-runner.config.mjs b/polygerrit-ui/web-test-runner.config.mjs
index 2a6f695..804fd9a 100644
--- a/polygerrit-ui/web-test-runner.config.mjs
+++ b/polygerrit-ui/web-test-runner.config.mjs
@@ -9,7 +9,9 @@
 import { playwrightLauncher } from '@web/test-runner-playwright';
 
 const runUnderBazel = !!process.env['RUNFILES_DIR'];
-const diffThreshold = 0.01;
+// We set this to a non-zero value because of sub-pixel rendering noise that
+// can create false positives.
+const diffThreshold = .02;
 
 function testRunnerHtmlFactory(prefix) {
   return (testFramework) => `
@@ -36,11 +38,11 @@
     ];
   }
 
-  const runfilesRoot = path.dirname(process.cwd());
+  const cwd = process.cwd();
   return [
-    path.join(runfilesRoot, 'plugins_npm', 'node_modules'),
-    path.join(runfilesRoot, 'ui_npm', 'node_modules'),
-    path.join(runfilesRoot, 'ui_dev_npm', 'node_modules'),
+    path.join(cwd, 'plugins/node_modules'),
+    path.join(cwd, 'polygerrit-ui/node_modules'),
+    path.join(cwd, 'polygerrit-ui/app/node_modules'),
   ];
 }
 
diff --git a/prologtests/examples/BUILD b/prologtests/examples/BUILD
index 83c98e2..6b4749f 100644
--- a/prologtests/examples/BUILD
+++ b/prologtests/examples/BUILD
@@ -1,3 +1,5 @@
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
+
 package(default_visibility = ["//visibility:public"])
 
 sh_test(
diff --git a/proto/BUILD b/proto/BUILD
index 4b3eae4..d523718 100644
--- a/proto/BUILD
+++ b/proto/BUILD
@@ -1,5 +1,5 @@
 load("@protobuf//bazel:java_proto_library.bzl", "java_proto_library")
-load("@rules_proto//proto:defs.bzl", "proto_library")
+load("@protobuf//bazel:proto_library.bzl", "proto_library")
 
 proto_library(
     name = "cache_proto",
diff --git a/proto/testing/BUILD b/proto/testing/BUILD
index ee01e38..4428d27 100644
--- a/proto/testing/BUILD
+++ b/proto/testing/BUILD
@@ -1,5 +1,5 @@
 load("@protobuf//bazel:java_proto_library.bzl", "java_proto_library")
-load("@rules_proto//proto:defs.bzl", "proto_library")
+load("@protobuf//bazel:proto_library.bzl", "proto_library")
 
 proto_library(
     name = "test_proto",
diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index 17e0fcc..06f07b5 100644
--- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -87,7 +87,9 @@
   {else}
     <link rel="icon" type="image/x-icon" href="{$canonicalPath}/favicon.ico">{\n}
   {/if}
-  <link rel="manifest" href="{$manifestPath}">{\n}
+  {if $manifestPath}
+    <link rel="manifest" href="{$manifestPath}">{\n}
+  {/if}
   {if $changeRequestsPath}
     {if $defaultChangeDetailHex}
       <link rel="preload" href="{$canonicalPath}/{$changeRequestsPath}/detail?O={$defaultChangeDetailHex}" as="fetch" type="application/json" crossorigin="anonymous"/>{\n}
diff --git a/resources/com/google/gerrit/server/BUILD b/resources/com/google/gerrit/server/BUILD
index e92c4e1..a64ed82 100644
--- a/resources/com/google/gerrit/server/BUILD
+++ b/resources/com/google/gerrit/server/BUILD
@@ -1,3 +1,5 @@
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
+
 filegroup(
     name = "server",
     srcs = glob(
diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
index 66f21b9..f557cfd 100644
--- a/resources/com/google/gerrit/server/mime/mime-types.properties
+++ b/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -244,6 +244,7 @@
 svg = application/xml
 svh = text/x-systemverilog
 swift = text/x-swift
+tada = text/x-python
 tcl = text/x-tcl
 tex = text/x-latex
 text = text/plain
diff --git a/tools/BUILD b/tools/BUILD
index e1dc0c5..8b3429d 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -2,8 +2,8 @@
     "@bazel_tools//tools/jdk:default_java_toolchain.bzl",
     "default_java_toolchain",
 )
+load("@protobuf//bazel/toolchains:proto_lang_toolchain.bzl", "proto_lang_toolchain")
 load("@rules_java//java:defs.bzl", "java_package_configuration")
-load("@rules_proto//proto:defs.bzl", "proto_lang_toolchain")
 
 exports_files([
     "nongoogle.toml",
diff --git a/tools/bazlets.MODULE.bazel b/tools/bazlets.MODULE.bazel
index 06604d2..8bf405d 100644
--- a/tools/bazlets.MODULE.bazel
+++ b/tools/bazlets.MODULE.bazel
@@ -6,7 +6,7 @@
 bazel_dep(name = "com_googlesource_gerrit_bazlets")
 git_override(
     module_name = "com_googlesource_gerrit_bazlets",
-    commit = "774491d949e05f3d6157f6714f44003f7884f131",
+    commit = "2366eccc72aac1a966ef3446c98a80230a7b5d89",
     remote = "https://gerrit.googlesource.com/bazlets",
 )
 
diff --git a/tools/bzl/BUILD b/tools/bzl/BUILD
index 62f0adb..7e09203 100644
--- a/tools/bzl/BUILD
+++ b/tools/bzl/BUILD
@@ -1,3 +1,5 @@
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
+
 exports_files([
     "diff_allowlist.sh",
     "license-map.py",
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index c6e293b..bb5a34a 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -5,6 +5,7 @@
     _gerrit_js_bundle = "gerrit_js_bundle",
     _js_component = "js_component",
 )
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
 
 gerrit_js_bundle = _gerrit_js_bundle
 js_component = _js_component
@@ -29,7 +30,7 @@
         --run-screenshots.
     """
 
-    native.sh_test(
+    sh_test(
         name = name,
         size = "enormous",
         srcs = srcs,
diff --git a/tools/bzl/license.bzl b/tools/bzl/license.bzl
index f2ae73f..c8d0130 100644
--- a/tools/bzl/license.bzl
+++ b/tools/bzl/license.bzl
@@ -1,3 +1,5 @@
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
+
 """This file contains rules to generate and test the license map"""
 
 def normalize_target_name(target):
@@ -75,7 +77,7 @@
         # the license files themselves from this list.
         expression = 'rdeps(%s, "%s", 1) - rdeps(%s, "%s", 0)' % (target, forbidden, target, forbidden),
     )
-    native.sh_test(
+    sh_test(
         name = name,
         srcs = ["//tools/bzl:test_license.sh"],
         args = ["$(location :%s)" % txt],
diff --git a/tools/bzl/maven_jar.bzl b/tools/bzl/maven_jar.bzl
deleted file mode 100644
index e269632..0000000
--- a/tools/bzl/maven_jar.bzl
+++ /dev/null
@@ -1,189 +0,0 @@
-GERRIT = "GERRIT:"
-
-GERRIT_API = "GERRIT_API:"
-
-MAVEN_CENTRAL = "MAVEN_CENTRAL:"
-
-MAVEN_LOCAL = "MAVEN_LOCAL:"
-
-ECLIPSE = "ECLIPSE:"
-
-MAVEN_SNAPSHOT = "https://ossrh-staging-api.central.sonatype.com/content/repositories/snapshots"
-
-SNAPSHOT = "-SNAPSHOT-"
-
-def _maven_release(ctx, parts):
-    """induce jar and url name from maven coordinates."""
-    if len(parts) not in [3, 4]:
-        fail('%s:\nexpected id="groupId:artifactId:version[:classifier]"' %
-             ctx.attr.artifact)
-    if len(parts) == 4:
-        group, artifact, version, classifier = parts
-        file_version = version + "-" + classifier
-    else:
-        group, artifact, version = parts
-        file_version = version
-
-    repository = ctx.attr.repository
-
-    if "-SNAPSHOT-" in version:
-        start = version.index(SNAPSHOT)
-        end = start + len(SNAPSHOT) - 1
-
-        # file version without snapshot constant, but with post snapshot suffix
-        file_version = version[:start] + version[end:]
-
-        # version without post snapshot suffix
-        version = version[:end]
-
-        # overwrite the repository with Maven snapshot repository
-        repository = MAVEN_SNAPSHOT
-
-    jar = artifact.lower() + "-" + file_version
-
-    url = "/".join([
-        repository,
-        group.replace(".", "/"),
-        artifact,
-        version,
-        artifact + "-" + file_version,
-    ])
-
-    return jar, url
-
-# Creates a struct containing the different parts of an artifact's FQN
-def _create_coordinates(fully_qualified_name):
-    parts = fully_qualified_name.split(":")
-    packaging = None
-    classifier = None
-
-    if len(parts) == 3:
-        group_id, artifact_id, version = parts
-    elif len(parts) == 4:
-        group_id, artifact_id, version, classifier = parts
-    elif len(parts) == 5:
-        group_id, artifact_id, version, packaging, classifier = parts
-    else:
-        fail("Invalid fully qualified name for artifact: %s" % fully_qualified_name)
-
-    return struct(
-        fully_qualified_name = fully_qualified_name,
-        group_id = group_id,
-        artifact_id = artifact_id,
-        packaging = packaging,
-        classifier = classifier,
-        version = version,
-    )
-
-def _format_deps(attr, deps):
-    formatted_deps = ""
-    if deps:
-        if len(deps) == 1:
-            formatted_deps += "%s = [\'%s\']," % (attr, deps[0])
-        else:
-            formatted_deps += "%s = [\n" % attr
-            for dep in deps:
-                formatted_deps += "        \'%s\',\n" % dep
-            formatted_deps += "    ],"
-    return formatted_deps
-
-def _generate_build_files(ctx, binjar, srcjar):
-    header = "# DO NOT EDIT: automatically generated BUILD file for maven_jar rule %s" % ctx.name
-    srcjar_attr = ""
-    if srcjar:
-        srcjar_attr = 'srcjar = "%s",' % srcjar
-    contents = """
-{header}
-load("@rules_java//java:defs.bzl", "java_import")
-package(default_visibility = ['//visibility:public'])
-java_import(
-    name = 'jar',
-    jars = ['{binjar}'],
-    {srcjar_attr}
-    {deps}
-    {exports}
-)
-java_import(
-    name = 'neverlink',
-    jars = ['{binjar}'],
-    neverlink = 1,
-    {deps}
-    {exports}
-)
-\n""".format(
-        srcjar_attr = srcjar_attr,
-        header = header,
-        binjar = binjar,
-        deps = _format_deps("deps", ctx.attr.deps),
-        exports = _format_deps("exports", ctx.attr.exports),
-    )
-    if srcjar:
-        contents += """
-java_import(
-    name = 'src',
-    jars = ['{srcjar}'],
-)
-""".format(srcjar = srcjar)
-    ctx.file("%s/BUILD" % ctx.path("jar"), contents, False)
-
-def _maven_jar_impl(ctx):
-    """rule to download a Maven archive."""
-    coordinates = _create_coordinates(ctx.attr.artifact)
-
-    name = ctx.name
-    sha1 = ctx.attr.sha1
-
-    parts = ctx.attr.artifact.split(":")
-
-    jar, url = _maven_release(ctx, parts)
-
-    binjar = jar + ".jar"
-    binjar_path = ctx.path("/".join(["jar", binjar]))
-    binurl = url + ".jar"
-
-    python = ctx.which("python3")
-    script = ctx.path(ctx.attr._download_script)
-
-    args = [python, script, "-o", binjar_path, "-u", binurl]
-    if ctx.attr.sha1:
-        args.extend(["-v", sha1])
-    for x in ctx.attr.exclude:
-        args.extend(["-x", x])
-
-    out = ctx.execute(args)
-
-    if out.return_code:
-        fail("failed %s: %s" % (args, out.stderr))
-
-    srcjar = None
-    if ctx.attr.src_sha1 or ctx.attr.attach_source:
-        srcjar = jar + "-src.jar"
-        srcurl = url
-        if coordinates.classifier != None:
-            srcurl = url.replace("-" + coordinates.classifier, "")
-        srcurl += "-sources.jar"
-        srcjar_path = ctx.path("jar/" + srcjar)
-        args = [python, script, "-o", srcjar_path, "-u", srcurl]
-        if ctx.attr.src_sha1:
-            args.extend(["-v", ctx.attr.src_sha1])
-        out = ctx.execute(args)
-        if out.return_code:
-            fail("failed %s: %s" % (args, out.stderr))
-
-    _generate_build_files(ctx, binjar, srcjar)
-
-maven_jar = repository_rule(
-    attrs = {
-        "artifact": attr.string(mandatory = True),
-        "attach_source": attr.bool(default = True),
-        "exclude": attr.string_list(),
-        "repository": attr.string(default = MAVEN_CENTRAL),
-        "sha1": attr.string(),
-        "src_sha1": attr.string(),
-        "exports": attr.string_list(),
-        "deps": attr.string_list(),
-        "_download_script": attr.label(default = Label("//tools:download_file.py")),
-    },
-    local = True,
-    implementation = _maven_jar_impl,
-)
diff --git a/tools/bzl/pkg_war.bzl b/tools/bzl/pkg_war.bzl
index ebc2c1e..e99384b 100644
--- a/tools/bzl/pkg_war.bzl
+++ b/tools/bzl/pkg_war.bzl
@@ -71,6 +71,9 @@
     "libgerrit-prolog-common",
     "libjgit-archive",
     "libjgit-servlet",
+    # Core WARs do not package JGit LFS server jars today. If that changes,
+    # exclude both the canonical and EE8 LFS jars here as Gerrit-internal jars.
+    "libjgit-servlet-ee8",
     "libquery_parser",
     "libssh-apache",
     "log4j-config",
diff --git a/tools/bzl/war_checks.bzl b/tools/bzl/war_checks.bzl
index c121b7f..59acd24 100644
--- a/tools/bzl/war_checks.bzl
+++ b/tools/bzl/war_checks.bzl
@@ -1,3 +1,5 @@
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
+
 """Reusable checks for WAR content guardrails."""
 
 def war_jars_allowlist_test(name, war_jars_manifest, allowlist, **kwargs):
@@ -9,7 +11,7 @@
         allowlist: label of the checked-in allowlist file
         **kwargs: forwarded to sh_test
     """
-    native.sh_test(
+    sh_test(
         name = name,
         srcs = ["//tools/bzl:diff_allowlist.sh"],
         args = [
diff --git a/tools/deps.toml b/tools/deps.toml
index 3b4b7af..e500cd6 100644
--- a/tools/deps.toml
+++ b/tools/deps.toml
@@ -2,13 +2,13 @@
 antlr = "3.5.2"
 autoValueGson = "1.3.1"
 bouncyCastle = "1.84"
-byteBuddy = "1.18.8"
+byteBuddy = "1.18.11"
 caffeine = "2.9.2"
 commonmark = "0.24.0"
 gitiles = "1.6.0"
 greenmail = "1.5.5"
 httpcomp = "4.5.14"
-jetty = "9.4.57.v20241219"
+jetty = "12.1.11"
 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/download_file.py b/tools/download_file.py
index 2af2c07..7abbd1b 100755
--- a/tools/download_file.py
+++ b/tools/download_file.py
@@ -86,11 +86,11 @@
 parser.add_argument('--exclude_java_sources', action='store_true')
 args = parser.parse_args()
 
-root_dir = args.o
+root_dir = path.abspath(path.dirname(args.o))
 while root_dir and path.dirname(root_dir) != root_dir:
-    root_dir, n = path.split(root_dir)
-    if n == 'WORKSPACE':
+    if path.exists(path.join(root_dir, 'MODULE.bazel')):
         break
+    root_dir = path.dirname(root_dir)
 
 redirects = download_properties(root_dir)
 cache_ent = cache_entry(args)
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index 12567ba..6bd0763 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -200,8 +200,6 @@
         classpathentry('src', 'modules/jgit/org.eclipse.jgit.archive/src',
             excluding='org/eclipse/jgit/archive/FormatActivator.java')
         classpathentry('src', 'modules/jgit/org.eclipse.jgit.archive/resources')
-        classpathentry('src', 'modules/jgit/org.eclipse.jgit.http.server/src')
-        classpathentry('src', 'modules/jgit/org.eclipse.jgit.http.server/resources')
         classpathentry('src', 'modules/jgit/org.eclipse.jgit.junit/src')
         classpathentry('src', 'modules/jgit/org.eclipse.jgit.ssh.apache/src')
         classpathentry('src', 'modules/jgit/org.eclipse.jgit.ssh.apache/resources')
@@ -261,6 +259,21 @@
     for p in source_cp:
         source_by_basename[_normalize_jar_basename(p)] = p
 
+    generated_jgit_sources = {
+        "org.eclipse.jgit.http.server.ee8/libjgit-servlet-ee8.jar":
+            "org.eclipse.jgit.http.server.ee8/jgit-http-server-ee8-srcs.srcjar",
+        "org.eclipse.jgit.lfs.server.ee8/libjgit-lfs-server-ee8.jar":
+            "org.eclipse.jgit.lfs.server.ee8/jgit-lfs-server-ee8-srcs.srcjar",
+    }
+
+    def generated_jgit_source(jar):
+        for jar_suffix, src_suffix in generated_jgit_sources.items():
+            if jar.endswith(jar_suffix):
+                candidate = jar[:-len(jar_suffix)] + src_suffix
+                if os.path.exists(candidate):
+                    return candidate
+        return None
+
     for p in runtime_cp:
         if p.endswith('-src.jar'):
             continue
@@ -339,12 +352,12 @@
             if os.path.basename(j) == "libjava-prettify.jar" and "/external/" in j:
                 continue
 
-            s = None
+            s = generated_jgit_source(j)
 
             # Attach sources using the classpath_collector output from rules_jvm_external.
             # This replaces the previous heuristic-based source lookup.
             key = _normalize_jar_basename(j)
-            if key in source_by_basename:
+            if not s and key in source_by_basename:
                 sp = _resolve_repo_path(output_base, source_by_basename[key])
                 s = sp
 
@@ -441,4 +454,3 @@
 except KeyboardInterrupt:
     print('Interrupted by user', file=sys.stderr)
     exit(1)
-
diff --git a/tools/java_deps.MODULE.bazel b/tools/java_deps.MODULE.bazel
index d456542..378b378 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",
 ]
 
 [
@@ -143,6 +137,38 @@
     for coord in _GERRIT_FORCED_ARTIFACTS
 ]
 
+# We consume Guice's unshaded artifact (published as the "classes"
+# classifier) plus an external OW2 ASM (see //lib/guice and tools/nongoogle.toml),
+# because the ASM shaded into the default guice jar cannot read Java 25 class
+# files and corrupts Guice's error-message source formatting. The external ASM
+# must be >= 9.8, the first release with Java 25 (class major 69) support;
+# Gerrit pins 9.9.1.
+#
+# That only holds if the plain (fat) com.google.inject:guice never reaches the
+# classpath. These artifacts pull it in transitively, so strip that edge from
+# each; they resolve Guice solely from the //lib/guice classes-classifier jar.
+# Keep this list in sync with everything that depends on Guice transitively --
+# a missed entry silently reintroduces the shaded ASM.
+#
+# TODO(davido): Drop these exclusions (and the classes classifier) once Guice
+# ships a default jar with an updated shaded ASM that reads current class
+# files: https://github.com/google/guice/issues/1926
+_GUICE_FAT_JAR_CONSUMERS = [
+    "com.google.inject.extensions:guice-assistedinject",
+    "com.google.inject.extensions:guice-servlet",
+    "com.google.template:soy",
+    "org.openid4java:openid4java",
+]
+
+[
+    maven.amend_artifact(
+        name = "external_deps",
+        coordinates = coord,
+        exclusions = ["com.google.inject:guice"],
+    )
+    for coord in _GUICE_FAT_JAR_CONSUMERS
+]
+
 # Empty maven.install call to apply the configuration to the installation from
 # toml files.
 maven.install(
diff --git a/tools/jgit-ee8/BUILD b/tools/jgit-ee8/BUILD
new file mode 100644
index 0000000..fa80f5c
--- /dev/null
+++ b/tools/jgit-ee8/BUILD
@@ -0,0 +1,21 @@
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
+
+# Guard: //:headless must not depend on the canonical Jakarta JGit
+# servlet jar. //lib:jgit-servlet exports the EE8 bridge; both jars
+# share class FQNs and cannot co-exist on a classpath.
+
+genquery(
+    name = "original_jgit_servlet_path",
+    expression = "somepath(//:headless, @jgit//org.eclipse.jgit.http.server:jgit-servlet)",
+    scope = [
+        "//:headless",
+        "@jgit//org.eclipse.jgit.http.server:jgit-servlet",
+    ],
+)
+
+sh_test(
+    name = "classpath_duplicates_test",
+    srcs = ["classpath_duplicates_test.sh"],
+    args = ["$(location :original_jgit_servlet_path)"],
+    data = [":original_jgit_servlet_path"],
+)
diff --git a/tools/jgit-ee8/classpath_duplicates_test.sh b/tools/jgit-ee8/classpath_duplicates_test.sh
new file mode 100755
index 0000000..8c891a3
--- /dev/null
+++ b/tools/jgit-ee8/classpath_duplicates_test.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2026 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+for query_output in "$@"; do
+  if [[ -s "${query_output}" ]]; then
+    echo "Unexpected path to original JGit servlet-facing target:" >&2
+    cat "${query_output}" >&2
+    exit 1
+  fi
+done
diff --git a/tools/nongoogle.toml b/tools/nongoogle.toml
index 28bae40..1f2e24a 100644
--- a/tools/nongoogle.toml
+++ b/tools/nongoogle.toml
@@ -3,8 +3,8 @@
 
 [versions]
 autoCommon = "1.2.2"
-autoFactory = "1.0.1"
-autoValue= "1.11.0"
+autoFactory = "1.1.0"
+autoValue= "1.11.1"
 flogger = "0.8"
 # Also update the GUAVA version in java/com/google/gerrit/extensions/BUILD
 # to keep Bazel and extension dependencies in sync.
@@ -12,7 +12,7 @@
 guice = "6.0.0"
 lucene = "10.4.0"
 slf4j = "2.0.18"
-sshd = "2.17.1"
+sshd = "2.19.0"
 truth = "1.4.4"
 
 [libraries]
@@ -32,7 +32,7 @@
 guava = { module = "com.google.guava:guava", version.ref = "guava" }
 guava-testlib = { module = "com.google.guava:guava-testlib", version.ref = "guava" }
 guice-assistedinject = { module = "com.google.inject.extensions:guice-assistedinject", version.ref = "guice" }
-guice-library = { module = "com.google.inject:guice", version.ref = "guice" }
+guice-library = { module = "com.google.inject:guice", version.ref = "guice", classifier = "classes" }
 guice-servlet = { module = "com.google.inject.extensions:guice-servlet", version.ref = "guice" }
 h2 = { module = "com.h2database:h2", version = "2.4.240" }
 hamcrest = { module = "org.hamcrest:hamcrest", version = "3.0" }
@@ -49,10 +49,10 @@
 lucene-core = { module = "org.apache.lucene:lucene-core", version.ref = "lucene" }
 lucene-misc = { module = "org.apache.lucene:lucene-misc", version.ref = "lucene" }
 lucene-queryparser = { module = "org.apache.lucene:lucene-queryparser", version.ref = "lucene" }
-mina-core = { module = "org.apache.mina:mina-core", version = "2.2.4" }
+mina-core = { module = "org.apache.mina:mina-core", version = "2.2.9" }
 nekohtml = { module = "net.sourceforge.nekohtml:nekohtml", version = "1.9.10" }
 openid-consumer = { module = "org.openid4java:openid4java", version = "1.0.0" }
-protobuf-java = { module = "com.google.protobuf:protobuf-java", version = "4.34.1" }
+protobuf-java = { module = "com.google.protobuf:protobuf-java", version = "4.35.0" }
 soy = { module = "com.google.template:soy", version = "2024-01-30" }
 sshd-mina = { module = "org.apache.sshd:sshd-mina", version.ref = "sshd" }
 sshd-osgi = { module = "org.apache.sshd:sshd-osgi", version.ref = "sshd" }
diff --git a/tools/util.py b/tools/util.py
index 4c7c3b6..c77f353 100644
--- a/tools/util.py
+++ b/tools/util.py
@@ -35,8 +35,7 @@
 
     A special case is supported, when prefix neither exists in
     REPO_ROOTS, no in redirects set: the url is returned as is.
-    This enables plugins to pass custom maven_repository URL as is
-    directly to maven_jar().
+    This enables plugins to pass custom maven_repository URL.
 
     Returns a resolved path for Maven artifact.
     """