Sync Model
pyra sync is the central command in Pyra. Every dependency operation — add, remove, update, run — ultimately flows through the same sync pipeline.
The pipeline
Section titled “The pipeline”pyproject.toml → selection → freshness check → resolve → lock → reconcileFive steps, always in this order:
- Read project inputs from
pyproject.toml - Decide the selected dependency set from defaults and CLI flags
- Check lock freshness against current inputs
- Resolve and rewrite
pylock.tomlif stale or missing - Reconcile the centralized environment exactly from the lock
Step 1: Project inputs
Section titled “Step 1: Project inputs”Sync reads:
[project].name— required[project].dependencies— base deps[project.optional-dependencies]— extras[dependency-groups]— groups, with include-group expansion[tool.pyra].python— required pinned interpreter[project].requires-python— optional constraint check[build-system]— determines editable project installation
Sync fails early if the project does not pin a Python version. If requires-python is present, the pinned interpreter must satisfy it.
Step 2: Selection
Section titled “Step 2: Selection”The selected dependency set determines what gets installed. Default:
- Base dependencies (always)
devgroup (if it exists)- No extras
This can be overridden with --group, --extra, --all-groups, --all-extras, --no-group, --no-dev, --only-group, and --only-dev flags. See Dependency Selection for full rules.
Step 3: Lock freshness
Section titled “Step 3: Lock freshness”pylock.toml is considered fresh when all of the following match current state:
- Normalized dependency input fingerprint
- Selected interpreter version
- Selected lock target set
- Index URL
- Resolution strategy identifier
Freshness means “lock matches current inputs.” It does not mean “latest upstream.” A lock stays fresh until project or resolution inputs change.
Step 4: Resolution
Section titled “Step 4: Resolution”When the lock is stale or missing, Pyra runs fresh resolution:
- One selected interpreter
- One target platform (current host by default)
- One union of base dependencies, all groups, and all extras
- PubGrub-based solver behind Pyra-specific abstractions
- Metadata from PyPI Simple Repository API
The result is written to pylock.toml.
Current strategy identifier: environment-scoped-union-v1
Step 5: Reconciliation
Section titled “Step 5: Reconciliation”Installation is driven from pylock.toml, not from re-resolution. The installer:
- Selects lock entries matching the current dependency groups and extras
- Narrows multi-target artifacts to the current host
- Installs missing or changed packages
- Removes packages not in the selected lock subset
- Installs the current project editable when
[build-system]is present
Reconciliation is exact by default: the environment contains precisely what the selected lock subset specifies.
Reproducibility modes
Section titled “Reproducibility modes”Default (pyra sync)
Section titled “Default (pyra sync)”Resolves when lock is stale or missing. Writes the updated lock. Reconciles the environment.
--locked
Section titled “--locked”Requires an existing fresh pylock.toml. Never resolves. Never rewrites. Fails if the lock is missing or stale.
Use for CI to verify that committed lock files match project inputs.
--frozen
Section titled “--frozen”Same guarantees as --locked. Requires an existing fresh pylock.toml. Never resolves or rewrites.
Idempotency
Section titled “Idempotency”Sync is idempotent when the lock is fresh and the environment is already reconciled. Running pyra sync twice with no changes is a no-op.
Why sync is central
Section titled “Why sync is central”Every mutation command flows through sync:
pyra addeditspyproject.toml, then syncspyra removeeditspyproject.toml, then syncspyra runensures sync before executionpyra updatewrites a fresh lock, but does not bypass the lock/install split
This keeps one pipeline instead of multiple competing installation paths. The lock is always the source of truth for what gets installed.