# Bump Pipelex

> Moves this repo's `pipelex` dependency pin to the latest release on PyPI (or a version you name), re-locks, migrates `.pipelex/` config if the schema moved, runs the checks, and writes the CHANGELOG entry — stopping before the commit. Use whenever the user says "bump pipelex", "update pipelex", "upgrade pipelex", "pin pipelex to 0.48.0", "get us on the latest pipelex", "what's the latest pipelex", "is our pipelex out of date", "move off the git pin", or asks to build against a newly published pipelex release. Also use when a failure looks like a pipelex version mismatch — a `TypeError` about a missing keyword-only argument from the runtime, an `extra="forbid"` rejection, or a `StorageConfigError` at boot. This is about the **pipelex dependency**, not about releasing pipelex-api itself; releasing this package is the `release` skill.

- Skill: `pipelex/bump-pipelex` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add pipelex/bump-pipelex`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pipelex/bump-pipelex/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: pipelex (https://skillmd.com/u/pipelex)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/pipelex/bump-pipelex

---


# Bump the pipelex dependency

This server is a thin FastAPI wrapper over the `pipelex` library, pinned to one
exact version. Bumping that pin is routine but not mechanical: pipelex is pre-1.0
and ships breaking changes without a deprecation window, and some of its models
are on this server's wire, so a bump can legitimately move the committed OpenAPI
artifact and the on-disk config format at the same time.

The job is to land the new pin in a state a human can read and commit: pin moved,
lock regenerated, config migrated, checks green, changelog written. **Stop before
committing** — the user stages and commits.

## What "bumping pipelex" touches

| File | Why it moves |
|---|---|
| `pyproject.toml` | The pin itself, in `[project].dependencies` — `pipelex[<extras>]==X.Y.Z` |
| `pyproject.toml` | A `[tool.uv.sources]` block, **if** one is pinning pipelex to a git rev — that has to go |
| `uv.lock` | Regenerated by `make li` |
| `docs/openapi/pipelex-api.openapi.yaml` | Only if pipelex models that reach the wire changed shape |
| `.pipelex/*.toml` | Only if the config schema narrowed — `pipelex migrate` handles it |
| `CHANGELOG.md` | An entry under `## [Unreleased]` saying what moved and what it costs a caller |

Do **not** touch `[project].version`. That is this package's own version and it
moves only at release time, via the `release` skill. Conflating the two is the
single most common way this task goes wrong.

## Workflow

### 1. Orient

Read the current pin — the line in `pyproject.toml` matching `^  "pipelex\[`.
Read it from the file, never from the virtualenv: `.venv` routinely lags the pin
(someone edited `pyproject.toml` without re-running `make li`), so the installed
version answers a different question than the one being asked.

Check `git status`. Uncommitted work in the tree is normal here — the user often
has concurrent work in the same worktree. Note what was already dirty **before**
you start, so the diff you report at the end separates your changes from theirs
and so you never stage something that isn't yours.

Also check whether `[tool.uv.sources]` names pipelex. If it does, this repo is
currently building against an unreleased git commit, and moving to PyPI means
removing that block — see step 3.

### 2. Resolve the target version

If the user named a version, use it. Otherwise ask PyPI:

```bash
curl -s https://pypi.org/pypi/pipelex/json \
  | python3 -c "import json,sys; print(json.load(sys.stdin)['info']['version'])"
```

`info.version` is the latest non-prerelease, which is what "latest" should mean
here — a server that other people deploy has no business riding an rc by
accident. If the user explicitly wants a prerelease, list the candidates from
`releases` in that same JSON and let them pick.

If the resolved version equals the current pin, say so and stop. There is nothing
to do, and manufacturing a no-op lock churn is worse than reporting "already
current". Mention what the installed venv has if it disagrees, since that is a
real thing to fix with `make li` even when the pin is right.

### 3. Move the pin

Edit the version in place and **keep the extras exactly as they are**:

```
"pipelex[mistralai,anthropic,google,google-genai,bedrock,fal]==0.48.0",
```

The extras list is load-bearing and easy to lose when retyping the line. Each one
pulls an inference provider's SDK; extras never travel through a transitive
dependency, so dropping one produces an image that imports fine and then fails at
the first request that reaches that provider. Change the digits and nothing else —
substituting only the version substring cannot drop an extra, whereas rewriting
the line can.

So make it a substring edit: on that line, replace `==0.47.0"` with `==0.48.0"`
and leave every other character alone. The pinned version string appears once in
the file, so the match is unambiguous. Use your editor rather than a shell
one-liner — `sed -i` takes a separate empty argument on macOS and an attached
suffix on GNU/Linux, so no single invocation is portable, and the `release` skill
edits this same file the same way.

Confirm the result with `grep '"pipelex\[' pyproject.toml` before moving on.

**If `[tool.uv.sources]` pins pipelex to a git rev, delete the whole block**
(along with the comment explaining it). This matters more than it looks: in uv, a
*source* wins over a version specifier. Leaving it means the `==0.48.0` you just
wrote is decorative — uv keeps resolving the git commit, `make li` succeeds, the
checks pass, and you report a bump that did not happen. Those blocks are written
as temporary and are meant to be stripped the moment the work lands on PyPI;
they usually carry a comment saying exactly that.

### 4. Re-lock and install

```bash
make li
```

That is `uv lock` + `uv sync`, so it both rewrites `uv.lock` and puts the new
pipelex in `.venv`, which everything downstream depends on.

If it reports the requirements are unsatisfiable, the usual cause is that the
version is not on PyPI yet — publication and the announcement do not always land
together. Check with the PyPI query from step 2, and if it genuinely isn't
published, stop and tell the user rather than inventing a git pin: adding one is a
deliberate decision with a cost (see step 3), not a workaround to apply silently.

### 5. Migrate the on-disk config

```bash
.venv/bin/pipelex migrate --dry-run
```

Pipelex validates `.pipelex/*.toml` against the current schema **at boot**, and
config narrowings are enforced loudly rather than silently rewritten. Because the
test fixtures boot pipelex, stale config does not fail one test — it errors every
test in the suite, which reads like a code catastrophe and is nothing of the
kind. Running the dry run before the tests means you interpret that correctly if
it happens.

If the dry run reports changes, apply them with `.venv/bin/pipelex migrate --yes`
and read the resulting diff. The command writes a `.bak.<timestamp>` copy beside
each file it touches; those backups are local scratch, so don't offer them for
commit.

Note the scope: `migrate` covers **this machine's** pipelex config, so it reads
`~/.pipelex` as well as the repo's `.pipelex/`. That is usually what you want —
the user's own config would break at boot too — but it means the command can
change files outside the repo. If the dry run lists anything under the home
directory, say so when you report, since those changes are invisible in
`git status` and the user cannot review them in the diff.

Migration handles fields the schema can prove are stale. It cannot handle a
narrowing of the *values* a free-form string accepts — the `uri_format`
placeholder set is the recurring example. If boot raises a config error the
migration didn't fix, the error names the offending key and lists what is now
supported; fix it by hand and note it in the changelog, because anyone running
this server against their own `.pipelex/` has to make the same edit.

### 6. Run the checks

```bash
make agent-check
make agent-test
make openapi-check
```

`make openapi-check` is a separate call on purpose: `agent-check` does not include
it (only the full `make check` does), and it is precisely the gate a pipelex bump
is most likely to trip. Pipelex models are on this server's wire — the run
result, the graph spec, the validation error vocabulary — so an upstream schema
change reaches `docs/openapi/pipelex-api.openapi.yaml` even though no code here
changed.

If it reports drift, regenerate and inspect:

```bash
make openapi-export
git diff docs/openapi/pipelex-api.openapi.yaml
```

Read that diff rather than accepting it — it is the clearest available statement
of what the bump changed for API consumers, and anything surprising in it (a field
disappearing, a type loosening to `string`) belongs in the changelog and possibly
in a conversation with the user before this lands.

Failures in `agent-check` or `agent-test` after a pipelex bump are usually the
breaking change announcing itself, not incidental breakage. Read the upstream
notes from step 7 before patching around them: the fix is normally to adopt the
new API, and a bump whose test failures were papered over is worse than no bump.

### 7. Digest the upstream changes

Get the release notes for every version **after** the old pin, up to and including
the new one. The helper does the boundary arithmetic:

```bash
.venv/bin/python .claude/skills/bump-pipelex/scripts/upstream_notes.py 0.47.0 0.48.0
```

It reads `../pipelex/CHANGELOG.md` from the workspace checkout. Two things it
protects you from, both of which have produced wrong changelog entries before:

- **It skips `## [Unreleased]`.** That section describes work that is *not* in the
  version you just pinned. Attributing it to the release is a plain factual error
  in a changelog people read to decide whether to upgrade.
- **It tells you when the checkout is too old.** The sibling repo is a working
  checkout that may predate the version you are pinning. If the target heading
  isn't there, the script says so and you should fall back to the published
  notes:

```bash
gh release view v0.48.0 --repo Pipelex/pipelex
```

If the sibling checkout is absent entirely, go straight to `gh`.

### 8. Write the changelog entry

Add to `CHANGELOG.md` under `## [Unreleased]`, creating that heading right after
`# Changelog` if it isn't there. Work in progress accumulates under `[Unreleased]`
until a release cuts it into a version heading — do not add a `## [vX.Y.Z]`
heading yourself, and do not bump the package version.

The entry goes under `### Changed` and leads with the pin. What makes it useful is
the second half: what an operator or a caller has to *do*. Write it for someone
deciding whether this upgrade will break them.

```markdown
## [Unreleased]

### Changed

- **Pinned `pipelex` 0.48.0.** Up from `==0.47.0`, exactly. <One or two sentences
  on why this version — the capability it brings or the fix it carries.>

  <If anything here breaks a caller or an operator, say so plainly and say what
  they must change. If the OpenAPI artifact moved, name the fields that moved.
  If `.pipelex/` config needed migrating, say that and name the setting.>
```

Match the surrounding entries' register: complete sentences, the reasoning stated,
no bare bullet lists of commit subjects. If the bump is genuinely uneventful —
a patch release with no wire, config, or behaviour change reaching this server —
then say that in one sentence and stop. Padding a quiet bump with upstream detail
that doesn't affect this repo makes the loud ones harder to spot.

Where a change is breaking, write "breaking" rather than "pre-1.0 breaking", per
the workspace convention.

### 9. Report and stop

Show the user:

- The version move, old → new.
- Every file you changed, and whether `uv.lock`, the OpenAPI artifact, or
  `.pipelex/` config moved.
- The check results, honestly — if `agent-test` failed, say so with the output
  rather than reporting a bump as done.
- Anything from the upstream notes that needs a human decision.
- Whether the bump is **breaking**, because that is a cross-repo signal. The
  hosted plane pins `pipelex` at several sites and consumes `pipelex-api` by git
  tag, so a breaking bump here means someone has to move `pipelex-server` too.
  That is not yours to change from this repo — mention it, and if the user wants
  it tracked, the workspace convention is a note in `../wip/inbox/`.

Then stop. Do not commit, branch, or push unless the user asks.

If they do ask you to commit, **stage the files explicitly by path** — never
`git add -A`. Concurrent uncommitted work in this worktree is common, and a
blanket add sweeps someone's in-flight work into a bump commit. Stage
`pyproject.toml`, `uv.lock`, `CHANGELOG.md`, and the OpenAPI artifact and
`.pipelex/` files only if they actually moved. A working branch here is
`chore/<Topic>`, and PRs target `dev`.

## Traps worth remembering

- **A `[tool.uv.sources]` git pin silently outranks the version specifier.** The
  bump looks successful and changes nothing. Always check for it, always remove it
  when returning to PyPI.
- **Extras are not transitive.** Losing one from the pin line produces a server
  that fails only on the request that needs that provider.
- **`make agent-check` does not run `openapi-check`.** Call it yourself; it's the
  gate this task trips most.
- **Every test erroring at once usually means stale `.pipelex/` config**, not
  broken code. Run `pipelex migrate --dry-run` before you start debugging.
- **`## [Unreleased]` upstream is not in the version you pinned.** Never quote it
  as part of the release.
- **`[project].version` is this package's version, not pipelex's.** Bumping it
  here is out of scope; that's the `release` skill.

