Skip to content

moczarr.open

open_hive: a morton-hive store as one lazy xarray Dataset.

The §5 reader flow, arithmetic-first (D10): manifest GET → root coverage MOC ∩ AOI → hive paths by string arithmetic → stamped-leaf opens → concat along the cell dimension. The discovery walk runs only when the root MOC is absent/unusable (D9: caches degrade to the walk, never to wrong answers). Debris (unstamped leaves) is skipped silently — absence of a stamp IS the answer (D4).

AOI semantics: aoi is a morton cover — packed uint64 words, decimal strings, or an object exposing __morton_moc__() (mortie's Moc), mixed orders allowed. Shards are rejected arithmetically (root MOC ∩ AOI), then per leaf by the tier-0 box off the stamp already fetched, and finally rows are subset exactly on the morton coordinate (tier 2 — the coordinate is the truth; the MOC tiers are only indexes).

An AOI (or window) that intersects no coverage is a data answer, not an error: the result is a schema-correct EMPTY dataset plus a UserWarning (issue #4). Only a store with no stamped coverage anywhere — no schema source at all — raises, as :class:moczarr.exceptions.NoCoverageError.

candidate_leaves(store_root, manifest=None, aoi=None, window=None, *, when=None, store=None, concurrency=None, **store_kwargs)

Store-relative leaf paths to try, ascending in packed-word order.

The store's leaf-discovery seam, shared by :func:open_hive and :func:moczarr.iter_occupancy_and so the two agree on what a store contains by construction rather than by duplicated arithmetic. Public (issue #39) for readers that need the leaf roster without opening anything — the paths are what moczarr.store.read_commits and moczarr.store.read_leaf_metas take. :func:candidate_shards is the same seam returning shard IDS — what :func:moczarr.open_leaf, and so the ragged/HHDC readers it hands a store to, take — through one shared implementation (issue #49), so the two views can never disagree; a caller wanting ids asks it rather than re-parsing these paths, whose grammar (the .zarr suffix, the path_grouping node depth, the windowed {id}_{window} dialect) is the store's own.

manifest is optional (issue #49): when omitted, this call fetches it — ONE extra metadata GET, the same one-GET-if-absent posture :func:moczarr.open_leaf takes for manifest=None — and raises ValueError when the root has none (a multi-product root gets the pointed §6.5 error naming its products). Pass it explicitly to skip the GET — the right call in a loop over many AOIs against one store. The GET is the whole extra cost: a call resolves ONE object store up front and threads it through every read it makes, so the transport count is one per call whichever route runs and whether or not the manifest was passed. **store_kwargs pass through to :func:moczarr.store.open_object_store (anonymous=True, region=..., explicit keys, ...), the passthrough the sibling functions (:func:moczarr.store.load_root_coverage, :func:moczarr.open_leaf) already have; store= stays the way to share ONE handle across many calls and wins when both are given, per moczarr.store._resolve_store.

Contract, in three parts:

  • Discovery. The root coverage.moc names the committed shards, and leaf paths come from it by string arithmetic (D10) — no LIST. The discovery walk (moczarr.store.walk_leaves) runs only when that envelope is absent or unusable, and is SEMANTICALLY EQUIVALENT: it is a fallback, never a wrong answer, so a caller never keys on which route ran (D9 — caches degrade to the walk, never to wrong answers). Both return CANDIDATES in ascending packed-word order, and the two candidate sets differ only where a stamp settles it anyway (D4: absence of a stamp IS the answer): the walk additionally names debris (unstamped leaves the root MOC does not list), and the arithmetic path additionally names MOC-listed shards whose object is gone. After the caller's commit-stamp GET — which every consumer here does — the two agree leaf-for-leaf with when=None; under when= the walk returns a SUPERSET, because the temporal map it would prune against rides the same absent carrier (see the when bullet — degradation in the safe direction, D9, and no commit stamp settles it: a stamp says the leaf exists, not when its rows are). Objects that reuse the window-naming dialect at ancestor nodes (overview zarrs, spec §4.2) are not leaves and neither route returns them, and neither names an order-29 POINT stem (§2/§6.6: points never live in hive paths). One further asymmetry: the walk can name a single shard TWICE, because a *.zarr child is a leaf at whatever node holds it and its node depth is not checked against the shard's order — so a store holding one shard's leaf at two nodes walks as two leaves, which is two distinguishable paths here and two EQUAL ids from :func:candidate_shards. The arithmetic route names each word once and never does; the two entries' relative order is the walk's own (the sort key is the word alone).
  • Window selection. window picks the time window of a morton-hive/2 store, validated through the one convention.validate_window seam (which also refuses the reserved all-time token). A windowed store with window=None cannot take the arithmetic path — the walk enumerates what exists and the ValueError lists the labels present. window on an unwindowed store is a ValueError.
  • aoi restriction (SHARD cover only). A morton cover — packed words, decimal strings, or an object exposing __morton_moc__() (mortie's Moc), mixed orders allowed — restricts the returned leaves to those whose subtree meets it. It cuts SHARDS and nothing finer: a returned leaf is kept whole, and the cells inside it are NOT clipped to the cover, so any cell-level use of this roster is a superset with respect to the AOI (false positives possible, false negatives impossible — zagg's AOI-overhang convention). The exact cell-level cut is :func:moczarr.coverage.aoi_mask, which :func:open_hive applies to the morton coordinate itself (tier 2 — the coordinate is the truth; the MOC tiers are only indexes).
  • when restriction (temporal, spec §10; issue #45). A temporal query — (start, end) as ISO strings / datetime64 / internal-ns ints (a CLOSED real interval — unlike every other window in this docstring, end itself is included), raw uint64 toc words, or an object exposing __toc_words__() (mortie's Toc) — prunes candidates against the root envelope's tier-1 per-shard toc word map, before anything is opened. The §10 asymmetry is the contract: only a shard the map LISTS whose word fails mortie.toc_overlaps for the window is dropped; a shard ABSENT from the map is always KEPT (unlisted is unknown, never empty — the section is a regenerable accelerator, not truth). A store publishing no temporal section prunes nothing, silently (§10's absence rule), and when=None is byte-identical to today. Pruning rides the root envelope's tier-1 map ALONE, so the discovery walk — which runs exactly when that envelope is absent or unusable — has nothing to prune against and returns the unpruned candidate set, a conservative superset (D9: degrade, never wrong). Like aoi, the cut is conservative and SHARD-level: kept leaves may hold rows outside the window. The over-report near window edges runs to a few seconds — up to TWO encoding quanta, one from round-tripping the query window itself through span2toc/toc2time and one from the shard's own outward-rounded word under mortie.toc_overlaps — and every widening is outward, so it never under-reports.
Source code in src/moczarr/open.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def candidate_leaves(
    store_root: str,
    manifest: dict | None = None,
    aoi=None,
    window: str | None = None,
    *,
    when=None,
    store: Any = None,
    concurrency: int | None = None,
    **store_kwargs: Any,
) -> list[str]:
    """Store-relative leaf paths to try, ascending in packed-word order.

    The store's leaf-discovery seam, shared by :func:`open_hive` and
    :func:`moczarr.iter_occupancy_and` so the two agree on what a store
    contains by construction rather than by duplicated arithmetic. Public
    (issue #39) for readers that need the leaf roster without opening
    anything — the paths are what ``moczarr.store.read_commits`` and
    ``moczarr.store.read_leaf_metas`` take. :func:`candidate_shards` is the
    same seam returning shard IDS — what :func:`moczarr.open_leaf`, and so
    the ragged/HHDC readers it hands a store to, take — through one shared
    implementation (issue #49), so the two views can never disagree; a
    caller wanting ids asks it rather than re-parsing these paths, whose
    grammar (the ``.zarr`` suffix, the ``path_grouping`` node depth, the
    windowed ``{id}_{window}`` dialect) is the store's own.

    ``manifest`` is optional (issue #49): when omitted, this call fetches it
    — ONE extra metadata GET, the same one-GET-if-absent posture
    :func:`moczarr.open_leaf` takes for ``manifest=None`` — and raises
    ``ValueError`` when the root has none (a multi-product root gets the
    pointed §6.5 error naming its products). Pass it explicitly to skip the
    GET — the right call in a loop over many AOIs against one store. The
    GET is the whole extra cost: a call resolves ONE object store up front
    and threads it through every read it makes, so the transport count is
    one per call whichever route runs and whether or not the manifest was
    passed. ``**store_kwargs`` pass through to
    :func:`moczarr.store.open_object_store` (``anonymous=True``,
    ``region=...``, explicit keys, ...), the passthrough the sibling
    functions (:func:`moczarr.store.load_root_coverage`,
    :func:`moczarr.open_leaf`) already have; ``store=`` stays the way to
    share ONE handle across many calls and wins when both are given, per
    ``moczarr.store._resolve_store``.

    Contract, in three parts:

    - **Discovery.** The root ``coverage.moc`` names the committed shards,
      and leaf paths come from it by string arithmetic (D10) — no LIST. The
      discovery walk (``moczarr.store.walk_leaves``) runs only when that
      envelope is absent or unusable, and is SEMANTICALLY EQUIVALENT: it is
      a fallback, never a wrong answer, so a caller never keys on which
      route ran (D9 — caches degrade to the walk, never to wrong answers).
      Both return CANDIDATES in ascending packed-word order, and the two
      candidate sets differ only where a stamp settles it anyway (D4:
      absence of a stamp IS the answer): the walk additionally names debris
      (unstamped leaves the root MOC does not list), and the arithmetic
      path additionally names MOC-listed shards whose object is gone. After
      the caller's commit-stamp GET — which every consumer here does — the
      two agree leaf-for-leaf with ``when=None``; under ``when=`` the walk
      returns a SUPERSET, because the temporal map it would prune against
      rides the same absent carrier (see the ``when`` bullet — degradation
      in the safe direction, D9, and no commit stamp settles it: a stamp
      says the leaf exists, not when its rows are). Objects that reuse the
      window-naming dialect
      at ancestor nodes (overview zarrs, spec §4.2) are not leaves and
      neither route returns them, and neither names an order-29 POINT stem
      (§2/§6.6: points never live in hive paths). One further asymmetry:
      the walk can name a single shard TWICE, because a ``*.zarr`` child is
      a leaf at whatever node holds it and its node depth is not checked
      against the shard's order — so a store holding one shard's leaf at
      two nodes walks as two leaves, which is two distinguishable paths
      here and two EQUAL ids from :func:`candidate_shards`. The arithmetic
      route names each word once and never does; the two entries' relative
      order is the walk's own (the sort key is the word alone).
    - **Window selection.** ``window`` picks the time window of a
      ``morton-hive/2`` store, validated through the one
      ``convention.validate_window`` seam (which also refuses the reserved
      all-time token). A windowed store with ``window=None`` cannot take
      the arithmetic path — the walk enumerates what exists and the
      ``ValueError`` lists the labels present. ``window`` on an unwindowed
      store is a ``ValueError``.
    - **``aoi`` restriction (SHARD cover only).** A morton cover — packed
      words, decimal strings, or an object exposing ``__morton_moc__()``
      (mortie's ``Moc``), mixed orders allowed — restricts the
      returned leaves to those whose subtree meets it. It cuts SHARDS and
      nothing finer: a returned leaf is kept whole, and the cells inside it
      are NOT clipped to the cover, so any cell-level use of this roster is
      a superset with respect to the AOI (false positives possible, false
      negatives impossible — zagg's AOI-overhang convention). The exact
      cell-level cut is :func:`moczarr.coverage.aoi_mask`, which
      :func:`open_hive` applies to the ``morton`` coordinate itself (tier 2
      — the coordinate is the truth; the MOC tiers are only indexes).
    - **``when`` restriction (temporal, spec §10; issue #45).** A temporal
      query — ``(start, end)`` as ISO strings / ``datetime64`` / internal-ns
      ints (a CLOSED real interval — unlike every other window in this
      docstring, ``end`` itself is included), raw ``uint64`` toc words, or
      an object exposing ``__toc_words__()`` (mortie's ``Toc``) — prunes
      candidates against the
      root envelope's tier-1 per-shard toc word map, before anything is
      opened. The §10 asymmetry is the contract: only a shard the map LISTS
      whose word fails ``mortie.toc_overlaps`` for the window is dropped; a
      shard ABSENT from the map is always KEPT (unlisted is *unknown*, never
      *empty* — the section is a regenerable accelerator, not truth). A
      store publishing no ``temporal`` section prunes nothing, silently
      (§10's absence rule), and ``when=None`` is byte-identical to today.
      Pruning rides the root envelope's tier-1 map ALONE, so the discovery
      walk — which runs exactly when that envelope is absent or unusable —
      has nothing to prune against and returns the unpruned candidate set,
      a conservative superset (D9: degrade, never wrong).
      Like ``aoi``, the cut is conservative and SHARD-level: kept leaves may
      hold rows outside the window. The over-report near window edges runs
      to a few seconds — up to TWO encoding quanta, one from round-tripping
      the query window itself through ``span2toc``/``toc2time`` and one from
      the shard's own outward-rounded word under ``mortie.toc_overlaps`` —
      and every widening is outward, so it never under-reports.
    """
    return [
        rel
        for _, rel in _candidate_pairs(
            store_root,
            manifest,
            aoi,
            window,
            when=when,
            store=store,
            concurrency=concurrency,
            **store_kwargs,
        )
    ]

candidate_shards(store_root, manifest=None, aoi=None, window=None, *, when=None, store=None, concurrency=None, **store_kwargs)

Candidate shard IDS (morton decimal strings), ascending in packed-word order.

:func:candidate_leaves returning what :func:moczarr.open_leaf takes (issue #49): the same discovery seam through the same one implementation — discovery routes, window selection, the shard-level aoi and when restrictions, and every posture in that docstring hold here verbatim; only what is handed back differs. A separate name rather than a flag because the return TYPE changes (mortie's Moc.to_order precedent, espg/mortie#197): ids like "11213", not store-relative paths like "1/1/2/1/3/11213.zarr".

The id is the path's stem the library knew before it built the path, so select-then-open needs no string surgery and no knowledge of the path grammar (.zarr suffix, path_grouping depth, the windowed {id}_{window} dialect). With the manifest fetched when omitted and **store_kwargs passing through (issue #49; the posture and the one-extra-GET cost are on :func:candidate_leaves's docstring), the whole selection is one call — and the manifest it fetched is worth reading once for the loop, since open_leaf(manifest=...) skips its own manifest GET (each open still GETs its leaf's root stamp — the versioned-leaf pointer, zagg spec §1.5 — unless stamp= is threaded from one batched :func:moczarr.store.read_commits over the paired :func:candidate_leaves)::

shards = candidate_shards(root, aoi=q, anonymous=True)
manifest = read_manifest(root, anonymous=True)  # once, not per leaf
for shard in shards:
    leaf = open_leaf(root, shard, manifest=manifest, anonymous=True)

On a windowed store the id is the BARE shard — the window label is not part of it; pass the same window= to :func:moczarr.open_leaf. Positionally identical lists: candidate_shards(...)[i] names the leaf candidate_leaves(...)[i] locates, for equal arguments against an unchanged store. The shared implementation makes ONE discovery's two views agree; it does not join two calls. Each call is its own discovery — two root-MOC GETs, or two full walks — so against a store being written concurrently the two lists can differ in length, and index i then names different things. The ruled idiom (issue #49 review; the internal pair stays private until a consumer shows real friction): make both calls share their inputs — fetch the manifest ONCE (or thread one store=) and pass it to both. Sharing buys exactly two things — the duplicate manifest GET is skipped, and ONE path_grouping is pinned across the pair so both render the same path grammar. It snapshots nothing: each call still re-GETs the root envelope (or re-walks), so the "unchanged store" caveat above is untouched by the idiom. A pair that must be tear-proof against a concurrent writer takes ONE view and derives the other through the convention seam — library parsers, not caller-side string surgery — instead of issuing both calls. Path to id: :func:moczarr.convention.split_leaf_name on the path's BASENAME (rel.rsplit("/", 1)[-1], .zarr suffix included — it is the suffixed name the parser requires, not the bare id), returning (id, window_label). Id to path: :func:moczarr.convention.leaf_path threaded with the same two arguments the discovery used, leaf_path(shard, window=window, path_grouping=manifest_path_grouping(manifest)) — a bare leaf_path(shard) silently drops the window label and assumes path_grouping=1, and stops naming the same objects.

Source code in src/moczarr/open.py
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def candidate_shards(
    store_root: str,
    manifest: dict | None = None,
    aoi=None,
    window: str | None = None,
    *,
    when=None,
    store: Any = None,
    concurrency: int | None = None,
    **store_kwargs: Any,
) -> list[str]:
    """Candidate shard IDS (morton decimal strings), ascending in packed-word order.

    :func:`candidate_leaves` returning what :func:`moczarr.open_leaf` takes
    (issue #49): the same discovery seam through the same one implementation
    — discovery routes, ``window`` selection, the shard-level ``aoi`` and
    ``when`` restrictions, and every posture in that docstring hold here
    verbatim; only what is handed back differs. A separate name rather than
    a flag because the return TYPE changes (mortie's ``Moc.to_order``
    precedent, espg/mortie#197): ids like ``"11213"``, not store-relative
    paths like ``"1/1/2/1/3/11213.zarr"``.

    The id is the path's stem the library knew before it built the path, so
    select-then-open needs no string surgery and no knowledge of the path
    grammar (``.zarr`` suffix, ``path_grouping`` depth, the windowed
    ``{id}_{window}`` dialect). With the manifest fetched when omitted and
    ``**store_kwargs`` passing through (issue #49; the posture and the
    one-extra-GET cost are on :func:`candidate_leaves`'s docstring), the
    whole selection is one call — and the manifest it fetched is worth
    reading once for the loop, since ``open_leaf(manifest=...)`` skips its
    own manifest GET (each open still GETs its leaf's root stamp — the
    versioned-leaf pointer, zagg spec §1.5 — unless ``stamp=`` is threaded
    from one batched :func:`moczarr.store.read_commits` over the paired
    :func:`candidate_leaves`)::

        shards = candidate_shards(root, aoi=q, anonymous=True)
        manifest = read_manifest(root, anonymous=True)  # once, not per leaf
        for shard in shards:
            leaf = open_leaf(root, shard, manifest=manifest, anonymous=True)

    On a windowed store the id is the BARE shard — the window label is not
    part of it; pass the same ``window=`` to :func:`moczarr.open_leaf`.
    Positionally identical lists: ``candidate_shards(...)[i]`` names the
    leaf ``candidate_leaves(...)[i]`` locates, for equal arguments against
    an unchanged store. The shared implementation makes ONE discovery's two
    views agree; it does not join two calls. Each call is its own discovery
    — two root-MOC GETs, or two full walks — so against a store being
    written concurrently the two lists can differ in length, and index
    ``i`` then names different things. The ruled idiom (issue #49 review;
    the internal pair stays private until a consumer shows real friction):
    make both calls share their inputs — fetch the manifest ONCE (or thread
    one ``store=``) and pass it to both. Sharing buys exactly two things —
    the duplicate manifest GET is skipped, and ONE ``path_grouping`` is
    pinned across the pair so both render the same path grammar. It
    snapshots nothing: each call still re-GETs the root envelope (or
    re-walks), so the "unchanged store" caveat above is untouched by the
    idiom. A pair that must be tear-proof against a concurrent writer takes
    ONE view and derives the other through the convention seam — library
    parsers, not caller-side string surgery — instead of issuing both
    calls. Path to id: :func:`moczarr.convention.split_leaf_name` on the
    path's BASENAME (``rel.rsplit("/", 1)[-1]``, ``.zarr`` suffix included
    — it is the suffixed name the parser requires, not the bare id),
    returning ``(id, window_label)``. Id to path:
    :func:`moczarr.convention.leaf_path` threaded with the same two
    arguments the discovery used, ``leaf_path(shard, window=window,
    path_grouping=manifest_path_grouping(manifest))`` — a bare
    ``leaf_path(shard)`` silently drops the window label and assumes
    ``path_grouping=1``, and stops naming the same objects.
    """
    pairs = _candidate_pairs(
        store_root,
        manifest,
        aoi,
        window,
        when=when,
        store=store,
        concurrency=concurrency,
        **store_kwargs,
    )
    if not pairs:
        return []
    # The batch render, not ``convention.morton_decimal`` in a loop: that
    # seam renders ONE word by contract (its docstring's own rule, the twin
    # of ``morton_word``'s) and mortie vectorizes this — measured 33x over
    # 3,000 words, and an o9 AOI runs to a few thousand shards. Equivalent
    # because candidates never hold a POINT word (``_shard_leaf_name`` skips
    # them, ``leaf_path`` refuses them), which is the one kind whose render
    # needs the moczarr-side §2 ``p`` marker ``decimal_repr`` does not add.
    from mortie import MortonIndexArray

    words = np.asarray([word for word, _ in pairs], dtype=np.uint64)
    return list(MortonIndexArray.from_words(words).decimal_repr())

open_hive(store_root, *, product=None, aoi=None, window=None, anonymous=False, fabricate_cell_ids='auto', decode=False, index_kind='moc', concurrency=32, xr_kwargs=None, store=None, manifest=None, _objects_out=None, **store_kwargs)

Open a morton-hive store as one xarray Dataset.

Parameters:

Name Type Description Default
store_root str

Store root (local directory or s3://bucket/prefix) — a bare single-product store, or a multi-product store root when product is given.

required
product str

Named product of a multi-product store root (D19, spec §6.5): sugar for opening the {store_root}/{product} subtree, which is a complete morton-hive store in its own right. Omitted on a multi-product root, the error lists the product names that exist (see :func:moczarr.products.list_products). Single-product stores are unaffected — the bare form is the valid degenerate case.

None
aoi array - like

Morton cover of the area of interest — packed uint64 words, decimal strings, or an object exposing __morton_moc__() (mortie's Moc), mixed orders allowed. Shards and rows outside the cover are excluded (rows exactly, via the morton coordinate).

None
window str

Window label for a time-windowed (morton-hive/2) store. Omitted on such a store, the error lists the labels that exist.

None
anonymous bool

Unsigned S3 requests (public buckets).

False
fabricate_cell_ids ('auto', True, False)

NESTED cell_ids posture (englacial/zagg#262: "NESTED is fabricated, never stored"). "auto" (default): a stored cell_ids coordinate is kept untouched; when absent (morton-only store) an exact NESTED view is fabricated from the morton coordinate via :func:moczarr.fabricate.fabricate_cell_ids. True always fabricates (replacing any stored array — exact, so a no-op on dual-written stores); False never fabricates. Fabrication runs once post-concat on the final morton coordinate — equivalent to per-leaf (the same words, and mort2healpix is elementwise) but a single vectorized call. The fabricated cell_ids is a Python-side convenience view: the dataset-level attrs["dggs"] block is left untouched, so on a morton-only store it still advertises the morton scheme while the added coordinate is NESTED. Re-serializing such a result is not internally consistent; the authoritative morton-only dggs discriminator is owned by the zagg#262 convention work.

"auto"
decode bool

Assign the xdggs MortonIndex to the morton coordinate before returning (moczarr.dggs.decode), enabling the ds.dggs accessor. Requires the moczarr[xdggs] extra; the default leaves the result xdggs-free.

False
index_kind ('moc', 'pandas')

Index posture for the morton coordinate (the xdggs vocabulary). "moc" (default) is the lazy path: the on-disk morton/cell_ids arrays are never read — the row domain comes from the same coverage arithmetic that selected the leaves (shard subtrees ∩ AOI), held as a :class:moczarr.moc_index.MortonMocIndex whose coordinate is fabricated on demand. The index attaches regardless of decode (it is core, xarray-only); decode=True additionally wraps it for the ds.dggs accessor. Requiring decode here would chain the core lazy index to the xdggs extra, against the ratified placement. "pandas" materializes instead: the stored coordinate is read and, with decode=True, indexed through a PandasIndex — use it when a workflow needs what the interval index cannot represent (notably xr.concat of overlapping or out-of-order domains; the disjoint, ascending batch-sweep concat works on the moc default).

"moc"
concurrency int or None

Maximum in-flight metadata requests (the candidate leaves' stamp GETs, and the discovery walk's per-level LISTs), default 32 — the zarr-python knob vocabulary. None or 1 runs the serial path (debugging). Leaf DATA opens stay serial either way (issue #5, measured in the phase-3 bench; revisit with the lazy-index work).

32
xr_kwargs dict

Extra keyword arguments for each leaf's xarray.open_zarr (e.g. chunks={} for dask-backed laziness).

None
store obstore store

An already-constructed object store rooted at the subtree actually opened, shared instead of building one from store_root + store_kwargs (issue #5 — the same spelling and posture as :func:moczarr.pyramid.open_overview_order and :func:moczarr.level.open_column_order, so a caller threading ONE handle across a product's levels gets shared reads on EVERY level, the native one included). With product=, the handle must be rooted at {store_root}/{product} — the subtree this open re-roots on — since every key is resolved against it.

None
manifest dict

An already-read morton_hive.json (of that same subtree), re-validated through :func:moczarr.convention.parse_manifest rather than re-fetched. What is passed is what the open uses, so a caller's manifest governs the cell/shard orders, the window dialect, and attrs["morton_hive"].

None
**store_kwargs Any

Extra keyword arguments for the object store (region=... etc.).

{}

Returns:

Type Description
Dataset

Leaves concatenated along the cell dimension in ascending packed morton order, with morton/cell_ids as coordinates and the manifest summary under attrs["morton_hive"].

An aoi (or window) that intersects no coverage returns a schema-correct EMPTY dataset — every data variable and coordinate present with its stored name/dtype/attrs (schema read from one covered leaf's metadata) and zero rows along the cell dimension — and emits a UserWarning naming the store (issue #4). The empty result composes with decode, index_kind="moc" (an empty interval domain), and fabricate_cell_ids. xr.concat of the empty result with a non-empty one preserves dtypes on either index_kind — both sides carry every variable, so xarray fills nothing and no int→float NaN promotion occurs; on the moc default the empty side contributes no intervals, so the concat returns the non-empty domain unchanged (issue #4's empty-composes-through-concat contract, pinned in tests/test_open.py).

Raises ValueError when the root is not a hive store (no manifest), and :class:moczarr.NoCoverageError — a ValueError subclass — when the store has no stamped coverage anywhere: with zero committed leaves there is no schema source at all, whatever the query.

Source code in src/moczarr/open.py
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
def open_hive(
    store_root: str,
    *,
    product: str | None = None,
    aoi=None,
    window: str | None = None,
    anonymous: bool = False,
    fabricate_cell_ids: bool | str = "auto",
    decode: bool = False,
    index_kind: str = "moc",
    concurrency: int | None = 32,
    xr_kwargs: dict[str, Any] | None = None,
    store: Any = None,
    manifest: dict | None = None,
    _objects_out: list | None = None,
    **store_kwargs: Any,
):
    """Open a morton-hive store as one xarray Dataset.

    Parameters
    ----------
    store_root : str
        Store root (local directory or ``s3://bucket/prefix``) — a bare
        single-product store, or a multi-product store root when ``product``
        is given.
    product : str, optional
        Named product of a multi-product store root (D19, spec §6.5): sugar
        for opening the ``{store_root}/{product}`` subtree, which is a
        complete morton-hive store in its own right. Omitted on a
        multi-product root, the error lists the product names that exist
        (see :func:`moczarr.products.list_products`). Single-product stores
        are unaffected — the bare form is the valid degenerate case.
    aoi : array-like, optional
        Morton cover of the area of interest — packed ``uint64`` words,
        decimal strings, or an object exposing ``__morton_moc__()``
        (mortie's ``Moc``), mixed orders allowed. Shards and rows outside
        the cover are excluded (rows exactly, via the ``morton`` coordinate).
    window : str, optional
        Window label for a time-windowed (``morton-hive/2``) store. Omitted
        on such a store, the error lists the labels that exist.
    anonymous : bool, optional
        Unsigned S3 requests (public buckets).
    fabricate_cell_ids : {"auto", True, False}, optional
        NESTED ``cell_ids`` posture (englacial/zagg#262: "NESTED is
        fabricated, never stored"). ``"auto"`` (default): a stored
        ``cell_ids`` coordinate is kept untouched; when absent (morton-only
        store) an exact NESTED view is fabricated from the ``morton``
        coordinate via :func:`moczarr.fabricate.fabricate_cell_ids`.
        ``True`` always fabricates (replacing any stored array — exact, so
        a no-op on dual-written stores); ``False`` never fabricates.
        Fabrication runs once post-concat on the final ``morton``
        coordinate — equivalent to per-leaf (the same words, and
        ``mort2healpix`` is elementwise) but a single vectorized call.
        The fabricated ``cell_ids`` is a Python-side convenience view: the
        dataset-level ``attrs["dggs"]`` block is left untouched, so on a
        morton-only store it still advertises the morton scheme while the
        added coordinate is NESTED. Re-serializing such a result is not
        internally consistent; the authoritative morton-only ``dggs``
        discriminator is owned by the zagg#262 convention work.
    decode : bool, optional
        Assign the xdggs ``MortonIndex`` to the ``morton`` coordinate before
        returning (``moczarr.dggs.decode``), enabling the ``ds.dggs``
        accessor. Requires the ``moczarr[xdggs]`` extra; the default leaves
        the result xdggs-free.
    index_kind : {"moc", "pandas"}, optional
        Index posture for the ``morton`` coordinate (the xdggs vocabulary).
        ``"moc"`` (default) is the lazy path: the on-disk
        ``morton``/``cell_ids`` arrays are never read — the row domain comes
        from the same coverage arithmetic that selected the leaves (shard
        subtrees ∩ AOI), held as a
        :class:`moczarr.moc_index.MortonMocIndex` whose coordinate is
        fabricated on demand. The index attaches regardless of ``decode``
        (it is core, xarray-only); ``decode=True`` additionally wraps it for
        the ``ds.dggs`` accessor. Requiring ``decode`` here would chain the
        core lazy index to the xdggs extra, against the ratified placement.
        ``"pandas"`` materializes instead: the stored coordinate is read
        and, with ``decode=True``, indexed through a ``PandasIndex`` — use
        it when a workflow needs what the interval index cannot represent
        (notably ``xr.concat`` of overlapping or out-of-order domains; the
        disjoint, ascending batch-sweep concat works on the moc default).
    concurrency : int or None, optional
        Maximum in-flight metadata requests (the candidate leaves' stamp
        GETs, and the discovery walk's per-level LISTs), default 32 — the
        zarr-python knob vocabulary. ``None`` or ``1`` runs the serial path
        (debugging). Leaf DATA opens stay serial either way (issue #5,
        measured in the phase-3 bench; revisit with the lazy-index work).
    xr_kwargs : dict, optional
        Extra keyword arguments for each leaf's ``xarray.open_zarr`` (e.g.
        ``chunks={}`` for dask-backed laziness).
    store : obstore store, optional
        An already-constructed object store rooted at the subtree actually
        opened, shared instead of building one from ``store_root`` +
        ``store_kwargs`` (issue #5 — the same spelling and posture as
        :func:`moczarr.pyramid.open_overview_order` and
        :func:`moczarr.level.open_column_order`, so a caller threading ONE
        handle across a product's levels gets shared reads on EVERY level,
        the native one included). With ``product=``, the handle must be
        rooted at ``{store_root}/{product}`` — the subtree this open
        re-roots on — since every key is resolved against it.
    manifest : dict, optional
        An already-read ``morton_hive.json`` (of that same subtree),
        re-validated through :func:`moczarr.convention.parse_manifest`
        rather than re-fetched. What is passed is what the open uses, so a
        caller's manifest governs the cell/shard orders, the window dialect,
        and ``attrs["morton_hive"]``.
    **store_kwargs
        Extra keyword arguments for the object store (``region=...`` etc.).

    Returns
    -------
    xarray.Dataset
        Leaves concatenated along the cell dimension in ascending packed
        morton order, with ``morton``/``cell_ids`` as coordinates and the
        manifest summary under ``attrs["morton_hive"]``.

        An ``aoi`` (or ``window``) that intersects no coverage returns a
        schema-correct EMPTY dataset — every data variable and coordinate
        present with its stored name/dtype/attrs (schema read from one
        covered leaf's metadata) and zero rows along the cell dimension —
        and emits a ``UserWarning`` naming the store (issue #4). The empty
        result composes with ``decode``, ``index_kind="moc"`` (an empty
        interval domain), and ``fabricate_cell_ids``. ``xr.concat`` of the
        empty result with a non-empty one preserves dtypes on either
        ``index_kind`` — both sides carry every variable, so xarray fills
        nothing and no int→float NaN promotion occurs; on the moc default
        the empty side contributes no intervals, so the concat returns the
        non-empty domain unchanged (issue #4's empty-composes-through-concat
        contract, pinned in ``tests/test_open.py``).

        Raises ``ValueError`` when the root is not a hive store (no
        manifest), and :class:`moczarr.NoCoverageError` — a ``ValueError``
        subclass — when the store has no stamped coverage anywhere: with
        zero committed leaves there is no schema source at all, whatever
        the query.
    """
    import xarray as xr
    from zarr.storage import ObjectStore

    if fabricate_cell_ids not in ("auto", True, False):
        raise ValueError(
            f"fabricate_cell_ids={fabricate_cell_ids!r}: expected 'auto', True, or False"
        )
    if fabricate_cell_ids != "auto":
        fabricate_cell_ids = bool(fabricate_cell_ids)
    if index_kind not in ("pandas", "moc"):
        raise ValueError(f"index_kind={index_kind!r}: expected 'pandas' or 'moc'")
    if anonymous:
        store_kwargs.setdefault("anonymous", True)
    if product is not None:
        # D19 sugar: a product subtree IS a complete morton-hive store, so
        # the whole open simply re-roots on it (equivalent to opening the
        # subtree path directly, pinned by a test).
        from moczarr.products import validate_product_name

        validate_product_name(product)
        store_root = f"{store_root.rstrip('/')}/{product}"
    # ONE store construction pair for the whole open (issue #5): the obstore
    # handle serves every JSON/sidecar read; the zarr wrapper serves every
    # leaf open via deep paths through the parentless digit tree.
    obstore_store = _resolve_store(store_root, store, store_kwargs)
    zarr_store = ObjectStore(obstore_store, read_only=True)
    if manifest is not None:
        manifest = parse_manifest(manifest)
    else:
        manifest = read_manifest(store_root, store=obstore_store)
    if manifest is None:
        if product is None:
            # A multi-product root has no root manifest by design (§6.5
            # content discrimination) — probe for named products so the
            # error is pointed, not "not a hive store". Error path only.
            from moczarr.products import list_products

            names = [p["name"] for p in list_products(store_root, store=obstore_store)]
            if names:
                raise ValueError(
                    f"{store_root} is a multi-product store root (products: {names}); "
                    f"pass product=... to open one (D19, mortie spec §6.5)"
                )
        raise ValueError(f"no morton_hive.json at {store_root} — not a hive store root")
    aoi_words = as_moc_words(aoi) if aoi is not None else None
    group = str(manifest["cell_order"])
    opened = []
    candidates = candidate_leaves(
        store_root, manifest, aoi_words, window, store=obstore_store, concurrency=concurrency
    )
    # One zarr.json GET per candidate serves BOTH the commit stamp and — for
    # the tree layer's per-object role surfacing (_objects_out, a private
    # collection hook in the zagg occupied_out style) — the D11 role attrs.
    metas = read_leaf_metas(store_root, candidates, store=obstore_store, concurrency=concurrency)
    stamps = [_stamp_from_meta(meta) for meta in metas]
    domain = None  # index_kind="moc": the accumulated interval-set row domain
    for rel, meta, stamp in zip(candidates, metas, stamps):
        if stamp is None:
            continue  # debris or a MOC-listed shard whose leaf is gone (D4)
        if _objects_out is not None:
            # Per-object role entry for every leaf this open ADMITS, recorded
            # before the AOI rejects below: the roster answers structural
            # questions (moczarr.pyramid.source_orders), so it must not shrink
            # with the query. Role absence means source (zagg spec §4.3); a
            # role-carrying object inside the source domain is off-convention
            # but surfaced verbatim rather than hidden (mixture-tolerant).
            attrs = meta.get("attributes") if isinstance(meta, dict) else None
            node_dec, node_label = split_leaf_name(rel.rsplit("/", 1)[-1])
            _objects_out.append(
                {
                    "node": node_dec,
                    "window": node_label,
                    "role": (attrs or {}).get("role") or "source",
                }
            )
        if aoi_words is not None:
            coverage = parse_leaf_coverage(stamp)
            if coverage is not None and coverage.get("box"):
                if box_and(coverage, aoi_words).size == 0:
                    continue  # conservative reject: false positives only
        if index_kind == "moc":
            # The leaf's row domain is arithmetic: zagg leaves are dense
            # within a shard, so rows = subtree ∩ AOI — the exact set the
            # pandas path's aoi_mask keeps, computed without reading the
            # stored coordinate (interval space, moczarr.ranges).
            from moczarr.ranges import MortonRanges

            shard, _label = split_leaf_name(rel.rsplit("/", 1)[-1])
            leaf_domain = MortonRanges.from_shards([morton_word(shard)], int(group))
            if aoi_words is not None:
                leaf_domain = leaf_domain.intersect(aoi_words)
                if leaf_domain.size == 0:
                    continue  # same skip the pandas path's empty aoi_mask takes
        try:
            ds = _open_leaf_group(rel, stamp, group, zarr_store, xr_kwargs)
        except FileNotFoundError:
            # Zagg spec §1.5's corrupted leaf: a pointer naming a version that
            # is missing or unstamped is debris. This path detects a MISSING
            # version only — the open reads arrays, never the version's own
            # stamp, so an unstamped version whose arrays survive is served
            # (catching it would cost one GET per versioned leaf; the
            # verifier, which pays it, reports that leaf as debris). The
            # miss is confirmed with one GET on this error path only (the
            # working open pays nothing), and it WARNS — unlike ordinary
            # debris, a write that never finished, a stamped pointer over
            # nothing means committed data was removed out of band. Any other
            # miss raises as it always has: a legacy leaf, or a stamped
            # version, without the manifest's cell-order group is a malformed
            # leaf, not debris.
            if not _dangling_pointer(store_root, rel, stamp, obstore_store):
                raise
            warnings.warn(
                f"leaf {rel} at {store_root} points at version "
                f"{stamp[LEAF_CURRENT_KEY]!r}, which is missing or unstamped — a "
                f"corrupted leaf, read as debris (zagg spec §1.5: the pointer is "
                f"written only after its version is stamped, so the version was "
                f"removed out of band)",
                UserWarning,
                stacklevel=2,
            )
            continue
        _check_composition_fill(ds, rel)
        coords = [name for name in ("morton", "cell_ids") if name in ds]
        ds = ds.set_coords(coords)
        if index_kind == "moc":
            # Drop the on-disk cell arrays before concat — lazily built, so
            # no chunk was read; the coordinate is the index's to fabricate.
            moc_dim = ds["morton"].dims[0] if "morton" in ds.coords else "cells"
            ds = ds.drop_vars(coords)
            if aoi_words is not None:
                full = MortonRanges.from_shards([morton_word(shard)], int(group))
                ds = ds.isel({moc_dim: full.rank(leaf_domain.fabricate())})
            domain = leaf_domain if domain is None else domain.union(leaf_domain)
        elif aoi_words is not None and "morton" in ds.coords:
            keep = aoi_mask(np.asarray(ds["morton"].values, dtype=np.uint64), aoi_words)
            if not keep.any():
                continue
            ds = ds.isel({ds["morton"].dims[0]: keep})
        opened.append(ds)
    if not opened:
        # Issue #4 contract: emptiness against a covered store is a data
        # answer — a schema-correct 0-row dataset plus a UserWarning. The
        # schema comes from ONE stamped leaf's metadata (an AOI-rejected
        # candidate when one exists, else _schema_leaf's store-wide search);
        # only a store with no stamped leaf anywhere has no schema to serve
        # and raises NoCoverageError.
        schema = _first_readable(store_root, candidates, stamps, obstore_store)
        schema_from_walk = False
        if schema is None:
            schema = _schema_leaf(
                store_root,
                window,
                store=obstore_store,
                concurrency=concurrency,
                path_grouping=manifest_path_grouping(manifest),
            )
            schema_from_walk = True
        if schema is None:
            raise NoCoverageError(
                f"nothing to open at {store_root}: the store has no stamped coverage "
                f"anywhere (no committed leaf exists to define a schema)"
            )
        schema_rel, schema_stamp = schema
        scope = [
            part
            for part, active in (
                ("the given AOI", aoi_words is not None),
                (f"window {window!r}", window is not None),
            )
            if active
        ]
        if not scope and schema_from_walk:
            # Unscoped whole-store open (aoi=None, window=None) where the root
            # MOC lists no openable leaf, yet _schema_leaf's walk found a
            # committed leaf on disk. That is a STALE root MOC, not an empty
            # store: silently returning 0 cells here (issue #4's empty
            # contract is scoped to an AOI/window over no coverage) would
            # hide committed data from a whole-store open. Raise instead —
            # never auto-walk the read path; regenerate the coverage
            # explicitly. Opting this case into the empty return is a
            # one-line change if that lean is preferred later.
            raise ValueError(
                f"stale root MOC at {store_root}: the root coverage lists no "
                f"openable leaf, but a committed leaf exists on disk at "
                f"{schema_rel!r}. Regenerate the root coverage before opening "
                f"(the store's writer / zagg's refresh_root_coverage / the "
                f"coverage sweep)."
            )
        # scope is non-empty here: the only unscoped way into this path is a
        # stale root MOC, handled by the raise above.
        warnings.warn(
            f"{' in '.join(scope)} intersects no coverage at {store_root}"
            "; returning a schema-correct empty dataset (0 cells)",
            UserWarning,
            stacklevel=2,
        )
        ds = _open_leaf_group(schema_rel, schema_stamp, group, zarr_store, xr_kwargs)
        _check_composition_fill(ds, schema_rel)
        coords = [name for name in ("morton", "cell_ids") if name in ds]
        ds = ds.set_coords(coords)
        empty_dim = ds["morton"].dims[0] if "morton" in ds.coords else "cells"
        if index_kind == "moc":
            from moczarr.ranges import MortonRanges

            moc_dim = empty_dim
            ds = ds.drop_vars(coords)
            domain = MortonRanges(np.empty((0, 2), dtype=np.uint64), int(group))
        opened.append(ds.isel({empty_dim: slice(0)}))
    dim = opened[0]["morton"].dims[0] if "morton" in opened[0].coords else "cells"
    if index_kind == "moc":
        dim = moc_dim
    result = xr.concat(opened, dim=dim) if len(opened) > 1 else opened[0]
    if index_kind == "moc":
        from moczarr.moc_index import MortonMocIndex

        assert domain is not None  # a leaf set it, or the empty path did
        index = MortonMocIndex(domain, dim=dim, name="morton")
        result = result.assign_coords(xr.Coordinates.from_xindex(index))
    if "morton" in result.coords and (
        fabricate_cell_ids is True
        or (fabricate_cell_ids == "auto" and "cell_ids" not in result.coords)
    ):
        ids = _fabricate_cell_ids(
            np.asarray(result["morton"].values, dtype=np.uint64),
            level=int(manifest["cell_order"]),
            # +1 frame vs a direct call so the >24 warning lands on the
            # user's open_hive(...) line, not this internal call site.
            _stacklevel=4,
        )
        result = result.assign_coords(cell_ids=(result["morton"].dims, ids))
    # Data-variable order is otherwise the completion order of zarr-python's
    # async member listing — non-deterministic run to run (a fresh open
    # reshuffles ds.data_vars). Sort lexically so a given store always opens
    # with the same variable order: reproducible reprs and stable notebook
    # outputs. Coordinates and the morton index ride along unchanged.
    result = result[sorted(result.data_vars)]
    result.attrs["morton_hive"] = {
        k: manifest[k] for k in ("spec", "cell_order", "shard_order", "dataset")
    }
    if decode:
        from moczarr import dggs  # lazy: raises the pointed extra hint when absent

        result = dggs.decode(result, index_kind=index_kind)
    return result

open_leaf(store_root, shard, *, window=None, product=None, manifest=None, stamp=None, anonymous=False, store=None, **store_kwargs)

Open ONE shard's leaf zarr as a read-only zarr store.

The leaf-direct twin of :func:open_hive, for the per-leaf readers (:func:moczarr.hhdc.read_tensors, :func:moczarr.ragged.open_ragged, ...): it owns the three layers a caller otherwise hand-assembles — the leaf path from the manifest's grammar (convention.leaf_path under the manifest's path_grouping), the transport with the SAME credential / anonymous handling as :func:open_hive, and the zarr.storage.ObjectStore read-only wrapper the readers take. No external path arithmetic, no bare-obstore incantation.

It also follows the versioned-leaf pointer (zagg spec §1.5): ONE GET of the leaf's root zarr.json (none when stamp= is threaded), and when that stamp names current the returned store is rooted at the version subgroup holding the arrays (:func:moczarr.store.leaf_data_prefix) — a legacy leaf, and an unstamped or absent one, root at the stable prefix as before. Either way the readers address {cell_order}/{name} unchanged.

Parameters:

Name Type Description Default
store_root str

The hive store root (s3://... or a local directory).

required
shard str or int

The leaf's shard — packed morton word or decimal string.

required
window str

Time-window label for a windowed leaf ({id}_{window}.zarr). Required on a windowed (morton-hive/2) store and refused on an unwindowed one, matching :func:open_hive; the reserved all-time token all is refused at the same seam (espg/moczarr#30).

None
product str

Product name under a multi-product root (D19) — the open re-roots on the product subtree, exactly as :func:open_hive does.

None
manifest dict

An already-read manifest of the subtree actually opened — the PRODUCT's manifest when product is given, not the root's. Passing it skips this call's manifest GET (the iterate-many-leaves case reads it once and threads it here) and is validated through :func:moczarr.convention.parse_manifest like any read one.

None
stamp dict

The leaf's already-read ROOT commit stamp — e.g. from :func:moczarr.store.read_commits over :func:candidate_leaves. Passing it skips the stamp GET, so with manifest= too the open issues no request; None (also debris's answer) reads it.

None
anonymous bool

Skip request signing (public buckets).

False
store obstore store

A shared handle for the manifest and root-stamp GETs (issue #5), rooted at the subtree this call opens — the PRODUCT subtree when product is given. The returned leaf store is always a fresh, leaf-rooted open; only those metadata reads can share a handle.

None
**store_kwargs Any

Forwarded to :func:moczarr.store.open_object_store (region=..., explicit keys, ...).

{}

Returns:

Type Description
ObjectStore

Read-only store rooted at the leaf's arrays (the current version of a versioned leaf); pass it with a field path like "{cell_order}/{name}" to the per-leaf readers. Deliberately bare — it carries no manifest — so a caller that needs cell_order for those paths takes it from manifest= (the one it threaded) or from :func:moczarr.store.read_manifest.

Raises:

Type Description
ValueError

When store_root (or the product subtree) has no manifest — the path grammar (path_grouping) is manifest-declared, so a leaf path cannot be derived without one — when window disagrees with the manifest's spec, when shard is not at the manifest's shard_order (a cell id where a shard id belongs), and when the leaf's stamp names a current that is not a version name.

Source code in src/moczarr/open.py
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
def open_leaf(
    store_root: str,
    shard: str | int,
    *,
    window: str | None = None,
    product: str | None = None,
    manifest: dict | None = None,
    stamp: dict | None = None,
    anonymous: bool = False,
    store: Any = None,
    **store_kwargs: Any,
):
    """Open ONE shard's leaf zarr as a read-only zarr store.

    The leaf-direct twin of :func:`open_hive`, for the per-leaf readers
    (:func:`moczarr.hhdc.read_tensors`, :func:`moczarr.ragged.open_ragged`,
    ...): it owns the three layers a caller otherwise hand-assembles — the
    leaf path from the manifest's grammar (``convention.leaf_path`` under the
    manifest's ``path_grouping``), the transport with the SAME credential /
    ``anonymous`` handling as :func:`open_hive`, and the
    ``zarr.storage.ObjectStore`` read-only wrapper the readers take. No
    external path arithmetic, no bare-obstore incantation.

    It also follows the versioned-leaf pointer (zagg spec §1.5): ONE GET of
    the leaf's root ``zarr.json`` (none when ``stamp=`` is threaded), and
    when that stamp names ``current`` the
    returned store is rooted at the version subgroup holding the arrays
    (:func:`moczarr.store.leaf_data_prefix`) — a legacy leaf, and an
    unstamped or absent one, root at the stable prefix as before. Either
    way the readers address ``{cell_order}/{name}`` unchanged.

    Parameters
    ----------
    store_root : str
        The hive store root (``s3://...`` or a local directory).
    shard : str or int
        The leaf's shard — packed morton word or decimal string.
    window : str, optional
        Time-window label for a windowed leaf (``{id}_{window}.zarr``).
        Required on a windowed (``morton-hive/2``) store and refused on an
        unwindowed one, matching :func:`open_hive`; the reserved all-time
        token ``all`` is refused at the same seam (espg/moczarr#30).
    product : str, optional
        Product name under a multi-product root (D19) — the open re-roots on
        the product subtree, exactly as :func:`open_hive` does.
    manifest : dict, optional
        An already-read manifest **of the subtree actually opened** — the
        PRODUCT's manifest when ``product`` is given, not the root's.
        Passing it skips this call's manifest GET (the iterate-many-leaves
        case reads it once and threads it here) and is validated through
        :func:`moczarr.convention.parse_manifest` like any read one.
    stamp : dict, optional
        The leaf's already-read ROOT commit stamp — e.g. from
        :func:`moczarr.store.read_commits` over :func:`candidate_leaves`.
        Passing it skips the stamp GET, so with ``manifest=`` too the open
        issues no request; ``None`` (also debris's answer) reads it.
    anonymous : bool
        Skip request signing (public buckets).
    store : obstore store, optional
        A shared handle for the manifest and root-stamp GETs (issue #5),
        rooted at the subtree this call opens — the PRODUCT subtree when
        ``product`` is given. The returned leaf store is always a fresh,
        leaf-rooted open; only those metadata reads can share a handle.
    **store_kwargs
        Forwarded to :func:`moczarr.store.open_object_store`
        (``region=...``, explicit keys, ...).

    Returns
    -------
    zarr.storage.ObjectStore
        Read-only store rooted at the leaf's arrays (the current version
        of a versioned leaf); pass it with a field path like
        ``"{cell_order}/{name}"`` to the per-leaf readers. Deliberately bare
        — it carries no manifest — so a caller that needs ``cell_order`` for
        those paths takes it from ``manifest=`` (the one it threaded) or
        from :func:`moczarr.store.read_manifest`.

    Raises
    ------
    ValueError
        When ``store_root`` (or the product subtree) has no manifest — the
        path grammar (``path_grouping``) is manifest-declared, so a leaf
        path cannot be derived without one — when ``window`` disagrees with
        the manifest's spec, when ``shard`` is not at the manifest's
        ``shard_order`` (a cell id where a shard id belongs), and when the
        leaf's stamp names a ``current`` that is not a version name.
    """
    from zarr.storage import ObjectStore

    if anonymous:
        store_kwargs.setdefault("anonymous", True)
    if product is not None:
        from moczarr.products import validate_product_name

        validate_product_name(product)
        store_root = f"{store_root.rstrip('/')}/{product}"
    if window is not None:
        # The same window seam every non-overview entry point runs through;
        # it is also what refuses the reserved all-time token (#30).
        validate_window(window, where=store_root)
    handle = store
    if manifest is None:
        # ONE subtree-rooted handle for this call's metadata reads: the
        # manifest here and the leaf's root stamp below.
        handle = _resolve_store(store_root, store, store_kwargs)
        manifest = read_manifest(store_root, store=handle)
        if manifest is None:
            if product is None:
                # A multi-product root has no root manifest by design (§6.5);
                # probe so the error is pointed, exactly as open_hive's is.
                names = [p["name"] for p in list_products(store_root, store=handle)]
                if names:
                    raise ValueError(
                        f"{store_root} is a multi-product store root (products: {names}); "
                        f"pass product=... to open one (D19, mortie spec §6.5)"
                    )
            raise ValueError(f"no morton_hive.json at {store_root} — not a hive store root")
    else:
        manifest = parse_manifest(manifest)
    windowed = manifest["spec"] == HIVE_SPEC_V2
    if window is not None and not windowed:
        raise ValueError(
            f"window={window!r} on a {manifest['spec']} store: unwindowed stores "
            f"have no window leaves (schedule: none)"
        )
    if window is None and windowed:
        # open_hive lists the labels it walked; a leaf-direct open names ONE
        # object and never enumerates, so it says what it needs instead.
        raise ValueError(
            f"{store_root} is a windowed ({HIVE_SPEC_V2}) store; pass window=... "
            f"(a leaf-direct open names one object, so there is nothing to enumerate)"
        )
    rel = leaf_path(shard, window, path_grouping=manifest_path_grouping(manifest))
    order = decimal_order(morton_decimal(shard))
    if order != int(manifest["shard_order"]):
        raise ValueError(
            f"shard {shard!r} is an order-{order} id, but {store_root} shards at order "
            f"{manifest['shard_order']} — leaves are named by SHARD id (a cell id "
            f"names no leaf)"
        )
    # The returned store is rooted where the arrays ARE (zagg spec §1.5): the
    # version a versioned leaf's root stamp names, else the root itself — so
    # every per-leaf reader keeps addressing ``{cell_order}/{name}`` and finds
    # the version's own stamp and ``coverage.moc`` beside the arrays.
    if stamp is None:
        stamp = read_commit(store_root, rel, store=handle, **store_kwargs)
    prefix = leaf_data_prefix(rel, stamp)
    leaf_root = f"{store_root.rstrip('/')}/{prefix}"
    return ObjectStore(open_object_store(leaf_root, **store_kwargs), read_only=True)

open_store(store_root, *, products=None, aoi=None, window=None, anonymous=False, fabricate_cell_ids='auto', decode=False, index_kind='moc', concurrency=32, xr_kwargs=None, **store_kwargs)

Open a store root — multi-product or bare — as one xarray.DataTree.

The tree shape follows the zagg O14 rulings (issue #15): the product axis becomes child nodes — one node per named product (D19, mortie spec §6.5), each holding exactly the lazy Dataset :func:open_hive returns for that product — and the root node is empty (store-level attrs only; a multi-product root is manifest-less by design). The window axis is never nodes (time stays a per-node dimension, scoped by window=), and the spatial digit axis is never nodes.

Resolution (pyramid-order) nodes (issue #15 phase 8b): when a product's manifest declares sweep-generated overviews (the zagg spec §4.5 pyramid.overview.orders binding — manifest declaration is the only discovery path), the product node becomes an empty intermediate and the stored orders become its children, named by the integer cell order they store: the source data is the source-order child (the same open_hive Dataset, plus per-object role entries under attrs["zagg_objects"]) and each declared overview order with at least one stamped object is a sibling node opened by :func:moczarr.pyramid.open_overview_order. role is per object, never per node; sibling variable sets differ in both directions; the seamless mixed-order composite is a computed view, never a node. Selection helpers: :func:moczarr.pyramid.source_orders, :func:moczarr.pyramid.overview_orders, :func:moczarr.pyramid.finest_source_at.

A zagg-pyramid/2 declaration (the fixed ladder, issue #36b) grows the same level: one child per materialized RESOLUTION of :func:moczarr.level.pyramid_levels' table — the §4.6 column-carried leaf resolutions included — named o{cell_order} (the espg-ruled reader-side group grammar, shared with :func:moczarr.level.open_pyramid: dot access works and the name is an unambiguous string; store-side wire paths stay numeric), each holding the :func:moczarr.level.open_level Dataset for that cell order (zagg_level/zagg_objects attrs riding along), with the normalized declaration record under the product node's zagg_pyramid attr. decode=True is refused for such a product (pyramid level surfaces are native moczarr only — the englacial/zagg#550 ruling), and an unknown pyramid revision fails loudly rather than reading as declared-off (§4.5's conformance rule).

Named after its scope (the store), not its return type: the xarray- native name open_datatree is reserved for zarr-native hierarchies, which the hive tree deliberately is not (the D12 interop hierarchy is xr.open_datatree's to open someday).

Parameters:

Name Type Description Default
store_root str

Store root (local directory or s3://bucket/prefix): a multi-product root (named product children, no root manifest) or a bare single-product store (manifest at the root), discriminated by content per spec §6.5.

required
products sequence of str

Open only these products (a filter for stores with many). An empty sequence raises — a childless tree is never the intended answer; pass None (the default) for "all". Names are checked against the spec §6.5 grammar before any I/O; unknown (but well-formed) names raise once the roster is known, listing what exists. Children stay in name-sorted order regardless of the filter's order.

None
aoi

Forwarded to each node's :func:open_hive (see there), with one tree-level refinement: window is forwarded only to the time-windowed (morton-hive/2) products — an unwindowed product's node holds the whole product (it has no window leaves to select), so one call opens a store that mixes windowed and unwindowed products. A window against a tree with no windowed product raises, preserving :func:open_hive's typo-catching posture. Omitting window while a windowed product is selected raises per-product with the labels that exist (scope with products= to skip windowed products instead).

None
window

Forwarded to each node's :func:open_hive (see there), with one tree-level refinement: window is forwarded only to the time-windowed (morton-hive/2) products — an unwindowed product's node holds the whole product (it has no window leaves to select), so one call opens a store that mixes windowed and unwindowed products. A window against a tree with no windowed product raises, preserving :func:open_hive's typo-catching posture. Omitting window while a windowed product is selected raises per-product with the labels that exist (scope with products= to skip windowed products instead).

None
anonymous

Forwarded to each node's :func:open_hive (see there), with one tree-level refinement: window is forwarded only to the time-windowed (morton-hive/2) products — an unwindowed product's node holds the whole product (it has no window leaves to select), so one call opens a store that mixes windowed and unwindowed products. A window against a tree with no windowed product raises, preserving :func:open_hive's typo-catching posture. Omitting window while a windowed product is selected raises per-product with the labels that exist (scope with products= to skip windowed products instead).

None
fabricate_cell_ids

Forwarded to each node's :func:open_hive (see there), with one tree-level refinement: window is forwarded only to the time-windowed (morton-hive/2) products — an unwindowed product's node holds the whole product (it has no window leaves to select), so one call opens a store that mixes windowed and unwindowed products. A window against a tree with no windowed product raises, preserving :func:open_hive's typo-catching posture. Omitting window while a windowed product is selected raises per-product with the labels that exist (scope with products= to skip windowed products instead).

None
decode

Forwarded to each node's :func:open_hive (see there), with one tree-level refinement: window is forwarded only to the time-windowed (morton-hive/2) products — an unwindowed product's node holds the whole product (it has no window leaves to select), so one call opens a store that mixes windowed and unwindowed products. A window against a tree with no windowed product raises, preserving :func:open_hive's typo-catching posture. Omitting window while a windowed product is selected raises per-product with the labels that exist (scope with products= to skip windowed products instead).

None
index_kind

Forwarded to each node's :func:open_hive (see there), with one tree-level refinement: window is forwarded only to the time-windowed (morton-hive/2) products — an unwindowed product's node holds the whole product (it has no window leaves to select), so one call opens a store that mixes windowed and unwindowed products. A window against a tree with no windowed product raises, preserving :func:open_hive's typo-catching posture. Omitting window while a windowed product is selected raises per-product with the labels that exist (scope with products= to skip windowed products instead).

None
concurrency

Forwarded to each node's :func:open_hive (see there), with one tree-level refinement: window is forwarded only to the time-windowed (morton-hive/2) products — an unwindowed product's node holds the whole product (it has no window leaves to select), so one call opens a store that mixes windowed and unwindowed products. A window against a tree with no windowed product raises, preserving :func:open_hive's typo-catching posture. Omitting window while a windowed product is selected raises per-product with the labels that exist (scope with products= to skip windowed products instead).

None
xr_kwargs

Forwarded to each node's :func:open_hive (see there), with one tree-level refinement: window is forwarded only to the time-windowed (morton-hive/2) products — an unwindowed product's node holds the whole product (it has no window leaves to select), so one call opens a store that mixes windowed and unwindowed products. A window against a tree with no windowed product raises, preserving :func:open_hive's typo-catching posture. Omitting window while a windowed product is selected raises per-product with the labels that exist (scope with products= to skip windowed products instead).

None
**store_kwargs

Forwarded to each node's :func:open_hive (see there), with one tree-level refinement: window is forwarded only to the time-windowed (morton-hive/2) products — an unwindowed product's node holds the whole product (it has no window leaves to select), so one call opens a store that mixes windowed and unwindowed products. A window against a tree with no windowed product raises, preserving :func:open_hive's typo-catching posture. Omitting window while a windowed product is selected raises per-product with the labels that exist (scope with products= to skip windowed products instead).

None

Returns:

Type Description
DataTree

Root node: no data variables, no coordinates — only attrs["morton_hive_store"] (store_root plus the name-sorted products roster the store publishes, unfiltered — exactly :func:list_products, so it is [] for a bare store). One child node per opened product, named by its product name; each child is the per-node open_hive Dataset unchanged (laziness, index posture, and the issue-#4 empty-AOI contract all apply per node), plus attrs["semantic_hash"] when the product's manifest carries the D19 identity hash. Sibling schemas may differ freely — the non-alignable-groups case is exactly what the tree is for.

A bare single-product store returns the valid degenerate form: a one-child tree, the child named from the manifest's dataset short_name lowercased (fallback "product"). That name is derived, not a published product, so the roster stays [] and the store attrs carry bare: True plus node: <derived name> instead of fabricating a roster list_products would deny — and products= against a bare store raises rather than filtering on a name the store never published.

Raises ValueError when the root has neither products nor a manifest, and per-node errors (NoCoverageError, the windowed window= requirement) propagate unchanged.

Source code in src/moczarr/open.py
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
def open_store(
    store_root: str,
    *,
    products: Sequence[str] | None = None,
    aoi=None,
    window: str | None = None,
    anonymous: bool = False,
    fabricate_cell_ids: bool | str = "auto",
    decode: bool = False,
    index_kind: str = "moc",
    concurrency: int | None = 32,
    xr_kwargs: dict[str, Any] | None = None,
    **store_kwargs: Any,
):
    """Open a store root — multi-product or bare — as one ``xarray.DataTree``.

    The tree shape follows the zagg O14 rulings (issue #15): the **product
    axis becomes child nodes** — one node per named product (D19, mortie
    spec §6.5), each holding exactly the lazy Dataset :func:`open_hive`
    returns for that product — and the root node is empty (store-level
    attrs only; a multi-product root is manifest-less by design). The
    window axis is **never** nodes (time stays a per-node dimension,
    scoped by ``window=``), and the spatial digit axis is never nodes.

    **Resolution (pyramid-order) nodes** (issue #15 phase 8b): when a
    product's manifest declares sweep-generated overviews (the zagg spec
    §4.5 ``pyramid.overview.orders`` binding — manifest declaration is the
    only discovery path), the product node becomes an empty intermediate
    and the stored orders become its children, named by the integer cell
    order they store: the source data is the source-order child (the same
    ``open_hive`` Dataset, plus per-object role entries under
    ``attrs["zagg_objects"]``) and each declared overview order with at
    least one stamped object is a sibling node opened by
    :func:`moczarr.pyramid.open_overview_order`. ``role`` is per object,
    never per node; sibling variable sets differ in both directions; the
    seamless mixed-order composite is a *computed* view, never a node.
    Selection helpers: :func:`moczarr.pyramid.source_orders`,
    :func:`moczarr.pyramid.overview_orders`,
    :func:`moczarr.pyramid.finest_source_at`.

    A ``zagg-pyramid/2`` declaration (the fixed ladder, issue #36b) grows
    the same level: one child per materialized RESOLUTION of
    :func:`moczarr.level.pyramid_levels`' table — the §4.6 column-carried
    leaf resolutions included — named ``o{cell_order}`` (the espg-ruled
    reader-side group grammar, shared with
    :func:`moczarr.level.open_pyramid`: dot access works and the name is an
    unambiguous string; store-side wire paths stay numeric), each holding
    the :func:`moczarr.level.open_level` Dataset for that cell order
    (``zagg_level``/``zagg_objects`` attrs riding along), with the
    normalized declaration record under the product node's
    ``zagg_pyramid`` attr. ``decode=True`` is refused for such a product
    (pyramid level surfaces are native moczarr only — the englacial/zagg#550
    ruling), and an unknown pyramid revision fails loudly rather than
    reading as declared-off (§4.5's conformance rule).

    Named after its scope (the store), not its return type: the xarray-
    native name ``open_datatree`` is reserved for zarr-native hierarchies,
    which the hive tree deliberately is not (the D12 interop hierarchy is
    ``xr.open_datatree``'s to open someday).

    Parameters
    ----------
    store_root : str
        Store root (local directory or ``s3://bucket/prefix``): a
        multi-product root (named product children, no root manifest) or a
        bare single-product store (manifest at the root), discriminated by
        content per spec §6.5.
    products : sequence of str, optional
        Open only these products (a filter for stores with many). An empty
        sequence raises — a childless tree is never the intended answer;
        pass ``None`` (the default) for "all". Names are
        checked against the spec §6.5 grammar **before any I/O**; unknown
        (but well-formed) names raise once the roster is known, listing what
        exists. Children stay in name-sorted order regardless of the
        filter's order.
    aoi, window, anonymous, fabricate_cell_ids, decode, index_kind, \
concurrency, xr_kwargs, **store_kwargs
        Forwarded to each node's :func:`open_hive` (see there), with one
        tree-level refinement: ``window`` is forwarded only to the
        time-windowed (``morton-hive/2``) products — an unwindowed
        product's node holds the whole product (it has no window leaves to
        select), so one call opens a store that mixes windowed and
        unwindowed products. A ``window`` against a tree with *no*
        windowed product raises, preserving :func:`open_hive`'s
        typo-catching posture. Omitting ``window`` while a windowed
        product is selected raises per-product with the labels that exist
        (scope with ``products=`` to skip windowed products instead).

    Returns
    -------
    xarray.DataTree
        Root node: no data variables, no coordinates — only
        ``attrs["morton_hive_store"]`` (``store_root`` plus the name-sorted
        ``products`` roster the store *publishes*, unfiltered — exactly
        :func:`list_products`, so it is ``[]`` for a bare store). One child node
        per opened product, named by its product name; each child is the
        per-node ``open_hive`` Dataset unchanged (laziness, index posture,
        and the issue-#4 empty-AOI contract all apply per node), plus
        ``attrs["semantic_hash"]`` when the product's manifest carries the
        D19 identity hash. Sibling schemas may differ freely — the
        non-alignable-groups case is exactly what the tree is for.

        A **bare single-product store** returns the valid degenerate form:
        a one-child tree, the child named from the manifest's dataset
        ``short_name`` lowercased (fallback ``"product"``). That name is
        *derived*, not a published product, so the roster stays ``[]`` and
        the store attrs carry ``bare: True`` plus ``node: <derived name>``
        instead of fabricating a roster ``list_products`` would deny —
        and ``products=`` against a bare store raises rather than filtering
        on a name the store never published.

        Raises ``ValueError`` when the root has neither products nor a
        manifest, and per-node errors (``NoCoverageError``, the windowed
        ``window=`` requirement) propagate unchanged.
    """
    import xarray as xr

    # Grammar before I/O: an off-spec product name is a caller bug, so it
    # costs no LIST/GET to reject (spec §6.5 names are pure syntax). An
    # empty filter is loud too — a childless tree is the module's one silent
    # nonsense answer, and in practice it means the caller's filter list
    # came out empty.
    wanted = {validate_product_name(name) for name in products} if products is not None else None
    if wanted is not None and not wanted:
        raise ValueError(
            f"products=[] selects nothing at {store_root} — omit products= to open every product"
        )

    obstore_store = open_object_store(store_root, anonymous=anonymous, **store_kwargs)
    records = list_products(store_root, store=obstore_store)
    bare = not records
    if bare:
        manifest = read_manifest(store_root, store=obstore_store)
        if manifest is None:
            raise ValueError(
                f"no products and no morton_hive.json at {store_root} — not a hive store root"
            )
        records = [
            {
                "name": _degenerate_node_name(manifest),
                "spec": manifest["spec"],
                "semantic_hash": manifest.get("semantic_hash"),
                "manifest": manifest,
            }
        ]
    # The roster is what the store *publishes* — exactly ``list_products``,
    # empty for a bare store; the degenerate child's name is derived from
    # the manifest, so it is reported separately and never as a product.
    roster = [] if bare else [record["name"] for record in records]
    if wanted is not None:
        if bare:
            raise ValueError(
                f"products={sorted(wanted)} but {store_root} is a bare single-product store "
                "(no named products — spec §6.5 product prefixes); open it without products="
            )
        missing = sorted(wanted - set(roster))
        if missing:
            raise ValueError(
                f"products {missing} not found at {store_root} (products present: {roster})"
            )
        records = [record for record in records if record["name"] in wanted]
    if window is not None:
        # Ahead of the typo-catching check below on purpose: a user who passes
        # the reserved all-time token gets told that, not "no windowed product
        # is selected", whatever the roster happens to be (#30).
        validate_window(window, where=store_root)
    if window is not None and all(record["spec"] != HIVE_SPEC_V2 for record in records):
        raise ValueError(
            f"window={window!r} but no windowed ({HIVE_SPEC_V2}) product is selected at "
            f"{store_root} (specs: { {r['name']: r['spec'] for r in records} })"
        )
    from moczarr import pyramid

    nodes: dict[str, Any] = {}
    for record in records:
        name = record["name"]
        node_window = window if record["spec"] == HIVE_SPEC_V2 else None
        manifest_rec = record.get("manifest")
        # Node discovery is MANIFEST DECLARATION (zagg spec §4.5; issue #15
        # phase 8b): the reader binds the pyramid block and nothing else.
        # Declared off/absent -> today's flat product node, unchanged; an
        # unknown revision fails loudly (§4.5's conformance rule — a future
        # /3 block must not read as declared-off).
        decl = pyramid.pyramid_declaration(manifest_rec) if manifest_rec else None
        if decl is not None and decl["spec"] == pyramid.PYRAMID_SPEC_V2:
            # The /2 fixed ladder (issue #36b): the product's children are
            # the RESOLUTION levels of pyramid_levels' table — source,
            # §4.6 column-carried leaf resolutions, and ladder rungs alike —
            # each opened through open_level's dispatch, so every child is
            # the #37 per-level Dataset (zagg_level/zagg_objects included).
            # A declared level with no stamped artifact is omitted with its
            # opener's warning, exactly as an unswept /1 order is.
            if decode:
                raise ValueError(
                    f"decode=True is not available for product {name!r}: its pyramid "
                    f"declares {pyramid.PYRAMID_SPEC_V2}, whose level surfaces are "
                    f"native moczarr only (the englacial/zagg#550 ruling) — open "
                    f"levels via open_pyramid/open_level and decode per level with "
                    f"moczarr.dggs.decode if needed"
                )
            from moczarr.level import open_level, pyramid_levels

            product_attrs = {
                "morton_hive": {
                    key: manifest_rec[key]
                    for key in ("spec", "cell_order", "shard_order", "dataset")
                },
                "zagg_pyramid": decl,
            }
            if record.get("semantic_hash"):
                product_attrs["semantic_hash"] = record["semantic_hash"]
            nodes[name] = xr.Dataset(attrs=product_attrs)
            product_root = store_root if bare else f"{store_root.rstrip('/')}/{name}"
            # One store construction and one root-MOC read for the whole
            # ladder (issue #5), same as the /1 loop below.
            product_store = open_object_store(product_root, anonymous=anonymous, **store_kwargs)
            product_envelope = load_root_coverage(product_root, store=product_store)
            for r in pyramid_levels(manifest_rec):
                child = open_level(
                    product_root,
                    r,
                    manifest=manifest_rec,
                    aoi=aoi,
                    window=node_window,
                    fabricate_cell_ids=fabricate_cell_ids,
                    index_kind=index_kind,
                    concurrency=concurrency,
                    xr_kwargs=xr_kwargs,
                    store=product_store,
                    _envelope=product_envelope,
                )
                if child is not None:
                    nodes[f"{name}/o{r}"] = child
            continue
        cell_orders = pyramid.overview_cell_orders(manifest_rec) if manifest_rec else {}
        objects: list[dict] = []
        ds = open_hive(
            store_root,
            product=None if bare else name,
            aoi=aoi,
            window=node_window,
            anonymous=anonymous,
            fabricate_cell_ids=fabricate_cell_ids,
            decode=decode,
            index_kind=index_kind,
            concurrency=concurrency,
            xr_kwargs=xr_kwargs,
            _objects_out=objects if cell_orders else None,
            **store_kwargs,
        )
        if not cell_orders or manifest_rec is None:  # None narrows for mypy
            if record.get("semantic_hash"):
                ds.attrs["semantic_hash"] = record["semantic_hash"]
            nodes[name] = ds
            continue
        # Pyramid form ({product}/{order}, zagg#201's ratified layout): the
        # product node becomes an empty intermediate carrying the product's
        # identity attrs; the source data becomes the source-order child
        # (named by the cell order it stores) with its per-object role
        # entries; each declared overview order that carries at least one
        # stamped object becomes a sibling order node. The seamless
        # mixed-order composite is a COMPUTED view, never a node.
        ds.attrs[pyramid.OBJECTS_ATTR] = objects
        product_attrs: dict[str, Any] = {
            "morton_hive": {
                key: manifest_rec[key] for key in ("spec", "cell_order", "shard_order", "dataset")
            }
        }
        if record.get("semantic_hash"):
            product_attrs["semantic_hash"] = record["semantic_hash"]
        nodes[name] = xr.Dataset(attrs=product_attrs)
        nodes[f"{name}/{int(manifest_rec['cell_order'])}"] = ds
        product_root = store_root if bare else f"{store_root.rstrip('/')}/{name}"
        # ONE store construction and ONE root-MOC read for this product's whole
        # order level (issue #5): the sidecar tier must not grow with the number
        # of declared orders — on a 5-order pyramid over S3 that would be 5
        # round-trips for one per-product envelope. A None envelope means "not
        # supplied", so the degraded no-MOC case re-reads and warns per order
        # exactly as before.
        product_store = open_object_store(product_root, anonymous=anonymous, **store_kwargs)
        product_envelope = load_root_coverage(product_root, store=product_store)
        for ancestor_order, target_order in cell_orders.items():
            overview_ds = pyramid.open_overview_order(
                product_root,
                manifest_rec,
                ancestor_order,
                aoi=aoi,
                window=node_window,
                fabricate_cell_ids=fabricate_cell_ids,
                decode=decode,
                index_kind=index_kind,
                concurrency=concurrency,
                xr_kwargs=xr_kwargs,
                store=product_store,
                _envelope=product_envelope,
            )
            if overview_ds is None:
                continue  # open_overview_order warned; the node is omitted
            nodes[f"{name}/{target_order}"] = overview_ds
    store_attrs: dict[str, Any] = {"store_root": store_root, "products": roster}
    if bare:
        store_attrs["bare"] = True
        store_attrs["node"] = records[0]["name"]
    root = xr.Dataset(attrs={"morton_hive_store": store_attrs})
    return xr.DataTree.from_dict({"/": root, **nodes})