Skip to content

Implements new tile57 API#45

Merged
beetlebugorg merged 36 commits into
mainfrom
feat/composite-bake
Jul 12, 2026
Merged

Implements new tile57 API#45
beetlebugorg merged 36 commits into
mainfrom
feat/composite-bake

Conversation

@beetlebugorg

Copy link
Copy Markdown
Owner

No description provided.

beetlebugorg and others added 30 commits July 8, 2026 12:48
POST /api/debug/partition bakes an ownership-partition PMTiles for every installed
provider (which cell renders which ground per band, no portrayed content) via the new
tile57.BakePartitionDebug binding, and registers each as the "{provider}-partition" tile
set — served at /tiles/{provider}-partition/{z}/{x}/{y}.mvt (layers: "partition" polygons
by cell colour, "labels" points with the owning cell name). GET returns each provider's
readiness + tile URL, so a debug UI can trigger generation and overlay the result.

Reuses encRootDir + setDir + the tileSets registry; the partition math is all in the
engine. Verified live against the NOAA provider (3528 cells, z0-12): bake ~12s, tiles
serve 200 with both layers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Settings → Advanced gains an "Ownership partition (debug)" section:
- "Generate partition overlays" POSTs /api/debug/partition (server bakes one partition
  PMTiles per installed provider), then polls until each is ready.
- Per provider, a "Show/Hide overlay" toggle adds/removes a MapLibre vector source over
  the chart from /tiles/{provider}-partition: a fill layer coloured by the face "color"
  property + a symbol layer labelling each face with its owning "cell". Re-applied on
  style rebuild (mariner change); removed on teardown.

Lets you see the composite ownership quilt directly on the chart to debug which cell
renders which ground per band. JS syntax-checked; server side verified live.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bakeProvider now bakes each cell to its own native-scale PMTiles (coverage embedded)
and streams them through the engine's ownership partition into tiles/chart.pmtiles
(tile57.BakeCell -> ComposeFiles), replacing tile57.BakeBundle's in-bake cross-cell
combiner. Only tiles/chart.pmtiles is needed by the serving path — the MapLibre style
is built dynamically (tile57.Style) and the sprite/glyphs/colortables are global
server assets — so the composite bake skips the bundle's (unused) assets/style/
manifest emission.

Progress is now per-cell (bake i/N) then a compose phase, driven from Go.

Escape hatch: TILE57_LEGACY_BAKE=1 restores the BakeBundle path while the composite
model beds in.

KNOWN GAPS to flush out: per-cell bake is serial (a warmup + worker pool is the next
lever for whole-district speed); the compose phase has no fine-grained progress.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…drop the tree)

composeProvider swallowed per-cell BakeCell errors and returned (0, nil) when every
cell failed, which bakeProvider treats as "no coverage" → os.RemoveAll(outDir). In the
colocated cache+data test layout that deletes the provider's ENC_ROOT source. BakeBundle
errors in this case (no removeAll), so match it: return an error when cells were present
but none baked. Fixes TestImportFetchDownloadOnly on the compose path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…shared helper)

Extract the compose flow (walk cells -> BakeCell each -> ComposeFiles) into
baker.ComposeENCRoot, shared by the server bakeProvider and the CLI runTile57Archive.
Both now default to the ownership-partition composite; TILE57_LEGACY_BAKE=1 restores
the in-bake BakeBundle combiner. The CLI reads the manifest bbox back from the composed
archive (OpenPMTiles.Info). Server composeProvider is now a thin wrapper that maps the
onProgress/onSkip callbacks to the import job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The composite provider bake now reports seconds-remaining (mean per-cell rate over cells baked
so far) on the import job, and the CLI prints it too. The web progress UI ignored the job's
free-text `note`, so the earlier note-only ETA never showed; add a dedicated `eta` field to the
import job and render it in chart-service._formatStatus via a compact fmtEta →
"1,234 / 4,567 charts · ~2m left". Rebuild + hard-refresh to pick up the embedded web asset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The composite bake's 3-5 min partition compose was one opaque step: the UI stuck on "Preparing"
(the client re-derived the verb from phase+unit and pinned bake+cells -> "Preparing") behind a
frozen 100% bar. Now:

- composeProvider feeds tile57's new ComposeFiles progress callback into the job: "Composing
  tiles" with a real bar (engine zoom-weight fraction), a "zoom N of M" detail, and an ETA
  extrapolated from elapsed compose time. The per-cell stage reports "Baking charts".
- The server owns the status wording: statusJSON emits display-ready action + detail + frac
  (bar fill; null = indeterminate) built server-side; the client renders them verbatim instead
  of interpreting phase/unit/note. The dead client PHASE map / fmtBytes / fmtEta and the
  "Preparing" heuristic are removed; the raw fields still ride along for pack/region context.
- The earlier note-only ETA now actually reaches the client (statusJSON had dropped the eta
  field, so the wired-up fmtEta path was dead).

Rebuild with `make TILE57=../tile57 build` + hard-refresh to pick up the embedded web asset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…don't jitter

- The per-cell bake stage now names the chart in flight ("Baking US5MD1MD" instead of a
  generic "Baking charts"): ComposeENCRoot's progress callback passes the cell stem, and
  composeProvider puts it in the action. The CLI prints it too.
- The server reports the ETA as its OWN field, separate from the count `detail`, so the
  client renders each in a fixed, right-aligned tabular slot (the ETA reserves a min-width).
  Glued together they changed width every tick and the whole line shifted around; split, the
  count and ETA stay put. The chart-library batch banner re-joins them for its one-line layout.

Rebuild with `make TILE57=../tile57 build` + hard-refresh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a Composer TileSource backed by tile57's on-demand compositor: per-cell
PMTiles held mmap'd + the ownership partition resident, composing each tile as
the camera asks instead of serving a prebaked archive. It returns decompressed
MLT (the HTTP layer gzips on the wire) with TileType=mlt.

A dev/test hook wires it in: set TILE57_LIVE_COMPOSE to a dir of per-cell
archives (from `tile57 compose --keep-cells`), optionally TILE57_LIVE_PARTITION
to a sidecar (`--save-partition`) and TILE57_LIVE_NAME (default "live"), and the
set is served at /tiles/{name}/{z}/{x}/{y}. No-op when unset.

Verified against NOAA d05 (848 cells): the server opens the compositor at boot
(loads the partition sidecar), and GET /tiles/live/14/4706/6244 returns the
3480-byte MLT byte-matching the batch reference — 0.61 ms/tile end-to-end over
HTTP, with gzip-on-the-wire and 204 for out-of-coverage tiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…as map layers

handlePacks built the provider list from disk packs + ENC_ROOT dirs only, so a
set that lives only in the s.sets registry (the live runtime compositor) never
reached the client and never became a map layer. Include s.sets.names() in the
union. Everything downstream already works for a registry set: the TileJSON
(/tiles/{name}.json) derives bounds/zoom/encoding from src.Meta(), and the engine
style defaults to all registered sets — so the client creates the chart-<name>
source and the style supplies its layers.

Verified: with TILE57_LIVE_COMPOSE set, /api/packs lists "live", and the web map
renders the Chesapeake from on-demand composed tiles (full S-52 symbology).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ict compose)

Importing a provider now builds the live compose structure itself — no CLI, no env
vars. bakeProvider bakes each cell to a KEPT per-provider dir (<setDir>/cells-pm,
incremental: adding a district re-bakes only its new cells), opens a runtime
Composer over them, and saves the ownership-partition sidecar (<setDir>/partition.
tpart) — skipping the whole-district compose pass (tiles compose on demand). The
provider registers + renders exactly like a baked pack. On boot, registerLive
Providers re-opens a Composer for every provider with kept archives (loading the
sidecar → ~25ms), so live layers survive a restart; rebakeMissingProviders skips
providers already served. TILE57_BATCH_COMPOSE=1 keeps the portable chart.pmtiles
path; a tile57-engine-commit stamp invalidates stale per-cell archives on upgrade.

Refactors: registerBakedSet → registerProviderSet(src, packPath) so a live source
(no disk pmtiles) registers with the same tail (cell manifest, aux, meta); baker.
PrepareLive is the input-prep half; tilesource.Composer.SavePartition persists the
sidecar.

Fix: scanPacks now skips the cells-pm dir — its per-cell PMTiles are compositor
INPUTS, not packs, so they no longer each surface as a provider (which made the
client probe /tiles/<cell>.json → 404 spam).

Verified on NOAA d05 (848 cells): boot registers one "noaa" live provider (z0..16),
/api/packs lists it with district d05, and the web map renders the Chesapeake from
on-demand composed tiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use the ownership partition to decide caching, so a live provider never caches a
blank that will fill in. The compositor reports `owned` per tile (a cell's face
covers it → SHOULD render). tileserve then:
  - content, or unowned blank (true empty ocean) → immutable per ?g;
  - owned-but-blank (a cell owns it but produced nothing), or the set is still
    baking (imports.runningFor) → no-cache, so it re-fetches once the cell lands;
  - owned-but-blank AFTER bakes are done → logged as a likely missing/failed cell.

Generation token: a live provider now carries a ?g (live.gen, bumped on each
completed import via bumpLiveGen; packGen falls back to it), so the client
re-fetches with a fresh URL when the cell set changes — invalidating tiles it
cached against a previous, less-complete set. registerLiveProviders only re-serves
a provider whose import completed (live.gen present); a set left partial by an
interrupted bake is finished by rebakeMissingProviders instead of served partial.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n path (host)

Step A of the legacy cleanup — remove every host caller of the batch/bundle
tile-production paths so cell-bake + live-compose is the only route:
 - bakeProvider collapses to prepareLiveProvider → registerProviderSet; drop the
   TILE57_LEGACY_BAKE (BakeBundle) and TILE57_BATCH_COMPOSE (composeProvider) branches.
 - delete composeProvider, registerBakedSet (the chart.pmtiles opener), bakeProgress
   (BakeBundle's per-band bar), and baker.ComposeENCRoot (BakeCell → ComposeFiles batch).
 - CLI `bake` now writes the LIVE STRUCTURE: <out>/tiles/*.pmtiles (per cell, via
   baker.PrepareLive, incremental) + <out>/partition.tpart (NewComposer + SavePartition).
   No merged chart.pmtiles, no per-scheme style/assets/manifest emission — style + assets
   are served at runtime. Deletes runTile57Archive/runTile57Bundle/bakeTile57Bundle +
   writeManifestJSON.

Nothing here calls tile57.BakeBundle/BakePmtiles/Compose/ComposeFiles anymore — the Go
bindings + C ABI for those come out next (Steps B–D). Builds + vets clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Align the runtime compositor's kept per-cell archives with the layout the
`tile57 bake -o <dir>` CLI writes (<dir>/tiles/<STEM>.pmtiles + partition.tpart),
so a CLI-baked structure drops straight into a provider's set dir. liveCellsDir
now returns <setDir>/tiles.

scanPacks recognizes a live provider by its partition.tpart sidecar and skips the
whole tiles/ dir (its per-cell archives are compositor INPUTS, not packs) — else
every cell would surface as a phantom provider. A legacy batch bundle's
tiles/chart.pmtiles (no sidecar) still registers by provider name.

Tests: new TestScanPacksSkipsLiveProvider locks the skip/keep behavior; the stale
TestImportPacks (asserted the removed batch tiles/chart.pmtiles) now asserts the
live structure — per-cell tiles/<STEM>.pmtiles + partition.tpart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ndlers

A live runtime-compositor provider has no disk pack (it lives only in the set
registry), so several handlers that assumed a packPath left it half-supported.
Surface it provider-centrically instead:

- /api/packs: emit coverage bounds for a live provider from the registry when
  enabled, else from its meta sidecar (BBox) — not only for disk packs.
- /api/set/enable: re-register a disabled live provider by re-opening its runtime
  compositor from the kept per-cell archives, not just re-opening a disk pack
  (previously disable->enable dropped a live set until restart).
- enabledPackCells (?active): include live providers' cells from the same
  per-provider cell manifest, so their charts count as "on the map".

TestImportPacks now also asserts the live provider's bounds appear in /api/packs
and that disable->enable round-trips through the registry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_setLabel branded district / river sets (noaa-d5 -> "NOAA · Mid-Atlantic",
ienc-* -> "IENC · …") but a BARE provider set — which the live-composite model
registers, one set per provider ("noaa") — fell through to the raw lowercase
routing key. Map it to the branded display name from the existing _providers()
list ("noaa" -> "NOAA", "ienc" -> "Inland ENC", "user" -> "User Charts"). The
routing key stays lowercase; this is display only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… dir

Make provider discovery fully provider-centric. scanPacks now reads ONLY the flat
<cache>/tiles dir for hand-dropped standalone .pmtiles/.mbtiles archives, instead
of walking the whole cache. Live runtime-compositor providers own
<cache>/<PROVIDER>/tiles/*.pmtiles + partition.tpart and are discovered by
registerLiveProviders (iterating installed providers) — never scavenged here.

This supersedes the earlier partition.tpart-guarded skip with a stronger
invariant: an interrupted import (per-cell archives present, partition not saved
yet) can no longer leak its cells as phantom sets, because provider dirs aren't
scanned at all.

TestScanPacksStandaloneArchivesOnly covers flat discovery + the provider-input
skip, both with and without a saved partition sidecar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the live provider's live.gen MTIME cache-bust token with a CONTENT token:
a sha-of-shas over its per-cell archives. PrepareLive writes an <archive>.sha
sidecar (the archive's content sha) at bake; liveGenToken sorts the per-cell
"stem:sha" lines and hashes them into a positive int63, stored in live.gen and
read by packGen. So a no-op re-bake keeps the token (the client's cached tiles
stay valid), and the token advances exactly when the cell set or a cell's content
changes — the foundation for progressive per-batch re-keying during import.

TestLiveGenTokenContentAddressed covers determinism, content-sensitivity, and
add/remove.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…p live

Register + serve a live provider DURING its import instead of only when the whole
provider finishes: every liveReKeyBatch (48) freshly-baked cells, re-open the
compositor over what's baked so far, register + enable it, and advance the
content token (progressiveReKey, driven from the bake progress callback). The
client re-fetches on each new ?g and the map fills in batch by batch. The
per-re-key partition rebuild is negligible against the per-cell (seconds-each)
bake, and it runs synchronously in the paused bake loop so it never races the
writer.

openLiveComposer now ALWAYS persists the (possibly rebuilt) partition sidecar, so
a re-key over a changed cell set leaves the on-disk sidecar current for a fast
boot instead of stale.

TestImportPacks exercises progressiveReKey over the real baked archives.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…a tile57

Rewire the live-provider bake onto tile57.BakeTree: one call walks the ENC tree
and bakes every cell IN PARALLEL to the mirrored <cellsDir>/<rel>.pmtiles (+ .sha),
the engine writing + freeing each archive (peak memory ~ workers). This restores
parallelism after making the cell the sole parallel unit (the host had gone fully
serial).

- PrepareLive is now a thin BakeTree wrapper (drops the serial per-cell loop, the
  .sha write, and ListCells-based iteration — the library does all of it).
- liveCellArchives walks the mirrored tree (was a flat ReadDir).
- liveBakeWorkers bounds concurrency (min(cpus,8), CHARTPLOTTER_BAKE_WORKERS override).
- Progress flows through BakeTree's (done,total) callback; drop the mid-bake
  progressive re-key (each re-key rebuilt the partition → StalePartition churn) —
  the provider registers once at the end.
- bakeProvider's failed-import cleanup removes only the baked tiles dir, not the
  whole set dir (which holds ENC_ROOT in single-dir mode) — fixes
  TestImportFetchDownloadOnly.
- TestImportPacks asserts the cell's archive in the mirrored tree.

Server package green. A cell shared by two districts now bakes once per district
dir (the ownership partition tie-breaks).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Live

PrepareLive's signature changed (workers + (done,total) progress, returns a
count). The CLI now bakes in parallel and walks <out>/tiles for the mirrored
per-cell archives to build the partition sidecar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sBytes, ComposeSource.Tile

Cell-index and bake-metadata scans call tile57.Cells(path) directly (no chart
handle over raw S-57 anymore); the simulator water mask uses
tile57.FeaturesBytes; the composer serves via ComposeSource.Tile. Submodule at
tile57 599f8ad (bake/render/compose sections, PMTiles-only mmap'd chart handle,
compose borrows charts, tile57_status everywhere).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…MBOL_SCALE icons), sector figures whole across seams, compose output backends, tile57_free(ptr)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bake required for decoration)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rt metadata' phase no longer holds the catalogue in RAM

providerCellData loaded every base cell + update of the whole provider into
memory (~8 GB for the full NOAA catalogue), only for ExtractCellMeta to write
it all back to a temp dir and re-scan it. registerProviderSet now reads the
per-cell metadata straight from the provider's on-disk ENC_ROOT
(baker.ExtractCellMetaDir over tile57.Cells — the engine streams cells and
never holds the set), and the cell manifest is written from the resulting
stems. The zip-upload path keeps its in-memory CellData (district-sized,
already in RAM from the upload).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…acks (kills false IALA boundaries at cell junctions)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… coarse zooms, scamin-restricted; re-bake required)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… build

registerLiveProviders re-registered kept per-cell archives at boot without the
.enginever check (that only ran on the import path), so a binary upgrade kept
serving stale tiles until a manual re-import. Skip stale sets at boot —
rebakeMissingProviders then re-bakes them through prepareLiveProvider, which
drops + re-stamps the dir. Also bumps the tile57 engine to 3c46b23 (sector-
light fixes: sub-band SCAMIN cull, 512-px figure sizing, compose reach ring).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The live compositor generates tiles at SERVE time, so the ?g content token
must address the engine build too: a serve-path engine fix used to leave the
token unchanged (archives byte-identical), and clients kept their cached
broken tiles until a manual hard refresh. liveGenToken now mixes
Server.EngineCommit into the sha-of-shas, and registerLiveProviders
re-persists live.gen at boot so a restart under a new engine mints the new
token without waiting for the next import.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es, submodule to b8e8499

The engine renamed its public surface so every symbol leads with its family
(tile57_chart_* handle methods, bake_charts/enc_charts, Go Cells -> Charts,
BakeCell -> BakeChart). Host call sites follow; host-internal Cells vocabulary
(import packs, set meta, ienc) is untouched. Binary rebuilt against the new
libtile57.a (engineCommit b8e8499, tile57_chart_* symbols verified in the
binary); baker/tilesource/server tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
beetlebugorg and others added 6 commits July 10, 2026 20:34
…s it as an arg

registerLiveProviders and rebakeMissingProviders run inside server.New, but
EngineCommit was assigned by the caller AFTER New returned — so the .enginever
stamp check always ran against "" ("counts as current") and stale kept
archives registered on every boot, silencing the self-heal re-bake. This is
why an engine upgrade never re-baked the live NOAA set: tiles baked pre-
meta-bounds-complement (and interrupted at d1+d5 of five districts) kept
serving. The commit is construction-time data, so it is now a New parameter,
set before boot registration; serve.go passes the ldflags stamp, tests pass "".

Also bumps the engine submodule to aeac905 (tile57_version now reports 0.3.0 —
the capi string had missed the version bump).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… tiles serve throughout

An engine upgrade used to RemoveAll the kept per-cell archives up front and
re-bake in place: an interrupted or failed re-bake left the provider with no
tiles at all (and boot refused to serve the stale survivors, blanking the
map for the whole multi-minute bake). Now stale archives keep serving —
stale tiles beat no tiles — while the full re-bake lands in a staging tree
(<setDir>/tiles.next, stamped with the baking engine up front so a same-build
re-run resumes incrementally and a different build starts over). The swap into
place is the LAST step, after the whole bake succeeded; a staged bake that
produces nothing is discarded. registerProviderSet's re-register closes the
old composer, so the dropped archives' mmaps release on swap. Boot recovers
the one crash window (old tree dropped, staged tree not yet renamed) by moving
the staged tree into place and routing the set through the self-heal bake.

invalidateLiveOnEngineChange is gone; rebakeMissingProviders now also queues
sets that are serving from another engine build's archives. Covered by
TestEngineRebakeStagesAndSwapsLast (failure path keeps the served tree
untouched; success path swaps, restamps, and leaves no staging behind).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ule)

Four files weren't gofmt-clean (Go 1.26 toolchain gofmt). Also scope `make fmt`
/`fmt-check` to `git ls-files '*.go'` instead of `.` — the latter walked into the
./tile57 engine submodule and failed the gate on the engine's own Go bindings,
which chartplotter-go doesn't own. git ls-files lists only this repo's tracked
files, so the submodule is naturally excluded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
govulncheck flagged GO-2026-5856 (Encrypted Client Hello privacy leak in
crypto/tls), reached from serveOSM's HTTP client. Fixed in the standard library
as of go1.26.5. Bump the toolchain directive; govulncheck is clean afterward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…GO-2026-5856)

go.mod already pins `toolchain go1.26.5`, but CI ignored it: setup-go
`go-version: '1.26'` installed the cached 1.26.4 and GOTOOLCHAIN=local
blocks the auto-upgrade to the toolchain directive, so govulncheck ran
under 1.26.4 and flagged GO-2026-5856. Pin the exact patch (1.26.5) in
every workflow; release.yml especially, so shipped binaries link the
patched crypto/tls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d73c)

The old pin (aeac905c8) predates the cross-platform filemap.zig refactor,
so a non-floating `submodule update --init` checkout still hit the
`std.posix.mmap` void-flag break on the windows cross-compile. Bump the
last-known-good pin to 4b6d73c (feat/per-cell-composite tip, now merged to
tile57 main), which builds clean for x86_64-windows-gnu.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@beetlebugorg beetlebugorg merged commit 2e0d4d3 into main Jul 12, 2026
3 checks passed
@beetlebugorg beetlebugorg deleted the feat/composite-bake branch July 12, 2026 00:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant