diff --git a/.github/scripts/coverage_history.py b/.github/scripts/coverage_history.py
index a8a4d64aa..461129609 100755
--- a/.github/scripts/coverage_history.py
+++ b/.github/scripts/coverage_history.py
@@ -27,6 +27,8 @@
summary print the parsed coverage as JSON (debugging / ad-hoc use)
append append one row per module to the append-only history CSV
+ merge re-apply locally added rows onto a refreshed history CSV, used
+ when a push to the coverage-history branch is rejected
render regenerate the human-readable COVERAGE.md from that CSV
compare render a markdown coverage diff against the last recorded entry,
for posting on a pull request
@@ -354,6 +356,37 @@ def render_current(rows: list[dict], suite: str, heading: str, note: str) -> lis
return lines
+def row_key(row: dict) -> tuple[str, str, str]:
+ """Identity of a history row: one module, one suite, one commit."""
+ return (row.get("commit", ""), row.get("suite", ""), row.get("module", ""))
+
+
+def cmd_merge(args: argparse.Namespace) -> None:
+ """Re-apply locally added rows on top of a refreshed history file.
+
+ Two runs appending to the end of the CSV cannot be merged by git: both
+ touch the same region and the rebase conflicts. The rows are independent
+ records, though, so the correct resolution is simply "keep both". This
+ reads the rows we added, rebases them onto whatever is now on the branch,
+ and drops any that are already there.
+ """
+ base = read_history(args.csv)
+ ours = read_history(args.ours)
+
+ present = {row_key(row) for row in base}
+ added = [row for row in ours if row_key(row) not in present]
+ if not added:
+ print("No local rows to re-apply.")
+ return
+
+ with open(args.csv, "a", newline="", encoding="utf-8") as handle:
+ writer = csv.DictWriter(handle, fieldnames=CSV_FIELDS, lineterminator="\n")
+ if not base:
+ writer.writeheader()
+ writer.writerows(added)
+ print(f"Re-applied {len(added)} row(s) onto {args.csv}")
+
+
def cmd_render(args: argparse.Namespace) -> None:
rows = read_history(args.csv)
if not rows:
@@ -508,7 +541,7 @@ def delta(current: float, module: str, field: str) -> str:
lines += [
"",
f"Line and branch coverage from {args.suite} test reports. History "
- f"is recorded in `coverage/history.csv` after each merge to "
+ f"is recorded on the `coverage-history` branch after each merge to "
f"`{args.base}`.",
"",
]
@@ -563,6 +596,13 @@ def main() -> None:
)
append.set_defaults(func=cmd_append)
+ merge = subparsers.add_parser(
+ "merge", help="re-apply locally added rows onto a refreshed history CSV"
+ )
+ merge.add_argument("--csv", default=DEFAULT_CSV, help="refreshed file to merge into")
+ merge.add_argument("--ours", required=True, help="our copy, holding the new rows")
+ merge.set_defaults(func=cmd_merge)
+
render = subparsers.add_parser("render", help="regenerate COVERAGE.md from the CSV")
render.add_argument("--csv", default=DEFAULT_CSV)
render.add_argument("--out", default=DEFAULT_MARKDOWN)
diff --git a/.github/scripts/push_coverage_history.sh b/.github/scripts/push_coverage_history.sh
new file mode 100755
index 000000000..238dc2fdd
--- /dev/null
+++ b/.github/scripts/push_coverage_history.sh
@@ -0,0 +1,88 @@
+#!/usr/bin/env bash
+#
+# Copyright 2026 Google LLC
+#
+# 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.
+#
+# Commits and pushes the coverage history to its own branch.
+#
+# Usage: push_coverage_history.sh ""
+#
+# The history deliberately does not live on main. Branch protection there uses
+# strict required status checks, so every commit pushed to main marks all open
+# pull requests out-of-date and forces contributors to update their branch and
+# re-run CI.
+#
+# If the branch moved under us, the update is redone on top of the new tip
+# rather than rebased. Both sides append to the end of history.csv, so a rebase
+# conflicts every time; the rows are independent records, so the right
+# resolution is to keep both and regenerate the report.
+
+set -euo pipefail
+
+MESSAGE="${1:?commit message required}"
+DATA_DIR="${DATA_DIR:-coverage-data}"
+DATA_BRANCH="${DATA_BRANCH:-coverage-history}"
+SCRIPT="${SCRIPT:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/coverage_history.py}"
+
+# Every generated file published on the data branch. COVERAGE.md is derived
+# from history.csv, so it is regenerated, not merged.
+FILES=(history.csv COVERAGE.md)
+
+cd "$DATA_DIR"
+
+git config user.name 'googlemaps-bot'
+git config user.email 'googlemaps-bot@google.com'
+
+# Stage first and compare the index: a newly generated file is untracked, and
+# "git diff" does not see untracked files, so checking before staging would
+# skip the commit the first time a new output appears.
+git add -- "${FILES[@]}"
+
+if git diff --cached --quiet -- "${FILES[@]}"; then
+ echo "Coverage history unchanged; nothing to commit."
+ exit 0
+fi
+
+git commit -m "$MESSAGE"
+
+for attempt in 1 2 3; do
+ if git push origin "HEAD:$DATA_BRANCH"; then
+ echo "Pushed to $DATA_BRANCH."
+ exit 0
+ fi
+
+ echo "Push rejected (attempt $attempt); redoing the update on the new tip."
+
+ # Keep our rows, take the branch as it now stands, then put ours back.
+ OURS="${RUNNER_TEMP:-/tmp}/coverage-history-ours.csv"
+ cp history.csv "$OURS"
+
+ git fetch origin "$DATA_BRANCH"
+ git reset --hard "origin/$DATA_BRANCH"
+
+ python3 "$SCRIPT" merge --csv history.csv --ours "$OURS"
+ python3 "$SCRIPT" render --csv history.csv --out COVERAGE.md
+
+ git add -- "${FILES[@]}"
+
+ if git diff --cached --quiet -- "${FILES[@]}"; then
+ echo "Our rows are already on $DATA_BRANCH; nothing left to push."
+ exit 0
+ fi
+
+ git commit -m "$MESSAGE"
+done
+
+echo "Could not push to $DATA_BRANCH after 3 attempts." >&2
+exit 1
diff --git a/.github/workflows/coverage-history.yml b/.github/workflows/coverage-history.yml
index 332d2415a..aa4a6386c 100644
--- a/.github/workflows/coverage-history.yml
+++ b/.github/workflows/coverage-history.yml
@@ -12,9 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Records unit test coverage in coverage/history.csv after every merge to main,
-# so coverage can be tracked over time. Release-please commits and the
-# workflow's own commits are skipped.
+# Records unit test coverage after every merge to main.
+#
+# The history is written to the "coverage-history" branch, never to main.
+# Branch protection on main uses strict required status checks, so any commit
+# pushed to main marks every open pull request out-of-date and forces
+# contributors to update their branch and re-run CI. Keeping the data on its
+# own branch means recording coverage costs contributors nothing.
+#
+# The first run creates the branch, seeded with the history that used to live
+# in coverage/ on main, so the trend stays continuous.
name: Record coverage history
on:
@@ -25,31 +32,117 @@ on:
permissions:
contents: read
-# Serialize runs so two quick merges cannot race on pushing the history file.
+# Serialize runs so two merges cannot race on pushing the history branch.
concurrency:
group: coverage-history
cancel-in-progress: false
+env:
+ # Branch holding history.csv and COVERAGE.md.
+ DATA_BRANCH: coverage-history
+
jobs:
record:
- # Skip release-please's release commits and this workflow's own commits.
- # (The commit below carries [skip ci] as well, and pushes made with
- # GITHUB_TOKEN do not trigger workflows, so this is belt and braces.)
+ # Skip release-please's release commits. The history no longer lands on
+ # main, so this workflow cannot retrigger itself.
if: >-
- !startsWith(github.event.head_commit.message, 'chore(main): release') &&
- !startsWith(github.event.head_commit.message, 'chore: update coverage history')
+ !startsWith(github.event.head_commit.message, 'chore(main): release')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- # Credentials are deliberately persisted here: the final step pushes the
- # updated history back to main, so persist-credentials must stay enabled.
- name: Checkout Repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
+ persist-credentials: false
+
+ - name: Check for the coverage history branch
+ id: branch
+ run: |
+ set +e
+ git ls-remote --exit-code --heads origin "$DATA_BRANCH" > /dev/null
+ STATUS=$?
+ set -e
+ case "$STATUS" in
+ 0) echo "exists=true" >> "$GITHUB_OUTPUT" ;;
+ 2) echo "exists=false" >> "$GITHUB_OUTPUT"
+ echo "$DATA_BRANCH does not exist yet; it will be created." ;;
+ *) echo "Could not query $DATA_BRANCH (git exit $STATUS)." >&2
+ exit 1 ;;
+ esac
+
+ # Credentials are deliberately persisted on this checkout only: the steps
+ # below push the updated history to the data branch.
+ - name: Checkout coverage history branch
+ if: steps.branch.outputs.exists == 'true'
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ with:
+ ref: ${{ env.DATA_BRANCH }}
+ path: coverage-data
token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO || secrets.GITHUB_TOKEN }}
+ # First run only. Full history is fetched so the seed step can find the
+ # last version of coverage/ on main, even after it has been removed there.
+ - name: Checkout main to seed the coverage history branch
+ if: steps.branch.outputs.exists == 'false'
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ with:
+ path: coverage-data
+ fetch-depth: 0
+ token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO || secrets.GITHUB_TOKEN }}
+
+ - name: Seed the coverage history branch from main
+ if: steps.branch.outputs.exists == 'false'
+ working-directory: coverage-data
+ run: |
+ # The newest commit that touched coverage/history.csv. If that commit
+ # removed the file, its parent holds the last version.
+ SOURCE=$(git rev-list -1 HEAD -- coverage/history.csv)
+ if [ -n "$SOURCE" ] && ! git cat-file -e "$SOURCE:coverage/history.csv" 2>/dev/null; then
+ SOURCE="$SOURCE^"
+ fi
+
+ git checkout --orphan "$DATA_BRANCH"
+ git rm -rfq .
+ git clean -fdxq
+
+ if [ -n "$SOURCE" ]; then
+ echo "Seeding from coverage/ at $(git rev-parse --short "$SOURCE")."
+ git show "$SOURCE:coverage/history.csv" > history.csv
+ git show "$SOURCE:coverage/COVERAGE.md" > COVERAGE.md 2>/dev/null || rm -f COVERAGE.md
+ else
+ echo "No earlier coverage history found; starting empty."
+ fi
+
+ cat > README.md <<'README'
+ # Coverage history
+
+ Generated data. Do not edit by hand, and do not open pull requests against
+ this branch.
+
+ | File | Contents |
+ | --- | --- |
+ | `history.csv` | One row per module per merged commit (unit tests). |
+ | `COVERAGE.md` | Human-readable report, regenerated from `history.csv`. |
+
+ Both are written by `.github/scripts/coverage_history.py` on `main`, run by the
+ `Record coverage history` workflow after every merge to `main`.
+
+ This data lives on its own branch rather than on `main` on purpose. Branch
+ protection on `main` uses strict required status checks, so every commit
+ pushed there marks all open pull requests out-of-date and forces contributors
+ to update their branch and re-run CI. Keeping coverage data here means
+ recording it costs contributors nothing.
+ README
+
+ git config user.name 'googlemaps-bot'
+ git config user.email 'googlemaps-bot@google.com'
+ git add -- README.md
+ [ -f history.csv ] && git add -- history.csv
+ [ -f COVERAGE.md ] && git add -- COVERAGE.md
+ git commit -qm 'chore: seed coverage history from main'
+
- name: Set up JDK 21
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
@@ -62,33 +155,36 @@ jobs:
- name: Create dummy secrets.properties
run: echo "MAPS_API_KEY=dummy" > secrets.properties
- - name: Run unit tests and generate coverage reports
- run: ./gradlew koverXmlReportDebug --stacktrace
-
- - name: Append to coverage history
- env:
- COMMIT_SHA: ${{ github.sha }}
+ - name: Resolve commit metadata
+ id: meta
run: |
SUBJECT=$(git log -1 --pretty=%s)
# Squash-merged PRs end their subject with "(#123)". The number is
# recorded in its own column, so drop it from the subject text.
PR=$(printf '%s' "$SUBJECT" | sed -n 's/.*(#\([0-9]\{1,\}\))$/\1/p')
SUBJECT=$(printf '%s' "$SUBJECT" | sed 's/ (#[0-9]\{1,\})$//')
+ {
+ echo "pr=$PR"
+ echo "subject<> "$GITHUB_OUTPUT"
- python3 .github/scripts/coverage_history.py append --suite unit \
- --commit "$COMMIT_SHA" --pr "$PR" --subject "$SUBJECT"
+ - name: Run unit tests and generate coverage reports
+ run: ./gradlew koverXmlReportDebug --stacktrace
- python3 .github/scripts/coverage_history.py render
+ - name: Record unit coverage
+ env:
+ COMMIT_SHA: ${{ github.sha }}
+ PR_NUMBER: ${{ steps.meta.outputs.pr }}
+ SUBJECT: ${{ steps.meta.outputs.subject }}
+ run: |
+ python3 .github/scripts/coverage_history.py append --suite unit \
+ --csv coverage-data/history.csv \
+ --commit "$COMMIT_SHA" --pr "$PR_NUMBER" --subject "$SUBJECT"
+ python3 .github/scripts/coverage_history.py render \
+ --csv coverage-data/history.csv --out coverage-data/COVERAGE.md
- - name: Commit coverage history
+ - name: Push unit coverage
run: |
- git config user.name 'googlemaps-bot'
- git config user.email 'googlemaps-bot@google.com'
- git add coverage/history.csv coverage/COVERAGE.md
- if git diff --staged --quiet; then
- echo "Coverage history unchanged; nothing to commit."
- exit 0
- fi
- git commit -m 'chore: update coverage history [skip ci]'
- git pull --rebase origin main
- git push origin HEAD:main
+ .github/scripts/push_coverage_history.sh 'chore: record unit coverage'
diff --git a/AGENTS.md b/AGENTS.md
index 27ab3abbe..1ca9b54e6 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -36,8 +36,14 @@ Shared Gradle conventions are in `build-logic/` (included build).
Coverage is tracked over time. After every merge to `main`, the
`Record coverage history` workflow appends a row per module to
-`coverage/history.csv` and regenerates `coverage/COVERAGE.md`. Both files are
-generated by `.github/scripts/coverage_history.py`. Never edit them by hand.
+`history.csv` and regenerates `COVERAGE.md`, both of which live on the
+[`coverage-history`](../../tree/coverage-history) branch. They are generated by
+`.github/scripts/coverage_history.py`. Never edit them by hand.
+
+The data is kept off `main` deliberately. Branch protection on `main` uses
+strict required status checks, so any commit pushed there marks every open
+pull request out-of-date and forces contributors to update their branch and
+re-run CI.
The history records unit test coverage. The CSV carries a `suite` column so
emulator coverage could be added later without changing the format.
diff --git a/coverage/COVERAGE.md b/coverage/COVERAGE.md
deleted file mode 100644
index 7234bcd88..000000000
--- a/coverage/COVERAGE.md
+++ /dev/null
@@ -1,53 +0,0 @@
-
-# Test coverage
-
-Raw data lives in [`history.csv`](history.csv), one row per module per
-suite per merged commit. This page is regenerated from it and should not
-be edited by hand.
-
-## Current
-
-### Unit tests
-
-JVM tests run by `./gradlew koverXmlReportDebug`, covering `library`, `clustering`, `data`, `heatmaps`, `ui`.
-
-Measured at `466bfa5`, recorded 2026-10-09T21:49:19Z.
-
-| Module | Lines | Line % | | Branches | Branch % |
-| --- | ---: | ---: | --- | ---: | ---: |
-| `clustering` | 607/1,849 | 32.83% | `███████░░░░░░░░░░░░░` | 181/852 | 21.24% |
-| `data` | 1,964/2,950 | 66.58% | `█████████████░░░░░░░` | 1,014/2,461 | 41.20% |
-| `heatmaps` | 257/314 | 81.85% | `████████████████░░░░` | 134/162 | 82.72% |
-| `library` | 1,023/1,172 | 87.29% | `█████████████████░░░` | 311/374 | 83.16% |
-| `ui` | 122/151 | 80.79% | `████████████████░░░░` | 48/68 | 70.59% |
-| **TOTAL** | 3,973/6,436 | 61.73% | `████████████░░░░░░░░` | 1,688/3,917 | 43.09% |
-
-## Trend (last 30 commits)
-
-Total line coverage, newest first.
-
-| Date | Commit | PR | Line % | Change | Subject |
-| --- | --- | --- | ---: | ---: | --- |
-| 2026-10-09 | `466bfa5` | n/a | 61.73% | 0.00 | build(deps): bump the github-actions-minor-and-patch group across 1 directory with 2 updates |
-| 2026-10-08 | `ab87997` | #1809 | 61.73% | +4.34 | fix(data): apply StyleMap styles and fix four KML and GeoJSON style bugs |
-| 2026-10-08 | `6d222ff` | #1812 | 57.39% | 0.00 | build(deps): bump the gradle-minor-and-patch group with 2 updates |
-| 2026-10-05 | `bbdbe83` | #1796 | 57.39% | 0.00 | build(deps): bump the gradle-minor-and-patch group with 5 updates |
-| 2026-10-01 | `145d2fa` | #1807 | 57.39% | 0.00 | ci: add a workflow to publish snapshot builds |
-| 2026-10-01 | `ad2ba13` | #1810 | 57.39% | 0.00 | docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes |
-| 2026-10-01 | `48d3371` | #1805 | 57.39% | 0.00 | test: add end-to-end demo smoke test |
-| 2026-10-01 | `2bd91d5` | #1806 | 57.39% | 0.00 | ci: merge generated docs PRs from the docs workflow |
-| 2026-09-30 | `de264f8` | #1716 | 57.39% | +4.18 | feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0) |
-| 2026-09-30 | `657fc55` | #1802 | 53.21% | 0.00 | build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions |
-| 2026-09-30 | `f805f5e` | #1803 | 53.21% | +0.02 | fix(library): match reference rounding and unsigned varints in flexible polyline |
-| 2026-09-30 | `50e88c9` | #1801 | 53.19% | +1.14 | feat(library): add flexible polyline encoding and decoding |
-| 2026-09-24 | `56b3c24` | #1792 | 52.05% | +0.14 | fix(data): enforce decompression limit and reject payload on KMZ directory entries |
-| 2026-09-23 | `51c59bf` | #1775 | 51.91% | 0.00 | ci: support GEMINI_MODEL repository variable in triage-issue workflow |
-| 2026-09-23 | `651c6e0` | #1791 | 51.91% | 0.00 | build(deps): bump org.robolectric:robolectric |
-| 2026-09-22 | `2e33859` | #1789 | 51.91% | 0.00 | ci: group dependabot minor and patch updates |
-| 2026-09-22 | `c3fa410` | #1787 | 51.91% | +1.36 | feat: add z-index support across geometry styles and layer mappers |
-| 2026-09-21 | `5557301` | #1762 | 50.55% | 0.00 | build(deps): bump com.google.android.libraries.navigation:navigation |
-| 2026-09-18 | `a70f257` | #1781 | 50.55% | 0.00 | build(deps): bump actions/setup-java from 5.7.0 to 6.0.1 |
-| 2026-09-18 | `9056fd9` | #1785 | 50.55% | +0.02 | feat: add polygon z-index support |
-| 2026-09-16 | `3884f22` | #1783 | 50.53% | 0.00 | build(deps): bump androidx.compose:compose-bom |
-| 2026-09-15 | `6015319` | #1778 | 50.53% | 0.00 | ci: record unit test coverage history |
-| 2026-09-15 | `51e9e43` | #1772 | 50.53% | n/a | chore(ci): harden Gemini issue triage against prompt injection |
diff --git a/coverage/history.csv b/coverage/history.csv
deleted file mode 100644
index ad009bd9f..000000000
--- a/coverage/history.csv
+++ /dev/null
@@ -1,139 +0,0 @@
-timestamp,commit,pr,subject,suite,module,lines_covered,lines_total,line_pct,branches_covered,branches_total,branch_pct,instructions_covered,instructions_total,instruction_pct
-2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,clustering,553,1779,31.08,169,834,20.26,3115,9947,31.32
-2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,data,1309,2642,49.55,797,2223,35.85,9451,19410,48.69
-2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,TOTAL,2731,5405,50.53,1340,3529,37.97,17702,35182,50.32
-2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32
-2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,data,1309,2642,49.55,797,2223,35.85,9451,19410,48.69
-2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,TOTAL,2731,5405,50.53,1341,3529,38.0,17702,35182,50.32
-2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,clustering,553,1779,31.08,171,834,20.5,3115,9947,31.32
-2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,data,1309,2642,49.55,797,2223,35.85,9451,19410,48.69
-2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,TOTAL,2731,5405,50.53,1342,3529,38.03,17702,35182,50.32
-2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,clustering,553,1779,31.08,169,834,20.26,3115,9947,31.32
-2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,data,1311,2644,49.58,797,2223,35.85,9473,19433,48.75
-2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,TOTAL,2733,5407,50.55,1340,3529,37.97,17724,35205,50.35
-2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32
-2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,data,1311,2644,49.58,797,2223,35.85,9473,19433,48.75
-2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,TOTAL,2733,5407,50.55,1341,3529,38.0,17724,35205,50.35
-2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,clustering,553,1779,31.08,169,834,20.26,3115,9947,31.32
-2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,data,1311,2644,49.58,797,2223,35.85,9473,19433,48.75
-2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,TOTAL,2733,5407,50.55,1340,3529,37.97,17724,35205,50.35
-2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32
-2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,data,1392,2658,52.37,834,2241,37.22,9921,19509,50.85
-2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,TOTAL,2814,5421,51.91,1378,3547,38.85,18172,35281,51.51
-2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32
-2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,data,1392,2658,52.37,834,2241,37.22,9921,19509,50.85
-2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,TOTAL,2814,5421,51.91,1378,3547,38.85,18172,35281,51.51
-2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32
-2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,data,1392,2658,52.37,834,2241,37.22,9921,19509,50.85
-2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,TOTAL,2814,5421,51.91,1378,3547,38.85,18172,35281,51.51
-2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32
-2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,data,1392,2658,52.37,834,2241,37.22,9921,19509,50.85
-2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,TOTAL,2814,5421,51.91,1378,3547,38.85,18172,35281,51.51
-2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32
-2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,data,1402,2663,52.65,840,2245,37.42,9961,19531,51.0
-2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22
-2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,TOTAL,2824,5426,52.05,1384,3551,38.97,18212,35303,51.59
-2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,clustering,553,1779,31.08,171,834,20.5,3115,9947,31.32
-2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,data,1402,2663,52.65,840,2245,37.42,9961,19531,51.0
-2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,library,649,702,92.45,274,334,82.04,3693,3989,92.58
-2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,TOTAL,2961,5567,53.19,1448,3625,39.94,19015,36124,52.64
-2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,clustering,553,1779,31.08,171,834,20.5,3115,9947,31.32
-2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,data,1402,2663,52.65,840,2245,37.42,9961,19531,51.0
-2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,library,651,704,92.47,276,336,82.14,3706,4002,92.6
-2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,TOTAL,2963,5569,53.21,1450,3627,39.98,19028,36137,52.66
-2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32
-2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,data,1402,2663,52.65,840,2245,37.42,9961,19531,51.0
-2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13
-2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,library,651,704,92.47,276,336,82.14,3706,4002,92.6
-2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,ui,115,148,77.7,43,66,65.15,561,745,75.3
-2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,TOTAL,2963,5569,53.21,1449,3627,39.95,19028,36137,52.66
-2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,clustering,607,1849,32.83,180,852,21.13,3410,10253,33.26
-2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29
-2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62
-2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61
-2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,ui,122,151,80.79,48,68,70.59,639,791,80.78
-2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,TOTAL,3687,6424,57.39,1619,3901,41.5,23084,40531,56.95
-2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26
-2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29
-2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62
-2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61
-2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78
-2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95
-2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26
-2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29
-2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62
-2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61
-2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78
-2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95
-2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26
-2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29
-2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62
-2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61
-2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78
-2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95
-2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26
-2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29
-2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62
-2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61
-2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78
-2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95
-2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,clustering,607,1849,32.83,182,852,21.36,3410,10253,33.26
-2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29
-2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62
-2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61
-2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78
-2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,TOTAL,3687,6424,57.39,1621,3901,41.55,23084,40531,56.95
-2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26
-2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29
-2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62
-2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61
-2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78
-2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95
-2026-10-08T20:28:57Z,ab87997,1809,fix(data): apply StyleMap styles and fix four KML and GeoJSON style bugs,unit,clustering,607,1849,32.83,180,852,21.13,3410,10253,33.26
-2026-10-08T20:28:57Z,ab87997,1809,fix(data): apply StyleMap styles and fix four KML and GeoJSON style bugs,unit,data,1964,2950,66.58,1014,2461,41.2,12838,20923,61.36
-2026-10-08T20:28:57Z,ab87997,1809,fix(data): apply StyleMap styles and fix four KML and GeoJSON style bugs,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62
-2026-10-08T20:28:57Z,ab87997,1809,fix(data): apply StyleMap styles and fix four KML and GeoJSON style bugs,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61
-2026-10-08T20:28:57Z,ab87997,1809,fix(data): apply StyleMap styles and fix four KML and GeoJSON style bugs,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78
-2026-10-08T20:28:57Z,ab87997,1809,fix(data): apply StyleMap styles and fix four KML and GeoJSON style bugs,unit,TOTAL,3973,6436,61.73,1687,3917,43.07,24597,40594,60.59
-2026-10-09T21:49:19Z,466bfa5,,build(deps): bump the github-actions-minor-and-patch group across 1 directory with 2 updates,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26
-2026-10-09T21:49:19Z,466bfa5,,build(deps): bump the github-actions-minor-and-patch group across 1 directory with 2 updates,unit,data,1964,2950,66.58,1014,2461,41.2,12838,20923,61.36
-2026-10-09T21:49:19Z,466bfa5,,build(deps): bump the github-actions-minor-and-patch group across 1 directory with 2 updates,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62
-2026-10-09T21:49:19Z,466bfa5,,build(deps): bump the github-actions-minor-and-patch group across 1 directory with 2 updates,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61
-2026-10-09T21:49:19Z,466bfa5,,build(deps): bump the github-actions-minor-and-patch group across 1 directory with 2 updates,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78
-2026-10-09T21:49:19Z,466bfa5,,build(deps): bump the github-actions-minor-and-patch group across 1 directory with 2 updates,unit,TOTAL,3973,6436,61.73,1688,3917,43.09,24597,40594,60.59