Latest Results
chore(deps): Bump the dependencies group across 1 directory with 3 updates (#875)
Bumps the dependencies group with 3 updates in the / directory:
[rusqlite](https://github.com/rusqlite/rusqlite),
[sha2](https://github.com/RustCrypto/hashes) and
[tokio](https://github.com/tokio-rs/tokio).
Updates `rusqlite` from 0.37.0 to 0.39.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rusqlite/rusqlite/releases">rusqlite's
releases</a>.</em></p>
<blockquote>
<h2>0.39.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix constraints on VTab Aux data <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1778">#1778</a>,
<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1771">#1771</a></li>
<li>Fix docs.rs generation <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1779">#1779</a></li>
<li>Fix a small typo in <code>rollback_hook</code> docstring <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1780">#1780</a></li>
<li>Fix some warnings from Intellij <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1781">#1781</a></li>
<li>Minimal doc for features <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1783">#1783</a></li>
<li>Clear hooks only for owning connections <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1785">#1785</a>,
<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1784">#1784</a></li>
<li>Fix link to SQLite C Interface, Prepare Flags <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1787">#1787</a></li>
<li>Comment functions which are not usable from a loadable extension <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1789">#1789</a></li>
<li>Factorize code <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1792">#1792</a></li>
<li>Update getrandom to 0.4 <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1798">#1798</a></li>
<li>Update Cargo.toml <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1800">#1800</a></li>
<li>Fix appveyor <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1807">#1807</a></li>
<li>Add support to unix timestamp for chrono, jiff and time <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1808">#1808</a>,
<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1803">#1803</a></li>
<li>fix(trace): check that the sql string pointer is not NULL <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1805">#1805</a></li>
<li>Bump bundled SQLite version to 3.51.3 <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1818">#1818</a></li>
<li>Use TryFrom<!-- raw HTML omitted --> for Value <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1819">#1819</a>,
<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1817">#1817</a></li>
<li>Make possible to pass your own pointers <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1626">#1626</a>,
<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1602">#1602</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/rusqlite/rusqlite/compare/v0.38.0...v0.39.0">https://github.com/rusqlite/rusqlite/compare/v0.38.0...v0.39.0</a></p>
<h2>0.38.0</h2>
<h2>What's Changed</h2>
<ul>
<li>bump sqlcipher to 4.10.0 (sqlite 3.50.4) <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1725">#1725</a></li>
<li>Use CARGO_CFG_TARGET_FEATURE for crt-static check <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1737">#1737</a></li>
<li>Disable u64, usize ToSql/FromSql impl by default <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1732">#1732</a>,
#<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1722">#1722</a>
(breaking change)</li>
<li>Make statement cache optional <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1682">#1682</a>,
<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1173">#1173</a>
(breaking change)</li>
<li>Remove shell scripts from the published package <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1744">#1744</a></li>
<li>Use new interfaces with 64-bit length parameters <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1749">#1749</a></li>
<li>sqlite3_vtab_rhs_value <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1753">#1753</a></li>
<li>Handle VTab IN values <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1754">#1754</a></li>
<li>Give access to Connection from VTabCursor::column <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1755">#1755</a></li>
<li>Bump minimal SQLite version to 3.34.1 <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1733">#1733</a>,
<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1731">#1731</a>
(breaking change)</li>
<li>Bump bundled SQLite version to 3.51.1 <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1758">#1758</a></li>
<li>Add support for transaction to the vtab module <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1761">#1761</a></li>
<li>Check Connection is owned when registering Closure as hook <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1764">#1764</a>
(breaking change)</li>
<li>Turn libsqlite3-sys in a !#[no_std] crate <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1767">#1767</a></li>
<li>Add <code>wasm32-unknown-unknown</code> support <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1769">#1769</a>,
<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/488">#488</a>,
<a
href="https://redirect.github.com/rusqlite/rusqlite/issues/827">#827</a></li>
<li>Remove useless Send/Sync on Module <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1774">#1774</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/rusqlite/rusqlite/compare/v0.37.0...v0.38.0">https://github.com/rusqlite/rusqlite/compare/v0.37.0...v0.38.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/2a1790a69107cd03dae85d501dcbdb11c5b32ef3"><code>2a1790a</code></a>
Merge pull request <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1820">#1820</a>
from gwenn/0.39.0</li>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/7c43afcb74fe973d22f74e9e4821a20f4fa5e94b"><code>7c43afc</code></a>
Prepare next release</li>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/487af3c2d335f6ac1409a12fd283a7b4650ed106"><code>487af3c</code></a>
Merge pull request <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1626">#1626</a>
from gwenn/ptr</li>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/643d581b67f91d236e63555cfd89efc8f2452a9e"><code>643d581</code></a>
Warn about potential memory leak</li>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/67f59c39f5199a7b4cfdb33bee90b8bf2ea36792"><code>67f59c3</code></a>
Merge pull request <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1819">#1819</a>
from gwenn/utf8error</li>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/870d5b6b3cb119b8378fc730da18fec1f69f0612"><code>870d5b6</code></a>
Use TryFrom<ValueRef> for Value</li>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/93085d8ac3fc5375ce17f1b01c22d229485abd51"><code>93085d8</code></a>
Merge pull request <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1818">#1818</a>
from gwenn/3.51.3</li>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/7bd509863f304a40ba6be1c1e3ad70a221d50490"><code>7bd5098</code></a>
Bump bundled SQLite version to 3.51.3</li>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/886832ed8416d5831158a2c6caaaf891ef8a00b4"><code>886832e</code></a>
Merge pull request <a
href="https://redirect.github.com/rusqlite/rusqlite/issues/1816">#1816</a>
from mqudsi/undo-3.52.0</li>
<li><a
href="https://github.com/rusqlite/rusqlite/commit/ca911a29bb1b229cb697346cfae6351240aeb589"><code>ca911a2</code></a>
Revert "Bump bundled SQLite version to 3.52.0"</li>
<li>Additional commits viewable in <a
href="https://github.com/rusqlite/rusqlite/compare/v0.37.0...v0.39.0">compare
view</a></li>
</ul>
</details>
<br />
Updates `sha2` from 0.10.9 to 0.11.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/RustCrypto/hashes/commit/ffe093984c004769747e998f77da8ff7c0e7a765"><code>ffe0939</code></a>
Release sha2 0.11.0 (<a
href="https://redirect.github.com/RustCrypto/hashes/issues/806">#806</a>)</li>
<li><a
href="https://github.com/RustCrypto/hashes/commit/8991b65fe400c31c4cc189510f86ae642c470cd9"><code>8991b65</code></a>
Use the standard order of the <code>[package]</code> section fields (<a
href="https://redirect.github.com/RustCrypto/hashes/issues/807">#807</a>)</li>
<li><a
href="https://github.com/RustCrypto/hashes/commit/3d2bc57db40fd6aeb25d6c6da98d67e2784c2985"><code>3d2bc57</code></a>
sha2: refactor backends (<a
href="https://redirect.github.com/RustCrypto/hashes/issues/802">#802</a>)</li>
<li><a
href="https://github.com/RustCrypto/hashes/commit/faa55fb83697c8f3113636d88070e5f5edc8c335"><code>faa55fb</code></a>
sha3: bump <code>keccak</code> to v0.2 (<a
href="https://redirect.github.com/RustCrypto/hashes/issues/803">#803</a>)</li>
<li><a
href="https://github.com/RustCrypto/hashes/commit/d3e6489e56f8486d4a93ceb7a8abf4924af1de7b"><code>d3e6489</code></a>
sha3 v0.11.0-rc.9 (<a
href="https://redirect.github.com/RustCrypto/hashes/issues/801">#801</a>)</li>
<li><a
href="https://github.com/RustCrypto/hashes/commit/bbf6f51ff97f81ab15e6e5f6cf878bfbcb1f47c8"><code>bbf6f51</code></a>
sha2: tweak backend docs (<a
href="https://redirect.github.com/RustCrypto/hashes/issues/800">#800</a>)</li>
<li><a
href="https://github.com/RustCrypto/hashes/commit/155dbbf2959dbec0ec75948a82590ddaede2d3bc"><code>155dbbf</code></a>
sha3: add default value for the <code>DS</code> generic parameter on
<code>TurboShake128/256</code>...</li>
<li><a
href="https://github.com/RustCrypto/hashes/commit/ed514f2b34526683b3b7c41670f1887982c3df64"><code>ed514f2</code></a>
Use published version of <code>keccak</code> v0.2 (<a
href="https://redirect.github.com/RustCrypto/hashes/issues/799">#799</a>)</li>
<li><a
href="https://github.com/RustCrypto/hashes/commit/702bcd83735a49c928c0fc24506924f5c0aa22af"><code>702bcd8</code></a>
Migrate to closure-based <code>keccak</code> (<a
href="https://redirect.github.com/RustCrypto/hashes/issues/796">#796</a>)</li>
<li><a
href="https://github.com/RustCrypto/hashes/commit/827c043f82d57666a0b146d156e91c39535c1305"><code>827c043</code></a>
sha3 v0.11.0-rc.8 (<a
href="https://redirect.github.com/RustCrypto/hashes/issues/794">#794</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/RustCrypto/hashes/compare/sha2-v0.10.9...sha2-v0.11.0">compare
view</a></li>
</ul>
</details>
<br />
Updates `tokio` from 1.52.1 to 1.52.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tokio-rs/tokio/releases">tokio's
releases</a>.</em></p>
<blockquote>
<h2>Tokio v1.52.3</h2>
<h1>1.52.3 (May 8th, 2026)</h1>
<h3>Fixed</h3>
<ul>
<li>sync: fix underflow in mpsc channel <code>len()</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8062">#8062</a>)</li>
<li>sync: notify receivers in mpsc <code>OwnedPermit::release()</code>
method (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8075">#8075</a>)</li>
<li>sync: require that an <code>RwLock</code> has <code>max_readers !=
0</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8076">#8076</a>)</li>
<li>sync: return <code>Empty</code> from <code>try_recv()</code> when
mpsc is closed with outstanding permits (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8074">#8074</a>)</li>
</ul>
<p><a
href="https://redirect.github.com/tokio-rs/tokio/issues/8062">#8062</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/8062">tokio-rs/tokio#8062</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8074">#8074</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/8074">tokio-rs/tokio#8074</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8075">#8075</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/8075">tokio-rs/tokio#8075</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8076">#8076</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/8076">tokio-rs/tokio#8076</a></p>
<h2>Tokio v1.52.2</h2>
<h1>1.52.2 (May 4th, 2026)</h1>
<p>This release reverts the LIFO slot stealing change introduced in
1.51.0 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7431">#7431</a>),
due to [its performance impact]<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8065">#8065</a>.
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8100">#8100</a>)</p>
<p><a
href="https://redirect.github.com/tokio-rs/tokio/issues/7431">#7431</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7431">tokio-rs/tokio#7431</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8065">#8065</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/8065">tokio-rs/tokio#8065</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8100">#8100</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/8100">tokio-rs/tokio#8100</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/tokio-rs/tokio/commit/d87569164fb61145e79e7ffe0b25783569cc8f93"><code>d875691</code></a>
chore: prepare Tokio v1.52.3 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8130">#8130</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/e1aebb031cb24bdb52289561343308f4a44a4d81"><code>e1aebb0</code></a>
Merge 'tokio-1.51.3' into 'tokio-1.52.x' (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8129">#8129</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/fd63094ee0d34b4f3f93f59507e91c65919a2d71"><code>fd63094</code></a>
chore: prepare Tokio v1.51.3 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8127">#8127</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/8c600d0fd2cdebea4828fe9f699ced4dfd8aad3b"><code>8c600d0</code></a>
Merge 'tokio-1.47.5' into 'tokio-1.51.x' (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8123">#8123</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/11bfc1345bbd5e901187e2b3702de10b0efbffdc"><code>11bfc13</code></a>
chore: prepare Tokio v1.47.5 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8122">#8122</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/f085b6211b8ebb6aba21f1f1f91e7b8b243aa815"><code>f085b62</code></a>
sync: notify receivers in mpsc <code>OwnedPermit::release()</code>
method (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8075">#8075</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/30d25ccb8bc91ca811773ee243e71e31772275d2"><code>30d25cc</code></a>
sync: require that an <code>RwLock</code> has <code>max_readers !=
0</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8076">#8076</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/9fccf5339d41c1f2f863f97b9133bc8a5a10bc28"><code>9fccf53</code></a>
sync: return <code>Empty</code> from <code>try_recv()</code> when mpsc
is closed with outstanding p...</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/ebf61b45b5184018f00bc666887ebccf3d4fe51b"><code>ebf61b4</code></a>
sync: fix underflow in mpsc channel <code>len()</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8062">#8062</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/4abe9d732eb01f7b092a571c3dcc4fbd266f4067"><code>4abe9d7</code></a>
chore: prepare Tokio v1.52.2 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8115">#8115</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-1.52.1...tokio-1.52.3">compare
view</a></li>
</ul>
</details>
<br />
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> v3 Step H: replace ocipkg::ImageName with ommx::artifact::ImageRef (#874)
## Summary
- Introduces `ommx::artifact::ImageRef`, the OMMX-owned parsed form of
an OCI image reference, and migrates every public surface
(`LocalArtifact`, `LocalArtifactBuilder`, SQLite Local Registry helpers,
CLI parse, PyO3 bindings, examples, dataset packaging) off
`ocipkg::ImageName`.
- Drops `ocipkg` from `Cargo.toml` (workspace + `rust/ommx` +
`python/ommx`) and the `ocipkg::*` re-export from the top-level `ommx`
crate. The `remote-artifact` feature no longer activates
`ocipkg/remote`.
- Rewrites the local-registry tests off `ocipkg`'s `OciDirBuilder` /
`OciArtifact` to a native fixture that writes `oci-layout` +
`index.json` + `blobs/sha256/...` directly, and verifies the save
round-trip via the public `inspect_archive` plus a native tar walk.
`ImageRef` keeps the same `host[:port]/name:reference` parsing rules and
exposes `hostname()` / `port()` / `name()` / `reference()`, plus the
v2-cache-compatible `as_path()` / `from_path()`. Field access
(`image_name.hostname`) becomes a method call (`image_name.hostname()`).
This is the final phase of ARTIFACT_V3.md §12.4 — Step H lands, and the
workspace is `ocipkg`-free.
## Test plan
- [x] `cargo check --workspace --all-targets`
- [x] `cargo clippy --workspace --all-targets -- -D warnings`
- [x] `task rust:test` — 538 unit + 9 ignored network-only + 44 doctests
pass
- [x] `task python:test` — full ommx + adapter suites green (excluding
`ommx-jijmodeling-adapter` which is unaffected)
- [x] `task python:stubgen` — auto-generated stubs unchanged after the
rebuild
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> v3 ocipkg removal Steps D+F: native remote transport + native tar I/O (#872)
## Summary
Lands Steps **D** and **F** of the §12.4 ocipkg-removal roadmap, and
adopts the "archive is purely an exchange format" model from Codex's
review feedback. After this PR the SDK no longer references the
ocipkg `OciArchive` / `OciArchiveBuilder` / `OciDir` / `OciDirBuilder`
/ `OciArtifact` / `Image` / `ImageBuilder` types or the ocipkg
`Remote` / `RemoteBuilder` / `auth_from_env` surface — only
`ocipkg::ImageName` remains, which Step H replaces with an OMMX-owned
`ImageRef`.
### Step D — native remote transport for Archive / pull paths
`Artifact<OciArchive>::push` now uses the v3 `RemoteTransport`
(oci-client) wrapper introduced in Step B. `pull_image` writes blobs
straight into `FileBlobStore` and publishes the manifest + ref in a
single SQLite transaction — no temp OCI Image Layout staging.
`Artifact<OciDir>::push`, `Artifact<Remote>`, `Artifact::from_remote`,
and the standalone `auth_from_env` helper are removed; dataset
loaders (`miplib2017`, `qplib`), the CLI Inspect / Push paths, and
`examples/pull_artifact.rs` are migrated to `pull_image` +
`LocalArtifact`. CLI `Inspect <remote-ref>` keeps the manifest-only
fetch via a new `fetch_remote_manifest` helper (no SQLite write).
`RemoteTransport` grows `auth_for(op)`, `pull_manifest_raw`, and
`pull_blob_to_vec`. The pull-blob path goes through
`pull_blob_stream` so `FileBlobStore::put_bytes` is the single
digest-verification point. `pull_blob_to_vec` takes the manifest-
declared `expected_size` and caps `Vec::with_capacity` at it; both
the registry's `Content-Length` and the accumulated chunk size are
rejected if they exceed the declared size — a malicious / broken
registry cannot force a runaway allocation before any digest check
would catch the body.
`pull_image` bails on `Conflicted` rather than returning it as
`Ok(...)`: every caller (Python `Artifact.load`, CLI `ommx pull`,
dataset loaders) treats a successful return as "the pulled bytes are
now resident", so the silent fall-through was unsafe under tag-race
/ registry-mutation.
### Step F — native tar I/O + archive becomes a pure exchange format
`LocalArtifact::save` is a direct `tar` crate writer producing a
strict OCI Image Layout archive (`oci-layout` + `index.json` +
`blobs/<algorithm>/<encoded>`). Manifest bytes are written verbatim
so the archive manifest digest is byte-identical to the source
SQLite digest. `tar::HeaderMode::Deterministic` keeps output
reproducible. The output is opened with `OpenOptions::create_new(true)`
to close the TOCTOU window between an `exists()` probe and the open.
`import::archive::import_oci_archive` is a native single-pass tar
reader: blobs land directly in `FileBlobStore` during the scan,
`index.json` + `oci-layout` are captured for the post-pass parse,
and one `publish_artifact_atomic` transaction commits the manifest +
ref. Archives missing the optional `oci-layout` marker (v2-era
output / older oras / crane) are accepted with a warning.
**Archive load semantics shift**: v3 treats `.ommx` archives as a
pure exchange format. There is no "open archive in place" path — the
ocipkg-typed `Artifact<OciArchive>` and the intermediate `OmmxArchive`
struct are both gone, along with `Artifact<Base: Image>` /
`Artifact<OciDir>`. Workflow:
- **Loading from an archive**: `Artifact.load_archive(file)` imports
the archive into the user's persistent SQLite Local Registry and
returns a `LocalArtifact` handle. Subsequent
`Artifact.load(image_name)` calls resolve from SQLite without
re-importing.
- **Inspecting an archive**: CLI `ommx inspect <archive>` reads the
manifest via a new native tar pre-scan
(`local_registry::read_archive_manifest`) without any registry
side effect.
- **Pushing an archive**: CLI `ommx push <archive>` bails with a
migration hint pointing at `ommx load <file>` then
`ommx push <image_name>`. v3 pushes always source from the SQLite
Local Registry.
- **Building an archive**: `ArchiveArtifactBuilder::build()` builds
into the user's persistent SQLite Local Registry **and** saves the
archive file out; the temp-registry / drop-guard pattern is gone.
Returns `LocalArtifact` directly. `new_archive_unnamed` is
removed — v3 requires every artifact to be addressable by ref.
Python's `ArtifactInner` enum collapses to a single struct backed by
`LocalArtifact`.
CLI `Inspect <oci-dir>` likewise reads the manifest blob directly
from disk (no SQLite mutation for a read-only op).
## Test plan
- [x] `task rust:test` — 522 unit + 44 doctest + 9 ignored (auth_e2e
via testcontainers)
- [x] `task python:test` — full Python suite + doctests + linters
- [x] `cargo clippy -p ommx --all-targets --features
remote-artifact,cli,built -- -D warnings`
- [x] `cargo fmt --all -- --check`
- [x] `cargo build -p ommx --examples --features
remote-artifact,cli,built`
- [ ] CI auth_e2e (`--include-ignored auth_e2e`) — anonymous push,
docker-config auth, env override, env-beats-docker,
load-then-push from archive, pull round-trip
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Drop legacy disk OCI dir cache from v3 import / save / load paths (#871)
## Summary
Implements §12.3 **Step C** from `ARTIFACT_V3.md` and lays out the
remaining ocipkg-removal roadmap in §12.4. After this PR, the v3
SQLite Local Registry no longer maintains the v2-era on-disk OCI
Image Layout cache — SQLite + `FileBlobStore` are the sole
post-import home of every v3 artifact.
### Step C (§12.3) — drop the legacy disk OCI dir cache
* `import::archive::import_oci_archive` and `import::remote::pull_image`
stage into a `tempfile::TempDir` under the registry root and drop
it on return. No more promotion to
`registry.root().join(image_name)`.
* `pull_image` short-circuits the network fetch when SQLite already
resolves the image ref **and** the manifest blob is present in
`FileBlobStore`. A SQLite ref with a missing blob (registry
corruption, interrupted import) falls through to a fresh pull with
a `tracing::warn!` rather than handing back a stale `Unchanged`
that would later surface as an opaque `get_blob` failure.
* New `LocalArtifact::save(output)` streams `.ommx` archives directly
from SQLite via `OciArchiveBuilder::add_blob`, asserting each
rehash matches the recorded manifest digest.
* CLI `Push` / `Inspect` / `Save` route the `Local(name)` branch
through `LocalArtifact::open`, which surfaces the
`ommx artifact import` migration hint on SQLite miss. Two helpers
share that hint: `bail_not_found_locally` always bails (used by
`Push`); `migration_hint_if_legacy_only` returns `Ok(())` when no
legacy dir exists, letting `Inspect` keep its remote fallback for
genuinely-remote refs while still bailing locally for pre-v3
artifacts.
* Python `ArtifactInner` collapses to `{Archive, Local}` (the v2
`OciDir` reader variant is gone). `Artifact.load_archive(dir_path)`
imports the OCI Image Layout into the user's default SQLite Local
Registry and returns a `Local` handle; the docstring now spells
out that side effect and points users at the archive path
alternative for transient reads. The file case is unchanged.
`BuilderInner::Dir` renamed to `Local` (it always wrapped
`LocalArtifactBuilder`).
`Artifact<OciArchive>::push` is left on the legacy ocipkg path; the
unification with the new transport is **Step D** in §12.4 below.
### §12.4 — ocipkg removal milestone series (D → F → H)
The remaining ocipkg surface is broken into three independent
PR-sized steps:
* **D** — extend the Step B `oci-client` transport to
`Artifact<OciArchive>::push` and replace `Artifact<Remote>::pull_to`
with a native streaming pull straight to `FileBlobStore`. Drops
ocipkg `Remote` / `RemoteBuilder` and `auth_from_env`.
* **F** — native tar reader / writer for `.ommx` archives, dropping
`OciArchive` / `OciDir` / `OciArtifact` / `Image` / `ImageBuilder`.
`Artifact<Base: Image>` collapses to a single struct.
* **H** — replace `ocipkg::ImageName` in public APIs with an
`ommx::artifact::ImageRef` new type wrapping `oci_client::Reference`.
Breaking change; must land before v3.0 stable.
D and F are independent (network vs local format); H follows both.
The doc calls out that D should not extend `FileBlobStore`'s write
API beyond Step B's surface to avoid forcing F to redesign mid-PR.
## Pre-v3 user migration
Users with only the v2 path-tree layout under `registry.root()` now
hit the `ommx artifact import` migration hint instead of either an
opaque "image not found" or a silent remote fetch. The existing
`ommx artifact import` subcommand performs the identity-preserving
migration into SQLite.
## Test plan
- [x] `cargo test -p ommx --features remote-artifact,cli`
— 521 unit tests + 44 doctests passing
- [x] Three new Step-C invariant tests:
- `import_oci_archive_does_not_leave_legacy_dir_behind`
- `pull_image_short_circuits_when_ref_is_present_with_blob`
- `pull_image_does_not_short_circuit_when_manifest_blob_is_missing`
- [x] `cargo build --bin ommx --features remote-artifact,cli,built`
- [x] `cargo fmt --check` clean; `cargo clippy` no new warnings on
changed files
- [x] `task python:test` (full Python suite + adapters) passing
- [x] `cargo test -p ommx --features remote-artifact,built,cli --test
auth_e2e`
remains green (no semantic change to auth flow)
- [ ] Smoke: `ommx load <archive>` → `ommx inspect <ref>` →
`ommx save <ref> <out>` round-trip on a fresh tempdir registry
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Native LocalArtifact push via oci-client transport (#867)
## Summary
- Replace the transitional `LocalArtifact` push path that materialised a
legacy on-disk OCI directory before delegating to `ocipkg` with a direct
SQLite + CAS → remote registry stream.
- Bring in
[`oci-client`](https://github.com/oras-project/rust-oci-client) (ORAS
project, the actively-maintained successor to `oci-distribution`) as the
OCI Distribution transport. `ocipkg` remains in place for the archive
build path and the legacy import staging area, and will be revisited in
Step C.
- Add a new `artifact::remote_transport` module that owns a private
current-thread `tokio` runtime and exposes a sync API to the rest of the
SDK. Later milestones can expand the async boundary outward without
rewriting callers, and ultimately surface it to Python via
`pyo3-async-runtimes`.
- Add a three-tier credential resolver (env override → docker config +
helpers → anonymous) so an existing `docker login` / `gcloud auth
configure-docker` / `aws ecr get-login-password` session is reused
without copying credentials into env vars. OMMX does not own a
credential store; the lookup is delegated to the
[`docker_credential`](https://github.com/keirlawson/docker_credential)
crate (MIT/Apache-2.0).
- Route `ommx push <image>` CLI through the same `LocalArtifact::push()`
path as the Python API, so a CLI user sees the same behaviour (including
#606's GCAR fix) as a Python user. `ImageNameOrPath::parse` now prefers
the SQLite Local Registry over the legacy disk OCI dir.
- End-to-end auth coverage via `testcontainers`: 8 scenarios in
`tests/auth_e2e.rs` validate every tier of the credential resolver
(anonymous, docker config alone, env override, env precedence,
wrong-credential rejection, partial-override bail) plus the CLI
dispatch, all against real ephemeral `registry:2` containers.
## Design notes
- **Manifest bytes round-trip exactly.** The SQLite-resident manifest
bytes are pushed verbatim via `oci_client::Client::push_manifest_raw`,
so the registry-side digest agrees byte-for-byte with the local manifest
digest. The typed `OciManifest` round-trip would risk perturbing field
order and optional-field rendering (`annotations: {}`, `subject`), which
would change the digest.
- **`oci-spec` versions coexist.** ommx (and `ocipkg`) pull `oci-spec
0.8.4`; `oci-client` brings `oci-spec 0.9.0`. The transport wrapper only
converts at the boundary by stringifying `ocipkg::ImageName` and parsing
into `oci_client::Reference`, so the two type universes never meet.
- **Credentials.** `oci-client` only accepts pre-resolved
`RegistryAuth::{Anonymous, Basic, Bearer}`, so the SDK has to
materialise one of those for every push. The resolver tries
`OMMX_BASIC_AUTH_*` first (explicit CI override), then
`~/.docker/config.json` / `$DOCKER_CONFIG` (with credential helpers
invoked transparently by `docker_credential`), then anonymous.
Credential lookup is keyed on `host[:port]` (matching `docker login
localhost:5000`), `DockerCredential::IdentityToken` maps to
`RegistryAuth::Bearer`, and a partial env-var override (DOMAIN set but a
credential variable missing) errors loudly rather than silently falling
through. Helper failures are non-fatal: warn via tracing without the
helper's stdout/stderr (to avoid leaking tokens from a malformed-JSON
failure), then fall through.
- **CLI parity.** The CLI's `ImageNameOrPath::Local` previously routed
to `Artifact::from_oci_dir(get_image_dir(&name)).push()` (ocipkg). It
now resolves through `LocalArtifact::try_open(name)`; if SQLite has the
image, `LocalArtifact::push()` runs. The legacy disk OCI dir is kept as
a fallback so pre-v3 user state remains addressable until Step C removes
it.
- **Auth e2e tests.** `rust/ommx/tests/auth_e2e.rs` brings up its own
`registry:2` containers via `testcontainers` (anonymous + htpasswd
flavours), populates the credentials the SDK is supposed to discover,
and pushes a real artifact. All tests are `#[ignore = "requires
docker"]` + `#[serial]`; `cargo test` skips them by default and CI runs
them with `--include-ignored --test-threads=1`. An `EnvGuard` wipes
every auth env var on entry/exit so failing tests can't leak state.
- Removed `LocalArtifact::registry_root()` (added in #864 only to
support the transitional Python push fallback that this PR deletes).
## Scope (per ARTIFACT_V3.md §12.3 Step B)
- `LocalArtifact::push()` is native. Per PR #869 (Step B', merged), the
SQLite Local Registry stores OCI Image Manifest with `artifactType`
only, so the push path is uniform: empty config blob + each entry in
`layers[]` + manifest bytes with
`application/vnd.oci.image.manifest.v1+json` Content-Type.
- `Artifact<OciArchive>::push` and `Artifact<OciDir>::push` stay on
`ocipkg`. Migrating those is part of Step C.
- `ommx push <image>` (CLI) and `Artifact.push()` (Python) both use the
native path for SQLite-resident images.
- `Inspect` for `ImageNameOrPath::Local` is updated to read from SQLite
first (then fall back to legacy disk dir); `Save` still uses the legacy
dir directly (read path, Step C scope).
- Pull paths are unchanged; `local_registry::import::remote` still goes
through `ocipkg` for stage 1.
## Related
- Issue Jij-Inc/ommx#606 (GCAR push fails inside
`ocipkg::distribution::auth::AuthChallenge::try_from` JSON parse).
Expected to be addressed by switching transport away from `ocipkg`, but
**not auto-closed** — needs to be verified against the reporter's Google
Cloud Artifact Registry scenario before closing. The new docker-config
credential path should also resolve the `gcloud auth configure-docker`
flow end-to-end.
## Test plan
- [x] `cargo test -p ommx --features remote-artifact` — 518 unit tests
(+9 credential resolver / port-key tests), 44 integration / doctests
pass.
- [x] `cargo test -p ommx --features remote-artifact,built,cli --test
auth_e2e -- --include-ignored --test-threads=1` — 8 e2e scenarios pass
against ephemeral `registry:2` containers (anonymous + htpasswd).
- [x] `cargo build --bin ommx --features remote-artifact,built,cli` —
CLI binary builds clean.
- [x] `task python:test` on the package — passes.
- [x] `cargo clippy -p ommx --features remote-artifact,built,cli
--all-targets` — no new warnings on `push.rs` / `remote_transport.rs` /
`manifest.rs` / `bin/ommx.rs` / `tests/auth_e2e.rs`.
- [ ] Smoke-test push to Google Cloud Artifact Registry
(Jij-Inc/ommx#606 verification) before closing the issue.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Latest Branches
N/A
N/A
claude/stoic-nobel-76577c N/A
copilot/fix-cd351290-4957-4ccd-92ca-cb64db14baf1 © 2026 CodSpeed Technology