docs: add spec-style description of the consolidated metadata format - #4283

Draft
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45
Draft

docs: add spec-style description of the consolidated metadata format#4283
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45

Conversation

@d-v-b

Copy link
Copy Markdown
Contributor

Summary

This is a claude-authored contribution that adds specification-style documentation for how zarr-python implements consolidated metadata. I'm pretty busy these days with a newborn baby so I can't give this careful review. I am opening this as a draft and leaving it to other folks to push it forward.

cc @normanrz

🤖 AI text below 🤖

Add a new user-guide page that describes exactly what zarr-python reads and writes for consolidated metadata in Zarr formats 2 and 3, so that other implementations can interoperate. Quotes and attributes the schema text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key ordering, the empty child-group marker, the v2 .zmetadata layout and its deviation from zarr-python 2.x, and the reader/writer procedures.

Also correct the 3.1.1 sort-order note on the existing page, which said "lexicographic" while the implementation uses NFKC-casefolded ordering.

Assisted-by: ClaudeCode:claude-fable-5

Author attestation

  • I am a human, these are my changes, and I have reviewed and understood every change and can explain why each is correct.

TODO

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/user-guide/*.md
  • Changes documented as a new file in changes/
  • GitHub Actions have all passed
  • Test coverage is 100% (Codecov passes)

Add a new user-guide page that describes exactly what zarr-python reads
and writes for consolidated metadata in Zarr formats 2 and 3, so that
other implementations can interoperate. Quotes and attributes the schema
text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key
ordering, the empty child-group marker, the v2 `.zmetadata` layout and
its deviation from zarr-python 2.x, and the reader/writer procedures.
Also correct the 3.1.1 sort-order note on the existing page, which said
"lexicographic" while the implementation uses NFKC-casefolded ordering.
Assisted-by: ClaudeCode:claude-fable-5
@github-actionsgithub-actionsBot added the needs release notes Automatically applied to PRs which haven't added release notes label Aug 25, 2026
@d-v-b

Copy link
Copy Markdown
ContributorAuthor

@TomAugspurger you should probably also have a look, since this is an LLM summarizing your work

@TomAugspurger

Copy link
Copy Markdown
Contributor

I'm pretty busy these days with a newborn baby so I can't give this careful review.

Congrats! I'll take a look when I get a chance.

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

@normanrz

Copy link
Copy Markdown
Member

I asked for a spec on Zulip and Davis was kind enough to create one. The motivation is that we picked up the consolidated metadata in the newly-formed Zarr Format Working Group and I wanted to get a better understanding of the current zarr-python implementation. Having a spec here would be great, but even better would be if we could eventually bring this document into the Zarr 3 spec.

@d-v-b

Copy link
Copy Markdown
ContributorAuthor

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another. For folks curious about the normative structure of consolidated metadata, but also zarr-python's particular implementation choices, I think some kind of document in our docs is a good play. Long term our goal should be to replace an actual spec with a link to a spec defined elsewhere.

@normanrz

Copy link
Copy Markdown
Member

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another.

Right. With @joshmoore, I am working on a proposal for the ZFWG governance and I could imagine using consolidated metadata as testcase for the governance process.

@TomAugspurgerTomAugspurger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context. With that in mind, I'll view this as a somewhat temporary document. Though just merging the zarr-specs PR feels infinitely better.

And for those interested in the implementation, rereading the discussion in #2113 might also be fruitful.

Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Consolidated metadata essentially stores all the metadata for a hierarchy in the
metadata of the root Group.

This page describes how to use consolidated metadata from Python. For a precise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some hesitancy about linking to this document from user-facing docs. I think the spec (or the PR for the spec) should be sufficient for users, and if it isn't then the docs should be improved there.

Comment on lines +91 to +93
of strings joined by `"/"`. For keys with the same depth, the tie is broken by
comparing the paths after Unicode NFKC normalization and case-folding. This
behavior ensures deterministic metadata output for a given group.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems worse for causal readers.

Let's at least keep "lexicographic" (since it isn't incorrect, right?) And if we want to be more specific we can, but let's link to the Python docs on normalization.


## Concepts

A hierarchy is **consolidated at** a group (the *consolidating group*). The

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"consolidating group" isn't a term in zarr-developers/zarr-specs#309. If we want to make it one, let's propose it there.

Comment on lines +66 to +69
(`GroupMetadata.consolidated_metadata.metadata`). It is never written to a
store and is mentioned here only because it leaks into the on-disk form in
one place (the [empty child marker](#child-groups-carry-an-empty-marker)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "leaks into" is accurate here, or it conveys the wrong impression. That's deliberate, saying that there aren't any children.


## Paths

A **path** is the name of a node relative to the consolidating group:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC paths are already defined in the spec. Link to that.

at `B` produces `C`, `y`; consolidating at `C` produces no paths (an empty
mapping, which is still written).

!!! note "Difference from the zarr-specs#309 text"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, I had a review pending from April 2025 pointing out this issue. Submitted that: zarr-developers/zarr-specs#309 (comment) and we can fix it in the spec.

Each value is a complete node metadata document, i.e. exactly what would be
found in that node's own `zarr.json`, with the following rules:

* The document MUST contain `zarr_format`. Readers discriminate on this first;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this isn't intended / well-tested.

Comment on lines +188 to +190
This is the one place the nested in-memory form shows through. The marker
does **not** mean the child group has no children: the child's descendants are
still listed in the flat mapping at the consolidating group. It exists so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong (or I'm misunderstanding something). But AFAIK the presence of metadata: {} definitely does indicate a group with no children.

d-v-band others added 2 commits August 30, 2026 22:22
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
@codecov

codecovBot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.21%. Comparing base (5b5f3a3) to head (c1bae56).

Additional details and impacted files
@@ Coverage Diff @@## main #4283 +/- ##
=======================================
Coverage 94.21% 94.21% =======================================
Files 92 92 Lines 12861 12861 =======================================
Hits 12117 12117 Misses 744 744 
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs release notesAutomatically applied to PRs which haven't added release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@d-v-b@TomAugspurger@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

docs: add spec-style description of the consolidated metadata format - #4283

Draft
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45
Draft

docs: add spec-style description of the consolidated metadata format#4283
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45

Conversation

@d-v-b

Copy link
Copy Markdown
Contributor

Summary

This is a claude-authored contribution that adds specification-style documentation for how zarr-python implements consolidated metadata. I'm pretty busy these days with a newborn baby so I can't give this careful review. I am opening this as a draft and leaving it to other folks to push it forward.

cc @normanrz

🤖 AI text below 🤖

Add a new user-guide page that describes exactly what zarr-python reads and writes for consolidated metadata in Zarr formats 2 and 3, so that other implementations can interoperate. Quotes and attributes the schema text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key ordering, the empty child-group marker, the v2 .zmetadata layout and its deviation from zarr-python 2.x, and the reader/writer procedures.

Also correct the 3.1.1 sort-order note on the existing page, which said "lexicographic" while the implementation uses NFKC-casefolded ordering.

Assisted-by: ClaudeCode:claude-fable-5

Author attestation

  • I am a human, these are my changes, and I have reviewed and understood every change and can explain why each is correct.

TODO

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/user-guide/*.md
  • Changes documented as a new file in changes/
  • GitHub Actions have all passed
  • Test coverage is 100% (Codecov passes)

Add a new user-guide page that describes exactly what zarr-python reads
and writes for consolidated metadata in Zarr formats 2 and 3, so that
other implementations can interoperate. Quotes and attributes the schema
text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key
ordering, the empty child-group marker, the v2 `.zmetadata` layout and
its deviation from zarr-python 2.x, and the reader/writer procedures.
Also correct the 3.1.1 sort-order note on the existing page, which said
"lexicographic" while the implementation uses NFKC-casefolded ordering.
Assisted-by: ClaudeCode:claude-fable-5
@github-actionsgithub-actionsBot added the needs release notes Automatically applied to PRs which haven't added release notes label Aug 25, 2026
@d-v-b

Copy link
Copy Markdown
ContributorAuthor

@TomAugspurger you should probably also have a look, since this is an LLM summarizing your work

@TomAugspurger

Copy link
Copy Markdown
Contributor

I'm pretty busy these days with a newborn baby so I can't give this careful review.

Congrats! I'll take a look when I get a chance.

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

@normanrz

Copy link
Copy Markdown
Member

I asked for a spec on Zulip and Davis was kind enough to create one. The motivation is that we picked up the consolidated metadata in the newly-formed Zarr Format Working Group and I wanted to get a better understanding of the current zarr-python implementation. Having a spec here would be great, but even better would be if we could eventually bring this document into the Zarr 3 spec.

@d-v-b

Copy link
Copy Markdown
ContributorAuthor

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another. For folks curious about the normative structure of consolidated metadata, but also zarr-python's particular implementation choices, I think some kind of document in our docs is a good play. Long term our goal should be to replace an actual spec with a link to a spec defined elsewhere.

@normanrz

Copy link
Copy Markdown
Member

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another.

Right. With @joshmoore, I am working on a proposal for the ZFWG governance and I could imagine using consolidated metadata as testcase for the governance process.

@TomAugspurgerTomAugspurger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context. With that in mind, I'll view this as a somewhat temporary document. Though just merging the zarr-specs PR feels infinitely better.

And for those interested in the implementation, rereading the discussion in #2113 might also be fruitful.

Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Consolidated metadata essentially stores all the metadata for a hierarchy in the
metadata of the root Group.

This page describes how to use consolidated metadata from Python. For a precise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some hesitancy about linking to this document from user-facing docs. I think the spec (or the PR for the spec) should be sufficient for users, and if it isn't then the docs should be improved there.

Comment on lines +91 to +93
of strings joined by `"/"`. For keys with the same depth, the tie is broken by
comparing the paths after Unicode NFKC normalization and case-folding. This
behavior ensures deterministic metadata output for a given group.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems worse for causal readers.

Let's at least keep "lexicographic" (since it isn't incorrect, right?) And if we want to be more specific we can, but let's link to the Python docs on normalization.


## Concepts

A hierarchy is **consolidated at** a group (the *consolidating group*). The

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"consolidating group" isn't a term in zarr-developers/zarr-specs#309. If we want to make it one, let's propose it there.

Comment on lines +66 to +69
(`GroupMetadata.consolidated_metadata.metadata`). It is never written to a
store and is mentioned here only because it leaks into the on-disk form in
one place (the [empty child marker](#child-groups-carry-an-empty-marker)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "leaks into" is accurate here, or it conveys the wrong impression. That's deliberate, saying that there aren't any children.


## Paths

A **path** is the name of a node relative to the consolidating group:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC paths are already defined in the spec. Link to that.

at `B` produces `C`, `y`; consolidating at `C` produces no paths (an empty
mapping, which is still written).

!!! note "Difference from the zarr-specs#309 text"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, I had a review pending from April 2025 pointing out this issue. Submitted that: zarr-developers/zarr-specs#309 (comment) and we can fix it in the spec.

Each value is a complete node metadata document, i.e. exactly what would be
found in that node's own `zarr.json`, with the following rules:

* The document MUST contain `zarr_format`. Readers discriminate on this first;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this isn't intended / well-tested.

Comment on lines +188 to +190
This is the one place the nested in-memory form shows through. The marker
does **not** mean the child group has no children: the child's descendants are
still listed in the flat mapping at the consolidating group. It exists so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong (or I'm misunderstanding something). But AFAIK the presence of metadata: {} definitely does indicate a group with no children.

d-v-band others added 2 commits August 30, 2026 22:22
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
@codecov

codecovBot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.21%. Comparing base (5b5f3a3) to head (c1bae56).

Additional details and impacted files
@@ Coverage Diff @@## main #4283 +/- ##
=======================================
Coverage 94.21% 94.21% =======================================
Files 92 92 Lines 12861 12861 =======================================
Hits 12117 12117 Misses 744 744 
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs release notesAutomatically applied to PRs which haven't added release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@d-v-b@TomAugspurger@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs: add spec-style description of the consolidated metadata format - #4283

Draft
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45
Draft

docs: add spec-style description of the consolidated metadata format#4283
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45

Conversation

@d-v-b

Copy link
Copy Markdown
Contributor

Summary

This is a claude-authored contribution that adds specification-style documentation for how zarr-python implements consolidated metadata. I'm pretty busy these days with a newborn baby so I can't give this careful review. I am opening this as a draft and leaving it to other folks to push it forward.

cc @normanrz

🤖 AI text below 🤖

Add a new user-guide page that describes exactly what zarr-python reads and writes for consolidated metadata in Zarr formats 2 and 3, so that other implementations can interoperate. Quotes and attributes the schema text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key ordering, the empty child-group marker, the v2 .zmetadata layout and its deviation from zarr-python 2.x, and the reader/writer procedures.

Also correct the 3.1.1 sort-order note on the existing page, which said "lexicographic" while the implementation uses NFKC-casefolded ordering.

Assisted-by: ClaudeCode:claude-fable-5

Author attestation

  • I am a human, these are my changes, and I have reviewed and understood every change and can explain why each is correct.

TODO

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/user-guide/*.md
  • Changes documented as a new file in changes/
  • GitHub Actions have all passed
  • Test coverage is 100% (Codecov passes)

Add a new user-guide page that describes exactly what zarr-python reads
and writes for consolidated metadata in Zarr formats 2 and 3, so that
other implementations can interoperate. Quotes and attributes the schema
text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key
ordering, the empty child-group marker, the v2 `.zmetadata` layout and
its deviation from zarr-python 2.x, and the reader/writer procedures.
Also correct the 3.1.1 sort-order note on the existing page, which said
"lexicographic" while the implementation uses NFKC-casefolded ordering.
Assisted-by: ClaudeCode:claude-fable-5
@github-actionsgithub-actionsBot added the needs release notes Automatically applied to PRs which haven't added release notes label Aug 25, 2026
@d-v-b

Copy link
Copy Markdown
ContributorAuthor

@TomAugspurger you should probably also have a look, since this is an LLM summarizing your work

@TomAugspurger

Copy link
Copy Markdown
Contributor

I'm pretty busy these days with a newborn baby so I can't give this careful review.

Congrats! I'll take a look when I get a chance.

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

@normanrz

Copy link
Copy Markdown
Member

I asked for a spec on Zulip and Davis was kind enough to create one. The motivation is that we picked up the consolidated metadata in the newly-formed Zarr Format Working Group and I wanted to get a better understanding of the current zarr-python implementation. Having a spec here would be great, but even better would be if we could eventually bring this document into the Zarr 3 spec.

@d-v-b

Copy link
Copy Markdown
ContributorAuthor

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another. For folks curious about the normative structure of consolidated metadata, but also zarr-python's particular implementation choices, I think some kind of document in our docs is a good play. Long term our goal should be to replace an actual spec with a link to a spec defined elsewhere.

@normanrz

Copy link
Copy Markdown
Member

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another.

Right. With @joshmoore, I am working on a proposal for the ZFWG governance and I could imagine using consolidated metadata as testcase for the governance process.

@TomAugspurgerTomAugspurger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context. With that in mind, I'll view this as a somewhat temporary document. Though just merging the zarr-specs PR feels infinitely better.

And for those interested in the implementation, rereading the discussion in #2113 might also be fruitful.

Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Consolidated metadata essentially stores all the metadata for a hierarchy in the
metadata of the root Group.

This page describes how to use consolidated metadata from Python. For a precise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some hesitancy about linking to this document from user-facing docs. I think the spec (or the PR for the spec) should be sufficient for users, and if it isn't then the docs should be improved there.

Comment on lines +91 to +93
of strings joined by `"/"`. For keys with the same depth, the tie is broken by
comparing the paths after Unicode NFKC normalization and case-folding. This
behavior ensures deterministic metadata output for a given group.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems worse for causal readers.

Let's at least keep "lexicographic" (since it isn't incorrect, right?) And if we want to be more specific we can, but let's link to the Python docs on normalization.


## Concepts

A hierarchy is **consolidated at** a group (the *consolidating group*). The

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"consolidating group" isn't a term in zarr-developers/zarr-specs#309. If we want to make it one, let's propose it there.

Comment on lines +66 to +69
(`GroupMetadata.consolidated_metadata.metadata`). It is never written to a
store and is mentioned here only because it leaks into the on-disk form in
one place (the [empty child marker](#child-groups-carry-an-empty-marker)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "leaks into" is accurate here, or it conveys the wrong impression. That's deliberate, saying that there aren't any children.


## Paths

A **path** is the name of a node relative to the consolidating group:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC paths are already defined in the spec. Link to that.

at `B` produces `C`, `y`; consolidating at `C` produces no paths (an empty
mapping, which is still written).

!!! note "Difference from the zarr-specs#309 text"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, I had a review pending from April 2025 pointing out this issue. Submitted that: zarr-developers/zarr-specs#309 (comment) and we can fix it in the spec.

Each value is a complete node metadata document, i.e. exactly what would be
found in that node's own `zarr.json`, with the following rules:

* The document MUST contain `zarr_format`. Readers discriminate on this first;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this isn't intended / well-tested.

Comment on lines +188 to +190
This is the one place the nested in-memory form shows through. The marker
does **not** mean the child group has no children: the child's descendants are
still listed in the flat mapping at the consolidating group. It exists so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong (or I'm misunderstanding something). But AFAIK the presence of metadata: {} definitely does indicate a group with no children.

d-v-band others added 2 commits August 30, 2026 22:22
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
@codecov

codecovBot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.21%. Comparing base (5b5f3a3) to head (c1bae56).

Additional details and impacted files
@@ Coverage Diff @@## main #4283 +/- ##
=======================================
Coverage 94.21% 94.21% =======================================
Files 92 92 Lines 12861 12861 =======================================
Hits 12117 12117 Misses 744 744 
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs release notesAutomatically applied to PRs which haven't added release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@d-v-b@TomAugspurger@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs: add spec-style description of the consolidated metadata format - #4283

Draft
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45
Draft

docs: add spec-style description of the consolidated metadata format#4283
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45

Conversation

@d-v-b

Copy link
Copy Markdown
Contributor

Summary

This is a claude-authored contribution that adds specification-style documentation for how zarr-python implements consolidated metadata. I'm pretty busy these days with a newborn baby so I can't give this careful review. I am opening this as a draft and leaving it to other folks to push it forward.

cc @normanrz

🤖 AI text below 🤖

Add a new user-guide page that describes exactly what zarr-python reads and writes for consolidated metadata in Zarr formats 2 and 3, so that other implementations can interoperate. Quotes and attributes the schema text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key ordering, the empty child-group marker, the v2 .zmetadata layout and its deviation from zarr-python 2.x, and the reader/writer procedures.

Also correct the 3.1.1 sort-order note on the existing page, which said "lexicographic" while the implementation uses NFKC-casefolded ordering.

Assisted-by: ClaudeCode:claude-fable-5

Author attestation

  • I am a human, these are my changes, and I have reviewed and understood every change and can explain why each is correct.

TODO

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/user-guide/*.md
  • Changes documented as a new file in changes/
  • GitHub Actions have all passed
  • Test coverage is 100% (Codecov passes)

Add a new user-guide page that describes exactly what zarr-python reads
and writes for consolidated metadata in Zarr formats 2 and 3, so that
other implementations can interoperate. Quotes and attributes the schema
text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key
ordering, the empty child-group marker, the v2 `.zmetadata` layout and
its deviation from zarr-python 2.x, and the reader/writer procedures.
Also correct the 3.1.1 sort-order note on the existing page, which said
"lexicographic" while the implementation uses NFKC-casefolded ordering.
Assisted-by: ClaudeCode:claude-fable-5
@github-actionsgithub-actionsBot added the needs release notes Automatically applied to PRs which haven't added release notes label Aug 25, 2026
@d-v-b

Copy link
Copy Markdown
ContributorAuthor

@TomAugspurger you should probably also have a look, since this is an LLM summarizing your work

@TomAugspurger

Copy link
Copy Markdown
Contributor

I'm pretty busy these days with a newborn baby so I can't give this careful review.

Congrats! I'll take a look when I get a chance.

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

@normanrz

Copy link
Copy Markdown
Member

I asked for a spec on Zulip and Davis was kind enough to create one. The motivation is that we picked up the consolidated metadata in the newly-formed Zarr Format Working Group and I wanted to get a better understanding of the current zarr-python implementation. Having a spec here would be great, but even better would be if we could eventually bring this document into the Zarr 3 spec.

@d-v-b

Copy link
Copy Markdown
ContributorAuthor

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another. For folks curious about the normative structure of consolidated metadata, but also zarr-python's particular implementation choices, I think some kind of document in our docs is a good play. Long term our goal should be to replace an actual spec with a link to a spec defined elsewhere.

@normanrz

Copy link
Copy Markdown
Member

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another.

Right. With @joshmoore, I am working on a proposal for the ZFWG governance and I could imagine using consolidated metadata as testcase for the governance process.

@TomAugspurgerTomAugspurger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context. With that in mind, I'll view this as a somewhat temporary document. Though just merging the zarr-specs PR feels infinitely better.

And for those interested in the implementation, rereading the discussion in #2113 might also be fruitful.

Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Consolidated metadata essentially stores all the metadata for a hierarchy in the
metadata of the root Group.

This page describes how to use consolidated metadata from Python. For a precise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some hesitancy about linking to this document from user-facing docs. I think the spec (or the PR for the spec) should be sufficient for users, and if it isn't then the docs should be improved there.

Comment on lines +91 to +93
of strings joined by `"/"`. For keys with the same depth, the tie is broken by
comparing the paths after Unicode NFKC normalization and case-folding. This
behavior ensures deterministic metadata output for a given group.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems worse for causal readers.

Let's at least keep "lexicographic" (since it isn't incorrect, right?) And if we want to be more specific we can, but let's link to the Python docs on normalization.


## Concepts

A hierarchy is **consolidated at** a group (the *consolidating group*). The

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"consolidating group" isn't a term in zarr-developers/zarr-specs#309. If we want to make it one, let's propose it there.

Comment on lines +66 to +69
(`GroupMetadata.consolidated_metadata.metadata`). It is never written to a
store and is mentioned here only because it leaks into the on-disk form in
one place (the [empty child marker](#child-groups-carry-an-empty-marker)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "leaks into" is accurate here, or it conveys the wrong impression. That's deliberate, saying that there aren't any children.


## Paths

A **path** is the name of a node relative to the consolidating group:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC paths are already defined in the spec. Link to that.

at `B` produces `C`, `y`; consolidating at `C` produces no paths (an empty
mapping, which is still written).

!!! note "Difference from the zarr-specs#309 text"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, I had a review pending from April 2025 pointing out this issue. Submitted that: zarr-developers/zarr-specs#309 (comment) and we can fix it in the spec.

Each value is a complete node metadata document, i.e. exactly what would be
found in that node's own `zarr.json`, with the following rules:

* The document MUST contain `zarr_format`. Readers discriminate on this first;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this isn't intended / well-tested.

Comment on lines +188 to +190
This is the one place the nested in-memory form shows through. The marker
does **not** mean the child group has no children: the child's descendants are
still listed in the flat mapping at the consolidating group. It exists so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong (or I'm misunderstanding something). But AFAIK the presence of metadata: {} definitely does indicate a group with no children.

d-v-band others added 2 commits August 30, 2026 22:22
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
@codecov

codecovBot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.21%. Comparing base (5b5f3a3) to head (c1bae56).

Additional details and impacted files
@@ Coverage Diff @@## main #4283 +/- ##
=======================================
Coverage 94.21% 94.21% =======================================
Files 92 92 Lines 12861 12861 =======================================
Hits 12117 12117 Misses 744 744 
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs release notesAutomatically applied to PRs which haven't added release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@d-v-b@TomAugspurger@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

docs: add spec-style description of the consolidated metadata format - #4283

Draft
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45
Draft

docs: add spec-style description of the consolidated metadata format#4283
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45

Conversation

@d-v-b

Copy link
Copy Markdown
Contributor

Summary

This is a claude-authored contribution that adds specification-style documentation for how zarr-python implements consolidated metadata. I'm pretty busy these days with a newborn baby so I can't give this careful review. I am opening this as a draft and leaving it to other folks to push it forward.

cc @normanrz

🤖 AI text below 🤖

Add a new user-guide page that describes exactly what zarr-python reads and writes for consolidated metadata in Zarr formats 2 and 3, so that other implementations can interoperate. Quotes and attributes the schema text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key ordering, the empty child-group marker, the v2 .zmetadata layout and its deviation from zarr-python 2.x, and the reader/writer procedures.

Also correct the 3.1.1 sort-order note on the existing page, which said "lexicographic" while the implementation uses NFKC-casefolded ordering.

Assisted-by: ClaudeCode:claude-fable-5

Author attestation

  • I am a human, these are my changes, and I have reviewed and understood every change and can explain why each is correct.

TODO

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/user-guide/*.md
  • Changes documented as a new file in changes/
  • GitHub Actions have all passed
  • Test coverage is 100% (Codecov passes)

Add a new user-guide page that describes exactly what zarr-python reads
and writes for consolidated metadata in Zarr formats 2 and 3, so that
other implementations can interoperate. Quotes and attributes the schema
text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key
ordering, the empty child-group marker, the v2 `.zmetadata` layout and
its deviation from zarr-python 2.x, and the reader/writer procedures.
Also correct the 3.1.1 sort-order note on the existing page, which said
"lexicographic" while the implementation uses NFKC-casefolded ordering.
Assisted-by: ClaudeCode:claude-fable-5
@github-actionsgithub-actionsBot added the needs release notes Automatically applied to PRs which haven't added release notes label Aug 25, 2026
@d-v-b

Copy link
Copy Markdown
ContributorAuthor

@TomAugspurger you should probably also have a look, since this is an LLM summarizing your work

@TomAugspurger

Copy link
Copy Markdown
Contributor

I'm pretty busy these days with a newborn baby so I can't give this careful review.

Congrats! I'll take a look when I get a chance.

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

@normanrz

Copy link
Copy Markdown
Member

I asked for a spec on Zulip and Davis was kind enough to create one. The motivation is that we picked up the consolidated metadata in the newly-formed Zarr Format Working Group and I wanted to get a better understanding of the current zarr-python implementation. Having a spec here would be great, but even better would be if we could eventually bring this document into the Zarr 3 spec.

@d-v-b

Copy link
Copy Markdown
ContributorAuthor

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another. For folks curious about the normative structure of consolidated metadata, but also zarr-python's particular implementation choices, I think some kind of document in our docs is a good play. Long term our goal should be to replace an actual spec with a link to a spec defined elsewhere.

@normanrz

Copy link
Copy Markdown
Member

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another.

Right. With @joshmoore, I am working on a proposal for the ZFWG governance and I could imagine using consolidated metadata as testcase for the governance process.

@TomAugspurgerTomAugspurger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context. With that in mind, I'll view this as a somewhat temporary document. Though just merging the zarr-specs PR feels infinitely better.

And for those interested in the implementation, rereading the discussion in #2113 might also be fruitful.

Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Consolidated metadata essentially stores all the metadata for a hierarchy in the
metadata of the root Group.

This page describes how to use consolidated metadata from Python. For a precise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some hesitancy about linking to this document from user-facing docs. I think the spec (or the PR for the spec) should be sufficient for users, and if it isn't then the docs should be improved there.

Comment on lines +91 to +93
of strings joined by `"/"`. For keys with the same depth, the tie is broken by
comparing the paths after Unicode NFKC normalization and case-folding. This
behavior ensures deterministic metadata output for a given group.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems worse for causal readers.

Let's at least keep "lexicographic" (since it isn't incorrect, right?) And if we want to be more specific we can, but let's link to the Python docs on normalization.


## Concepts

A hierarchy is **consolidated at** a group (the *consolidating group*). The

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"consolidating group" isn't a term in zarr-developers/zarr-specs#309. If we want to make it one, let's propose it there.

Comment on lines +66 to +69
(`GroupMetadata.consolidated_metadata.metadata`). It is never written to a
store and is mentioned here only because it leaks into the on-disk form in
one place (the [empty child marker](#child-groups-carry-an-empty-marker)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "leaks into" is accurate here, or it conveys the wrong impression. That's deliberate, saying that there aren't any children.


## Paths

A **path** is the name of a node relative to the consolidating group:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC paths are already defined in the spec. Link to that.

at `B` produces `C`, `y`; consolidating at `C` produces no paths (an empty
mapping, which is still written).

!!! note "Difference from the zarr-specs#309 text"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, I had a review pending from April 2025 pointing out this issue. Submitted that: zarr-developers/zarr-specs#309 (comment) and we can fix it in the spec.

Each value is a complete node metadata document, i.e. exactly what would be
found in that node's own `zarr.json`, with the following rules:

* The document MUST contain `zarr_format`. Readers discriminate on this first;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this isn't intended / well-tested.

Comment on lines +188 to +190
This is the one place the nested in-memory form shows through. The marker
does **not** mean the child group has no children: the child's descendants are
still listed in the flat mapping at the consolidating group. It exists so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong (or I'm misunderstanding something). But AFAIK the presence of metadata: {} definitely does indicate a group with no children.

d-v-band others added 2 commits August 30, 2026 22:22
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
@codecov

codecovBot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.21%. Comparing base (5b5f3a3) to head (c1bae56).

Additional details and impacted files
@@ Coverage Diff @@## main #4283 +/- ##
=======================================
Coverage 94.21% 94.21% =======================================
Files 92 92 Lines 12861 12861 =======================================
Hits 12117 12117 Misses 744 744 
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs release notesAutomatically applied to PRs which haven't added release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@d-v-b@TomAugspurger@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs: add spec-style description of the consolidated metadata format - #4283

Draft
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45
Draft

docs: add spec-style description of the consolidated metadata format#4283
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45

Conversation

@d-v-b

Copy link
Copy Markdown
Contributor

Summary

This is a claude-authored contribution that adds specification-style documentation for how zarr-python implements consolidated metadata. I'm pretty busy these days with a newborn baby so I can't give this careful review. I am opening this as a draft and leaving it to other folks to push it forward.

cc @normanrz

🤖 AI text below 🤖

Add a new user-guide page that describes exactly what zarr-python reads and writes for consolidated metadata in Zarr formats 2 and 3, so that other implementations can interoperate. Quotes and attributes the schema text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key ordering, the empty child-group marker, the v2 .zmetadata layout and its deviation from zarr-python 2.x, and the reader/writer procedures.

Also correct the 3.1.1 sort-order note on the existing page, which said "lexicographic" while the implementation uses NFKC-casefolded ordering.

Assisted-by: ClaudeCode:claude-fable-5

Author attestation

  • I am a human, these are my changes, and I have reviewed and understood every change and can explain why each is correct.

TODO

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/user-guide/*.md
  • Changes documented as a new file in changes/
  • GitHub Actions have all passed
  • Test coverage is 100% (Codecov passes)

Add a new user-guide page that describes exactly what zarr-python reads
and writes for consolidated metadata in Zarr formats 2 and 3, so that
other implementations can interoperate. Quotes and attributes the schema
text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key
ordering, the empty child-group marker, the v2 `.zmetadata` layout and
its deviation from zarr-python 2.x, and the reader/writer procedures.
Also correct the 3.1.1 sort-order note on the existing page, which said
"lexicographic" while the implementation uses NFKC-casefolded ordering.
Assisted-by: ClaudeCode:claude-fable-5
@github-actionsgithub-actionsBot added the needs release notes Automatically applied to PRs which haven't added release notes label Aug 25, 2026
@d-v-b

Copy link
Copy Markdown
ContributorAuthor

@TomAugspurger you should probably also have a look, since this is an LLM summarizing your work

@TomAugspurger

Copy link
Copy Markdown
Contributor

I'm pretty busy these days with a newborn baby so I can't give this careful review.

Congrats! I'll take a look when I get a chance.

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

@normanrz

Copy link
Copy Markdown
Member

I asked for a spec on Zulip and Davis was kind enough to create one. The motivation is that we picked up the consolidated metadata in the newly-formed Zarr Format Working Group and I wanted to get a better understanding of the current zarr-python implementation. Having a spec here would be great, but even better would be if we could eventually bring this document into the Zarr 3 spec.

@d-v-b

Copy link
Copy Markdown
ContributorAuthor

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another. For folks curious about the normative structure of consolidated metadata, but also zarr-python's particular implementation choices, I think some kind of document in our docs is a good play. Long term our goal should be to replace an actual spec with a link to a spec defined elsewhere.

@normanrz

Copy link
Copy Markdown
Member

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another.

Right. With @joshmoore, I am working on a proposal for the ZFWG governance and I could imagine using consolidated metadata as testcase for the governance process.

@TomAugspurgerTomAugspurger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context. With that in mind, I'll view this as a somewhat temporary document. Though just merging the zarr-specs PR feels infinitely better.

And for those interested in the implementation, rereading the discussion in #2113 might also be fruitful.

Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Consolidated metadata essentially stores all the metadata for a hierarchy in the
metadata of the root Group.

This page describes how to use consolidated metadata from Python. For a precise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some hesitancy about linking to this document from user-facing docs. I think the spec (or the PR for the spec) should be sufficient for users, and if it isn't then the docs should be improved there.

Comment on lines +91 to +93
of strings joined by `"/"`. For keys with the same depth, the tie is broken by
comparing the paths after Unicode NFKC normalization and case-folding. This
behavior ensures deterministic metadata output for a given group.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems worse for causal readers.

Let's at least keep "lexicographic" (since it isn't incorrect, right?) And if we want to be more specific we can, but let's link to the Python docs on normalization.


## Concepts

A hierarchy is **consolidated at** a group (the *consolidating group*). The

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"consolidating group" isn't a term in zarr-developers/zarr-specs#309. If we want to make it one, let's propose it there.

Comment on lines +66 to +69
(`GroupMetadata.consolidated_metadata.metadata`). It is never written to a
store and is mentioned here only because it leaks into the on-disk form in
one place (the [empty child marker](#child-groups-carry-an-empty-marker)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "leaks into" is accurate here, or it conveys the wrong impression. That's deliberate, saying that there aren't any children.


## Paths

A **path** is the name of a node relative to the consolidating group:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC paths are already defined in the spec. Link to that.

at `B` produces `C`, `y`; consolidating at `C` produces no paths (an empty
mapping, which is still written).

!!! note "Difference from the zarr-specs#309 text"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, I had a review pending from April 2025 pointing out this issue. Submitted that: zarr-developers/zarr-specs#309 (comment) and we can fix it in the spec.

Each value is a complete node metadata document, i.e. exactly what would be
found in that node's own `zarr.json`, with the following rules:

* The document MUST contain `zarr_format`. Readers discriminate on this first;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this isn't intended / well-tested.

Comment on lines +188 to +190
This is the one place the nested in-memory form shows through. The marker
does **not** mean the child group has no children: the child's descendants are
still listed in the flat mapping at the consolidating group. It exists so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong (or I'm misunderstanding something). But AFAIK the presence of metadata: {} definitely does indicate a group with no children.

d-v-band others added 2 commits August 30, 2026 22:22
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
@codecov

codecovBot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.21%. Comparing base (5b5f3a3) to head (c1bae56).

Additional details and impacted files
@@ Coverage Diff @@## main #4283 +/- ##
=======================================
Coverage 94.21% 94.21% =======================================
Files 92 92 Lines 12861 12861 =======================================
Hits 12117 12117 Misses 744 744 
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs release notesAutomatically applied to PRs which haven't added release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@d-v-b@TomAugspurger@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs: add spec-style description of the consolidated metadata format - #4283

Draft
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45
Draft

docs: add spec-style description of the consolidated metadata format#4283
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45

Conversation

@d-v-b

Copy link
Copy Markdown
Contributor

Summary

This is a claude-authored contribution that adds specification-style documentation for how zarr-python implements consolidated metadata. I'm pretty busy these days with a newborn baby so I can't give this careful review. I am opening this as a draft and leaving it to other folks to push it forward.

cc @normanrz

🤖 AI text below 🤖

Add a new user-guide page that describes exactly what zarr-python reads and writes for consolidated metadata in Zarr formats 2 and 3, so that other implementations can interoperate. Quotes and attributes the schema text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key ordering, the empty child-group marker, the v2 .zmetadata layout and its deviation from zarr-python 2.x, and the reader/writer procedures.

Also correct the 3.1.1 sort-order note on the existing page, which said "lexicographic" while the implementation uses NFKC-casefolded ordering.

Assisted-by: ClaudeCode:claude-fable-5

Author attestation

  • I am a human, these are my changes, and I have reviewed and understood every change and can explain why each is correct.

TODO

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/user-guide/*.md
  • Changes documented as a new file in changes/
  • GitHub Actions have all passed
  • Test coverage is 100% (Codecov passes)

Add a new user-guide page that describes exactly what zarr-python reads
and writes for consolidated metadata in Zarr formats 2 and 3, so that
other implementations can interoperate. Quotes and attributes the schema
text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key
ordering, the empty child-group marker, the v2 `.zmetadata` layout and
its deviation from zarr-python 2.x, and the reader/writer procedures.
Also correct the 3.1.1 sort-order note on the existing page, which said
"lexicographic" while the implementation uses NFKC-casefolded ordering.
Assisted-by: ClaudeCode:claude-fable-5
@github-actionsgithub-actionsBot added the needs release notes Automatically applied to PRs which haven't added release notes label Aug 25, 2026
@d-v-b

Copy link
Copy Markdown
ContributorAuthor

@TomAugspurger you should probably also have a look, since this is an LLM summarizing your work

@TomAugspurger

Copy link
Copy Markdown
Contributor

I'm pretty busy these days with a newborn baby so I can't give this careful review.

Congrats! I'll take a look when I get a chance.

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

@normanrz

Copy link
Copy Markdown
Member

I asked for a spec on Zulip and Davis was kind enough to create one. The motivation is that we picked up the consolidated metadata in the newly-formed Zarr Format Working Group and I wanted to get a better understanding of the current zarr-python implementation. Having a spec here would be great, but even better would be if we could eventually bring this document into the Zarr 3 spec.

@d-v-b

Copy link
Copy Markdown
ContributorAuthor

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another. For folks curious about the normative structure of consolidated metadata, but also zarr-python's particular implementation choices, I think some kind of document in our docs is a good play. Long term our goal should be to replace an actual spec with a link to a spec defined elsewhere.

@normanrz

Copy link
Copy Markdown
Member

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another.

Right. With @joshmoore, I am working on a proposal for the ZFWG governance and I could imagine using consolidated metadata as testcase for the governance process.

@TomAugspurgerTomAugspurger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context. With that in mind, I'll view this as a somewhat temporary document. Though just merging the zarr-specs PR feels infinitely better.

And for those interested in the implementation, rereading the discussion in #2113 might also be fruitful.

Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Consolidated metadata essentially stores all the metadata for a hierarchy in the
metadata of the root Group.

This page describes how to use consolidated metadata from Python. For a precise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some hesitancy about linking to this document from user-facing docs. I think the spec (or the PR for the spec) should be sufficient for users, and if it isn't then the docs should be improved there.

Comment on lines +91 to +93
of strings joined by `"/"`. For keys with the same depth, the tie is broken by
comparing the paths after Unicode NFKC normalization and case-folding. This
behavior ensures deterministic metadata output for a given group.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems worse for causal readers.

Let's at least keep "lexicographic" (since it isn't incorrect, right?) And if we want to be more specific we can, but let's link to the Python docs on normalization.


## Concepts

A hierarchy is **consolidated at** a group (the *consolidating group*). The

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"consolidating group" isn't a term in zarr-developers/zarr-specs#309. If we want to make it one, let's propose it there.

Comment on lines +66 to +69
(`GroupMetadata.consolidated_metadata.metadata`). It is never written to a
store and is mentioned here only because it leaks into the on-disk form in
one place (the [empty child marker](#child-groups-carry-an-empty-marker)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "leaks into" is accurate here, or it conveys the wrong impression. That's deliberate, saying that there aren't any children.


## Paths

A **path** is the name of a node relative to the consolidating group:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC paths are already defined in the spec. Link to that.

at `B` produces `C`, `y`; consolidating at `C` produces no paths (an empty
mapping, which is still written).

!!! note "Difference from the zarr-specs#309 text"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, I had a review pending from April 2025 pointing out this issue. Submitted that: zarr-developers/zarr-specs#309 (comment) and we can fix it in the spec.

Each value is a complete node metadata document, i.e. exactly what would be
found in that node's own `zarr.json`, with the following rules:

* The document MUST contain `zarr_format`. Readers discriminate on this first;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this isn't intended / well-tested.

Comment on lines +188 to +190
This is the one place the nested in-memory form shows through. The marker
does **not** mean the child group has no children: the child's descendants are
still listed in the flat mapping at the consolidating group. It exists so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong (or I'm misunderstanding something). But AFAIK the presence of metadata: {} definitely does indicate a group with no children.

d-v-band others added 2 commits August 30, 2026 22:22
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
@codecov

codecovBot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.21%. Comparing base (5b5f3a3) to head (c1bae56).

Additional details and impacted files
@@ Coverage Diff @@## main #4283 +/- ##
=======================================
Coverage 94.21% 94.21% =======================================
Files 92 92 Lines 12861 12861 =======================================
Hits 12117 12117 Misses 744 744 
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs release notesAutomatically applied to PRs which haven't added release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@d-v-b@TomAugspurger@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

docs: add spec-style description of the consolidated metadata format - #4283

Draft
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45
Draft

docs: add spec-style description of the consolidated metadata format#4283
d-v-b wants to merge 6 commits into
zarr-developers:mainfrom
d-v-b:claude/docs-consolidated-metadata-spec-6f3c45

Conversation

@d-v-b

Copy link
Copy Markdown
Contributor

Summary

This is a claude-authored contribution that adds specification-style documentation for how zarr-python implements consolidated metadata. I'm pretty busy these days with a newborn baby so I can't give this careful review. I am opening this as a draft and leaving it to other folks to push it forward.

cc @normanrz

🤖 AI text below 🤖

Add a new user-guide page that describes exactly what zarr-python reads and writes for consolidated metadata in Zarr formats 2 and 3, so that other implementations can interoperate. Quotes and attributes the schema text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key ordering, the empty child-group marker, the v2 .zmetadata layout and its deviation from zarr-python 2.x, and the reader/writer procedures.

Also correct the 3.1.1 sort-order note on the existing page, which said "lexicographic" while the implementation uses NFKC-casefolded ordering.

Assisted-by: ClaudeCode:claude-fable-5

Author attestation

  • I am a human, these are my changes, and I have reviewed and understood every change and can explain why each is correct.

TODO

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/user-guide/*.md
  • Changes documented as a new file in changes/
  • GitHub Actions have all passed
  • Test coverage is 100% (Codecov passes)

Add a new user-guide page that describes exactly what zarr-python reads
and writes for consolidated metadata in Zarr formats 2 and 3, so that
other implementations can interoperate. Quotes and attributes the schema
text from zarr-specs#309 (Tom Augspurger, CC-BY-4.0), documents the key
ordering, the empty child-group marker, the v2 `.zmetadata` layout and
its deviation from zarr-python 2.x, and the reader/writer procedures.
Also correct the 3.1.1 sort-order note on the existing page, which said
"lexicographic" while the implementation uses NFKC-casefolded ordering.
Assisted-by: ClaudeCode:claude-fable-5
@github-actionsgithub-actionsBot added the needs release notes Automatically applied to PRs which haven't added release notes label Aug 25, 2026
@d-v-b

Copy link
Copy Markdown
ContributorAuthor

@TomAugspurger you should probably also have a look, since this is an LLM summarizing your work

@TomAugspurger

Copy link
Copy Markdown
Contributor

I'm pretty busy these days with a newborn baby so I can't give this careful review.

Congrats! I'll take a look when I get a chance.

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

@normanrz

Copy link
Copy Markdown
Member

I asked for a spec on Zulip and Davis was kind enough to create one. The motivation is that we picked up the consolidated metadata in the newly-formed Zarr Format Working Group and I wanted to get a better understanding of the current zarr-python implementation. Having a spec here would be great, but even better would be if we could eventually bring this document into the Zarr 3 spec.

@d-v-b

Copy link
Copy Markdown
ContributorAuthor

Do you have any more info on the motivation for this document. I gather it's primarily for other implementations looking to interoperate with what we write, and am trying to balance this approach vs. telling them to read the spec + source code :)

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another. For folks curious about the normative structure of consolidated metadata, but also zarr-python's particular implementation choices, I think some kind of document in our docs is a good play. Long term our goal should be to replace an actual spec with a link to a spec defined elsewhere.

@normanrz

Copy link
Copy Markdown
Member

IMO we don't really have a place for a consolidated metadata spec in the zarr-specs repo until we unblock the blockage that prevented your original PR from getting merged in some form or another.

Right. With @joshmoore, I am working on a proposal for the ZFWG governance and I could imagine using consolidated metadata as testcase for the governance process.

@TomAugspurgerTomAugspurger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context. With that in mind, I'll view this as a somewhat temporary document. Though just merging the zarr-specs PR feels infinitely better.

And for those interested in the implementation, rereading the discussion in #2113 might also be fruitful.

Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Comment threaddocs/user-guide/consolidated_metadata_format.md Outdated
Consolidated metadata essentially stores all the metadata for a hierarchy in the
metadata of the root Group.

This page describes how to use consolidated metadata from Python. For a precise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some hesitancy about linking to this document from user-facing docs. I think the spec (or the PR for the spec) should be sufficient for users, and if it isn't then the docs should be improved there.

Comment on lines +91 to +93
of strings joined by `"/"`. For keys with the same depth, the tie is broken by
comparing the paths after Unicode NFKC normalization and case-folding. This
behavior ensures deterministic metadata output for a given group.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems worse for causal readers.

Let's at least keep "lexicographic" (since it isn't incorrect, right?) And if we want to be more specific we can, but let's link to the Python docs on normalization.


## Concepts

A hierarchy is **consolidated at** a group (the *consolidating group*). The

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"consolidating group" isn't a term in zarr-developers/zarr-specs#309. If we want to make it one, let's propose it there.

Comment on lines +66 to +69
(`GroupMetadata.consolidated_metadata.metadata`). It is never written to a
store and is mentioned here only because it leaks into the on-disk form in
one place (the [empty child marker](#child-groups-carry-an-empty-marker)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "leaks into" is accurate here, or it conveys the wrong impression. That's deliberate, saying that there aren't any children.


## Paths

A **path** is the name of a node relative to the consolidating group:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC paths are already defined in the spec. Link to that.

at `B` produces `C`, `y`; consolidating at `C` produces no paths (an empty
mapping, which is still written).

!!! note "Difference from the zarr-specs#309 text"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, I had a review pending from April 2025 pointing out this issue. Submitted that: zarr-developers/zarr-specs#309 (comment) and we can fix it in the spec.

Each value is a complete node metadata document, i.e. exactly what would be
found in that node's own `zarr.json`, with the following rules:

* The document MUST contain `zarr_format`. Readers discriminate on this first;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this isn't intended / well-tested.

Comment on lines +188 to +190
This is the one place the nested in-memory form shows through. The marker
does **not** mean the child group has no children: the child's descendants are
still listed in the flat mapping at the consolidating group. It exists so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong (or I'm misunderstanding something). But AFAIK the presence of metadata: {} definitely does indicate a group with no children.

d-v-band others added 2 commits August 30, 2026 22:22
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
Co-authored-by: Tom Augspurger <tom.augspurger88@gmail.com>
@codecov

codecovBot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.21%. Comparing base (5b5f3a3) to head (c1bae56).

Additional details and impacted files
@@ Coverage Diff @@## main #4283 +/- ##
=======================================
Coverage 94.21% 94.21% =======================================
Files 92 92 Lines 12861 12861 =======================================
Hits 12117 12117 Misses 744 744 
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs release notesAutomatically applied to PRs which haven't added release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@d-v-b@TomAugspurger@normanrz