conda / conda/ceps

CEP 37: loosen `custom_metadata` from `dict[str, str]`

Open
#163 3 comments 1 reaction 0 assignees View on GitHub
Dominant language
No language data
Stars
34
Forks
41
Avg merge
1h 46m
Merged PRs (30d)
1

Description

CEP 37 specifies `metadata.custom_metadata` as `dict[str, str]`, free-form key-value string pairs. The current conda-lock implementation enforces this via a `StrictModel`.

The string constraint is fine for flat annotations (a git SHA, a build ID, a commit URL), but it breaks down as soon as anyone wants to stash a structured payload there.

Concrete example: in conda-lockfiles (https://github.com/conda-incubator/conda-lockfiles/pull/132) I'm recording user-requested MatchSpec strings so that exported lockfiles carry user intent, not just the pinned solve. That's a `list[str]`. The spec forces me to JSON-encode it and embed it as a single string:

```yaml
custom_metadata:
created_by: conda-lockfiles 0.1.x
requested_specs: '["python_abi", "python >=3.11"]'
```

versus what the YAML actually wants to be:

```yaml
custom_metadata:
created_by: conda-lockfiles 0.1.x
requested_specs:
- python_abi
- python >=3.11
```

The JSON-string form is less diffable, less greppable, and hides structure from anyone casually reading the lockfile. It also means every consumer has to know which keys are JSON-encoded and which aren't.

## What I'd like to see

Widen the type to something like `dict[str, str | int | bool | list | dict]` or `dict[str, Any]` (effectively "any YAML-scalar-or-container value"). That keeps the intent (free-form user metadata) and removes the string wrapper workaround.

Lockfile writers that don't want to generate structured values can continue to write flat strings and nothing changes for them. Consumers need to handle more value types, but arguably they already need to: conda-lock's `LockMeta.__or__` merges `custom_metadata` dicts today, which would produce surprising results with JSON-string collision anyway.

## Why now

CEP 37 is accepted but very recent (March 2026) and conda-lock's implementation is the only one that meaningfully enforces it. Catching this before a second implementation bakes in the same constraint seems worthwhile.

## Alternatives considered

- **Invent a sibling block.** CEP 37 already lists `git_metadata`, `inputs_metadata`, `time_metadata` as structured cousins of `custom_metadata`. I could propose a new structured block per use case, but that explodes the spec and misses the point: `custom_metadata` is the escape hatch, it should actually work as one.
- **Keep JSON-strings as the convention.** Works, ships today, ugly forever.

## Prior art

- conda-lock `LockMeta.custom_metadata` type: https://github.com/conda/conda-lock/blob/main/conda_lock/lockfile/v1/models.py
- CEP 37 text: https://github.com/conda/ceps/blob/main/cep-0037.md#custom_metadata
- Downstream use case: https://github.com/conda-incubator/conda-lockfiles/pull/132

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.