ADR-0023: Canonical single pylock with optional target projections¶
- Status: proposed
- Date: 2026-04-16
- Deciders: @Aksem
- Tags: packaging, lockfiles, reproducibility, environments
Context¶
FineCode uses lock files to make environment preparation reproducible.
Historically, projects tended to keep multiple target-specific lock files, often encoded in file names (platform, Python version, architecture). That pattern is practical with current tooling, but it duplicates data and makes consistency harder: one logical dependency update can require synchronized changes across many files.
PEP 751 (pylock.toml) can represent multi-use lock data in one file through
environment markers and multiple artifact entries. This enables one canonical lock
file to describe the complete target matrix.
The remaining challenge is tool interoperability. Some installers and pipelines fully support this model, while others are still maturing. FineCode therefore needs a decision that separates:
- canonical lock data model (architecture)
- compatibility mechanics for tools that cannot consume the canonical model directly
Related ADRs Considered¶
- ADR-0016 — different focus (action invocation scopes, not lockfile data model).
- ADR-0018 — related input surface (environment declaration), but not lockfile storage strategy.
- ADR-0022 — different focus (multi-env execution orchestration, not lockfile representation).
Decision¶
FineCode adopts a single canonical lock file per source artifact, with optional derived files for compatibility.
The decision has five rules:
- Canonical source of truth
- The canonical lock is
pylock.toml(or a namedpylock.<name>.tomlwhen required by scope), following PEP 751 naming rules. -
Canonical lock content should encode the full supported target matrix using PEP 751 semantics (
environments,packages.marker, per-artifact hashes/metadata). -
One canonical lock spans all dependency groups
- FineCode dependency groups (for example
dev_workspace,dev,dev_no_runtime,docs,wal_explorer) share the same canonical lock source. - This rule holds even when groups are always installed into separate virtualenvs; installation topology (many envs) does not imply multiple canonical lock sources.
-
Group-specific installation is achieved by filtering canonical lock data by dependency group and environment markers for the target env.
-
Installation prefers canonical consumption
-
If the selected installer can consume the canonical lock directly, FineCode installs from that file without transformation.
-
Projection for compatibility
- If a selected installer cannot consume the canonical lock directly, FineCode may generate a target-specific projected lock file from the canonical lock before install.
-
Projection is a filtering/selection step only; it must not perform dependency resolution.
-
Optional merge+validate component
- If the selected locker cannot emit the complete canonical lock in one run, FineCode may generate multiple target locks and merge them into the canonical file.
-
This merge step must validate determinism and ambiguity constraints (for a target, package selection must resolve to one unambiguous install set).
-
Target-specific file naming is for derived artifacts, not canonical truth
- Naming that encodes platform/interpreter/architecture is allowed for derived projection files (for example in
locks/), but those files are derivative outputs, not the authoritative lock source.
Consequences¶
- Pros
- One authoritative lock view reduces drift and review overhead.
- FineCode can still interoperate with tools that are not fully aligned with canonical multi-use lock consumption.
-
The architecture decouples lock data semantics from installer maturity.
-
Cons
- FineCode must own projection and (optionally) merge+validate logic.
-
Validation rules become part of FineCode's compatibility contract.
-
Operational implications
- CI should validate canonical lock correctness for the supported target matrix.
- Derived lock files can be regenerated and should be treated as disposable artifacts unless a workflow explicitly requires committing them.