From 5a7c4dc1fb79d9c401ff29a27bd9114de2311a67 Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Sun, 9 Aug 2026 15:40:59 +0200 Subject: [PATCH 1/2] =?UTF-8?q?docs:=20CLAUDE.md=20=E2=80=94=20correct=20t?= =?UTF-8?q?he=20workspace=20test=20invocations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `cargo test --workspace` no longer fails to compile; it passes, 296 tests over 10 binaries. The documented failure moved rather than disappeared: `cargo test -p pg-core` alone still hits `unresolved import crate::test`, and what rescues the workspace build is cryptify's dev-dependency asking for pg-core with the `test` feature, which unification then applies to every member. That has only held since cryptify joined on 2026-07-31, and it reverts if that dev-dep line changes. Adds the invocation that does fail and was undocumented: `cargo test --workspace --all-features` cannot compile on any host, because it enables pg-core's wasm32-only `web` feature. Per-crate `--all-features` is unaffected and stays correct. --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3c4c51c..5a4ea89 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Migrated from the dobby memory repo (`encryption4all/dobby`). This file is the h ## Workspace & CI -- Workspace members: `pg-core` (lib), `pg-ffi` (C ABI), `pg-pkg` (PKG service), `pg-cli`, `cryptify` (file-transfer service, merged in 2026-07-31). `pg-wasm` is a sibling crate the root `Cargo.toml` lists under `exclude`, so it is not part of the workspace and is built separately with wasm-pack (see Release & configuration). Sub-crates share workspace files. Build the workspace from repo root with `cargo build`. A bare `cargo test --workspace` FAILS to compile: `pg-core`'s tests are gated behind its `test` feature (also `rust`/`stream`), so the item is configured out and imports like `crate::test::TestSetup` don't resolve. CI (`.github/workflows/build.yml`) runs tests per crate: `cargo test --manifest-path pg-core/Cargo.toml --features test,rust,stream` for core, `--all-targets` for `cryptify` (it declares no features of its own, so that is the equivalent invocation), `--all-features` for `pkg`/`cli`/`ffi`. None of these cover `pg-wasm`. The `test`/`format`/`clippy` matrices key on the crate **directory** rather than a `pg-` suffix, because `cryptify` doesn't carry that prefix — so the check contexts are `Test workspace (pg-core)`, not `Test workspace (core)`. Nothing required points at them today, but whatever adds to the required list later must use the current names. Where that list lives is not obvious and an agent cannot read it: required checks come from **classic branch protection** (`gh api repos/encryption4all/postguard/branches/main/protection`, currently exactly `Wire compat`), *not* from the `main` ruleset, which carries only `deletion`/`non_fast_forward`/`pull_request` and no `required_status_checks` rule at all. `dobby-coder` gets 403 on the protection endpoint, so reading the ruleset alone concludes nothing is required, which is wrong — ask a maintainer rather than inferring. `pg-core` uses CGWKV + MKEM for multi-recipient encryption (production feature set `["cgwkv", "mkem"]`). [#262](https://github.com/encryption4all/postguard/issues/262) (merged `0343369`, #295) replaced the two per-language `Wire compat (published pg-core)` / `Wire compat (published pg-wasm/pg-js)` required checks with this single `wire-compat` job (`needs: [wire-compat-rust, wire-compat-js]`, `permissions: contents: read`); the two old names are no longer required and the underlying per-language jobs still exist and still run, just aren't required individually anymore. +- Workspace members: `pg-core` (lib), `pg-ffi` (C ABI), `pg-pkg` (PKG service), `pg-cli`, `cryptify` (file-transfer service, merged in 2026-07-31). `pg-wasm` is a sibling crate the root `Cargo.toml` lists under `exclude`, so it is not part of the workspace and is built separately with wasm-pack (see Release & configuration). Sub-crates share workspace files. Build the workspace from repo root with `cargo build`. Two test invocations are worth knowing, and the obvious reading of each is wrong. **`cargo test --workspace` passes** — 296 tests over 10 binaries, verified 2026-08-09 — but only by accident, so don't read it as the workspace being self-consistent. `pg-core`'s tests really are gated behind its `test` feature (also `rust`/`stream`), and `cargo test -p pg-core` alone still fails exactly as it always did, with `unresolved import crate::test` on `crate::test::TestSetup`. What rescues the workspace build is feature unification: `cryptify`'s dev-dependency asks for `pg-core` with `features = ["rust", "stream", "test"]` (`cryptify/Cargo.toml`), so the feature is on for every member — confirm with `cargo tree -e features -p pg-core --workspace`. That has only been true since `cryptify` joined the workspace on 2026-07-31; it is a side effect of one member's dev-deps rather than a property of the workspace, and it reverts to the old compile failure the moment that line changes. **`cargo test --workspace --all-features` does not compile at all**, on any host: it switches on `pg-core`'s `web`, whose `compile_error!` fires off wasm32 — `error: "web" feature should only be enabled on wasm32 targets`, `pg-core/src/client/web/mod.rs:19`. `--all-features` is correct *per crate* (it is what the CI matrices use for `pkg`/`cli`/`ffi`, and what the api_gate suite further down is run with) and wrong workspace-wide. CI (`.github/workflows/build.yml`) runs tests per crate: `cargo test --manifest-path pg-core/Cargo.toml --features test,rust,stream` for core, `--all-targets` for `cryptify` (it declares no features of its own, so that is the equivalent invocation), `--all-features` for `pkg`/`cli`/`ffi`. None of these cover `pg-wasm`. The `test`/`format`/`clippy` matrices key on the crate **directory** rather than a `pg-` suffix, because `cryptify` doesn't carry that prefix — so the check contexts are `Test workspace (pg-core)`, not `Test workspace (core)`. Nothing required points at them today, but whatever adds to the required list later must use the current names. Where that list lives is not obvious and an agent cannot read it: required checks come from **classic branch protection** (`gh api repos/encryption4all/postguard/branches/main/protection`, currently exactly `Wire compat`), *not* from the `main` ruleset, which carries only `deletion`/`non_fast_forward`/`pull_request` and no `required_status_checks` rule at all. `dobby-coder` gets 403 on the protection endpoint, so reading the ruleset alone concludes nothing is required, which is wrong — ask a maintainer rather than inferring. `pg-core` uses CGWKV + MKEM for multi-recipient encryption (production feature set `["cgwkv", "mkem"]`). [#262](https://github.com/encryption4all/postguard/issues/262) (merged `0343369`, #295) replaced the two per-language `Wire compat (published pg-core)` / `Wire compat (published pg-wasm/pg-js)` required checks with this single `wire-compat` job (`needs: [wire-compat-rust, wire-compat-js]`, `permissions: contents: read`); the two old names are no longer required and the underlying per-language jobs still exist and still run, just aren't required individually anymore. - `cryptify` was a separate repo until `ba380a1` (#277) merged it as a workspace member, history-preserving, the deliberate yes decided in #255/#285. What that bought: one lockfile, which closes a real drift class — the two trees previously built `pg-core`'s container serializer against two different `bincode-next` versions with nothing comparing them. What it costs is a **standing dependency coupling, and it does not go away**: `pg-pkg` reaches `libsqlite3-sys` through `sqlx`, `cryptify` reaches it through `rusqlite`, and it is `libsqlite3-sys` itself that declares `links = "sqlite3"` (`rusqlite`, `sqlx` and `sqlx-sqlite` all declare none — check with `cargo metadata`, not by guessing which crate in the chain owns it). Only one package per resolve graph may claim a given `links` value, and optional dependencies sit in the graph whether their feature is enabled or not — so `sqlx-sqlite` is locked even though `pg-pkg` enables only `postgres`. The newest intersecting pair is **sqlx 0.9 + rusqlite 0.39** (`libsqlite3-sys 0.37` in the root lockfile), so every `rusqlite` bump waits on sqlx's `libsqlite3-sys` ceiling — and that block is live today, not merely future: `rusqlite` 0.40.0 wants `libsqlite3-sys ^0.38.0` and 0.40.1 wants `^0.38.1`, both published, neither yanked, and both outside `sqlx-sqlite` 0.9.0's `>=0.30.1, <0.38.0`, so a lone rusqlite bump does not resolve at all. Don't bump either half alone; check the intersection first. - `pg-compat` is a second excluded sibling crate (root `Cargo.toml` `exclude`), and the exclusion is load-bearing: it depends on `pg-core` from **crates.io** (`=0.6.1`), not on `../pg-core`, so it can open bytes sealed by this tree with published readers. It has its own `Cargo.lock` (run it with `--locked`). Its input comes from `cargo run -p pg-core --features stream --example seal-samples -- `, a deterministic sealer whose output layout is documented in `pg-compat/README.md`. CI wires the two together: `wire-compat-rust` in `build.yml` seals with HEAD and opens with published pg-core on any PR touching the wire surface (pg-core/pg-wasm/pg-compat/pg-compat-js trees, the ROOT `Cargo.lock`/`Cargo.toml` — pg-core resolves from the root lockfile — and build.yml itself); `pg-compat-lint` covers the crate's fmt/clippy, which the per-crate matrices don't. The `=0.6.1` pin is currently one release behind: `pg-core` 0.6.2 published 2026-07-30 and neither the pin nor `COMPATIBILITY.md`'s `crates.io` reader row moved, because nothing reads those rows — the npm rows cannot drift this way, since `manifest.test.mjs` machine-reads them. Tracked in #268; move the row and the pin together. - `pg-compat-js` is the Node half of the same gate: a private npm package (no build step, no lint/format tooling in the repo) that opens the sealer's output with the published npm readers, installed side by side through `npm:` aliases (`pg-wasm-0-6-1`, `pg-js-2-3-3`, `pg-js-1-11-0`) because several versions of one package have to coexist. Run it locally with `npm --prefix pg-compat-js ci` then `PG_COMPAT_ARTIFACTS= npm --prefix pg-compat-js test`; in CI the `wire-compat-js` job in `build.yml` downloads the artifact `wire-compat-rust` sealed and runs the same suite, gated on `needs.wire-compat-rust.outputs.sealed` rather than a second copy of the path filter. `node --test test/` does NOT work (Node reads the bare directory as a module path and dies with `Cannot find module .../test`) — the `test` script passes the glob `"test/*.test.mjs"` instead. Its reader list is not free to edit on its own: `test/manifest.test.mjs` parses the fenced `Reader list` block in root `COMPATIBILITY.md` (` ` rows) and compares the `npm` rows against `readers()`, so that block is machine-read and reformatting it into prose or a table breaks the test. It also compares each declared `package` and `version` against the installed alias's own `package.json`, read off disk because neither published package exports `package.json` (`import('/package.json')` is `ERR_PACKAGE_PATH_NOT_EXPORTED`). From 950f9c426ba90f6282277bd1188613fde66d1b2b Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Sun, 9 Aug 2026 16:18:19 +0200 Subject: [PATCH 2/2] docs: narrow the --all-features claim to pg-core, fix the target count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-crate was the wrong axis: `cargo test -p pg-core --all-features` hits the same wasm32 `compile_error!`, so scoping does not make it safe. What makes pkg/cli/ffi safe is that they declare no `[features]` at all, which makes `--all-features` a no-op there — pg-core is the only member with a `[features]` section, so it is the only one that can break. Also 9 test binaries plus pg-core's doc-tests, not 10 binaries. --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5a4ea89..2f0d388 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Migrated from the dobby memory repo (`encryption4all/dobby`). This file is the h ## Workspace & CI -- Workspace members: `pg-core` (lib), `pg-ffi` (C ABI), `pg-pkg` (PKG service), `pg-cli`, `cryptify` (file-transfer service, merged in 2026-07-31). `pg-wasm` is a sibling crate the root `Cargo.toml` lists under `exclude`, so it is not part of the workspace and is built separately with wasm-pack (see Release & configuration). Sub-crates share workspace files. Build the workspace from repo root with `cargo build`. Two test invocations are worth knowing, and the obvious reading of each is wrong. **`cargo test --workspace` passes** — 296 tests over 10 binaries, verified 2026-08-09 — but only by accident, so don't read it as the workspace being self-consistent. `pg-core`'s tests really are gated behind its `test` feature (also `rust`/`stream`), and `cargo test -p pg-core` alone still fails exactly as it always did, with `unresolved import crate::test` on `crate::test::TestSetup`. What rescues the workspace build is feature unification: `cryptify`'s dev-dependency asks for `pg-core` with `features = ["rust", "stream", "test"]` (`cryptify/Cargo.toml`), so the feature is on for every member — confirm with `cargo tree -e features -p pg-core --workspace`. That has only been true since `cryptify` joined the workspace on 2026-07-31; it is a side effect of one member's dev-deps rather than a property of the workspace, and it reverts to the old compile failure the moment that line changes. **`cargo test --workspace --all-features` does not compile at all**, on any host: it switches on `pg-core`'s `web`, whose `compile_error!` fires off wasm32 — `error: "web" feature should only be enabled on wasm32 targets`, `pg-core/src/client/web/mod.rs:19`. `--all-features` is correct *per crate* (it is what the CI matrices use for `pkg`/`cli`/`ffi`, and what the api_gate suite further down is run with) and wrong workspace-wide. CI (`.github/workflows/build.yml`) runs tests per crate: `cargo test --manifest-path pg-core/Cargo.toml --features test,rust,stream` for core, `--all-targets` for `cryptify` (it declares no features of its own, so that is the equivalent invocation), `--all-features` for `pkg`/`cli`/`ffi`. None of these cover `pg-wasm`. The `test`/`format`/`clippy` matrices key on the crate **directory** rather than a `pg-` suffix, because `cryptify` doesn't carry that prefix — so the check contexts are `Test workspace (pg-core)`, not `Test workspace (core)`. Nothing required points at them today, but whatever adds to the required list later must use the current names. Where that list lives is not obvious and an agent cannot read it: required checks come from **classic branch protection** (`gh api repos/encryption4all/postguard/branches/main/protection`, currently exactly `Wire compat`), *not* from the `main` ruleset, which carries only `deletion`/`non_fast_forward`/`pull_request` and no `required_status_checks` rule at all. `dobby-coder` gets 403 on the protection endpoint, so reading the ruleset alone concludes nothing is required, which is wrong — ask a maintainer rather than inferring. `pg-core` uses CGWKV + MKEM for multi-recipient encryption (production feature set `["cgwkv", "mkem"]`). [#262](https://github.com/encryption4all/postguard/issues/262) (merged `0343369`, #295) replaced the two per-language `Wire compat (published pg-core)` / `Wire compat (published pg-wasm/pg-js)` required checks with this single `wire-compat` job (`needs: [wire-compat-rust, wire-compat-js]`, `permissions: contents: read`); the two old names are no longer required and the underlying per-language jobs still exist and still run, just aren't required individually anymore. +- Workspace members: `pg-core` (lib), `pg-ffi` (C ABI), `pg-pkg` (PKG service), `pg-cli`, `cryptify` (file-transfer service, merged in 2026-07-31). `pg-wasm` is a sibling crate the root `Cargo.toml` lists under `exclude`, so it is not part of the workspace and is built separately with wasm-pack (see Release & configuration). Sub-crates share workspace files. Build the workspace from repo root with `cargo build`. Two test invocations are worth knowing, and the obvious reading of each is wrong. **`cargo test --workspace` passes** — 296 tests over 10 targets (9 binaries plus `pg-core`'s doc-tests), verified 2026-08-09 — but only by accident, so don't read it as the workspace being self-consistent. `pg-core`'s tests really are gated behind its `test` feature (also `rust`/`stream`), and `cargo test -p pg-core` alone still fails exactly as it always did, with `unresolved import crate::test` on `crate::test::TestSetup`. What rescues the workspace build is feature unification: `cryptify`'s dev-dependency asks for `pg-core` with `features = ["rust", "stream", "test"]` (`cryptify/Cargo.toml`), so the feature is on for every member — confirm with `cargo tree -e features -p pg-core --workspace`. That has only been true since `cryptify` joined the workspace on 2026-07-31; it is a side effect of one member's dev-deps rather than a property of the workspace, and it reverts to the old compile failure the moment that line changes. **`cargo test --workspace --all-features` does not compile at all**, on any host: it switches on `pg-core`'s `web`, whose `compile_error!` fires off wasm32 — `error: "web" feature should only be enabled on wasm32 targets`, `pg-core/src/client/web/mod.rs:19`. The axis is whether `pg-core` is in the selected set, **not** how the invocation is scoped: `cargo test -p pg-core --all-features` fails identically, so this is not a workspace-wide problem that scoping fixes. `--all-features` is correct only for the members that declare no features of their own — `pkg`, `cli` and `ffi` in the CI matrices, and the api_gate suite further down, which lives in `pg-pkg` — where it is a no-op. `pg-core` is the sole member with a `[features]` section, so it is the sole member `--all-features` can break. CI (`.github/workflows/build.yml`) runs tests per crate: `cargo test --manifest-path pg-core/Cargo.toml --features test,rust,stream` for core, `--all-targets` for `cryptify` (it declares no features of its own, so that is the equivalent invocation), `--all-features` for `pkg`/`cli`/`ffi`. None of these cover `pg-wasm`. The `test`/`format`/`clippy` matrices key on the crate **directory** rather than a `pg-` suffix, because `cryptify` doesn't carry that prefix — so the check contexts are `Test workspace (pg-core)`, not `Test workspace (core)`. Nothing required points at them today, but whatever adds to the required list later must use the current names. Where that list lives is not obvious and an agent cannot read it: required checks come from **classic branch protection** (`gh api repos/encryption4all/postguard/branches/main/protection`, currently exactly `Wire compat`), *not* from the `main` ruleset, which carries only `deletion`/`non_fast_forward`/`pull_request` and no `required_status_checks` rule at all. `dobby-coder` gets 403 on the protection endpoint, so reading the ruleset alone concludes nothing is required, which is wrong — ask a maintainer rather than inferring. `pg-core` uses CGWKV + MKEM for multi-recipient encryption (production feature set `["cgwkv", "mkem"]`). [#262](https://github.com/encryption4all/postguard/issues/262) (merged `0343369`, #295) replaced the two per-language `Wire compat (published pg-core)` / `Wire compat (published pg-wasm/pg-js)` required checks with this single `wire-compat` job (`needs: [wire-compat-rust, wire-compat-js]`, `permissions: contents: read`); the two old names are no longer required and the underlying per-language jobs still exist and still run, just aren't required individually anymore. - `cryptify` was a separate repo until `ba380a1` (#277) merged it as a workspace member, history-preserving, the deliberate yes decided in #255/#285. What that bought: one lockfile, which closes a real drift class — the two trees previously built `pg-core`'s container serializer against two different `bincode-next` versions with nothing comparing them. What it costs is a **standing dependency coupling, and it does not go away**: `pg-pkg` reaches `libsqlite3-sys` through `sqlx`, `cryptify` reaches it through `rusqlite`, and it is `libsqlite3-sys` itself that declares `links = "sqlite3"` (`rusqlite`, `sqlx` and `sqlx-sqlite` all declare none — check with `cargo metadata`, not by guessing which crate in the chain owns it). Only one package per resolve graph may claim a given `links` value, and optional dependencies sit in the graph whether their feature is enabled or not — so `sqlx-sqlite` is locked even though `pg-pkg` enables only `postgres`. The newest intersecting pair is **sqlx 0.9 + rusqlite 0.39** (`libsqlite3-sys 0.37` in the root lockfile), so every `rusqlite` bump waits on sqlx's `libsqlite3-sys` ceiling — and that block is live today, not merely future: `rusqlite` 0.40.0 wants `libsqlite3-sys ^0.38.0` and 0.40.1 wants `^0.38.1`, both published, neither yanked, and both outside `sqlx-sqlite` 0.9.0's `>=0.30.1, <0.38.0`, so a lone rusqlite bump does not resolve at all. Don't bump either half alone; check the intersection first. - `pg-compat` is a second excluded sibling crate (root `Cargo.toml` `exclude`), and the exclusion is load-bearing: it depends on `pg-core` from **crates.io** (`=0.6.1`), not on `../pg-core`, so it can open bytes sealed by this tree with published readers. It has its own `Cargo.lock` (run it with `--locked`). Its input comes from `cargo run -p pg-core --features stream --example seal-samples -- `, a deterministic sealer whose output layout is documented in `pg-compat/README.md`. CI wires the two together: `wire-compat-rust` in `build.yml` seals with HEAD and opens with published pg-core on any PR touching the wire surface (pg-core/pg-wasm/pg-compat/pg-compat-js trees, the ROOT `Cargo.lock`/`Cargo.toml` — pg-core resolves from the root lockfile — and build.yml itself); `pg-compat-lint` covers the crate's fmt/clippy, which the per-crate matrices don't. The `=0.6.1` pin is currently one release behind: `pg-core` 0.6.2 published 2026-07-30 and neither the pin nor `COMPATIBILITY.md`'s `crates.io` reader row moved, because nothing reads those rows — the npm rows cannot drift this way, since `manifest.test.mjs` machine-reads them. Tracked in #268; move the row and the pin together. - `pg-compat-js` is the Node half of the same gate: a private npm package (no build step, no lint/format tooling in the repo) that opens the sealer's output with the published npm readers, installed side by side through `npm:` aliases (`pg-wasm-0-6-1`, `pg-js-2-3-3`, `pg-js-1-11-0`) because several versions of one package have to coexist. Run it locally with `npm --prefix pg-compat-js ci` then `PG_COMPAT_ARTIFACTS= npm --prefix pg-compat-js test`; in CI the `wire-compat-js` job in `build.yml` downloads the artifact `wire-compat-rust` sealed and runs the same suite, gated on `needs.wire-compat-rust.outputs.sealed` rather than a second copy of the path filter. `node --test test/` does NOT work (Node reads the bare directory as a module path and dies with `Cannot find module .../test`) — the `test` script passes the glob `"test/*.test.mjs"` instead. Its reader list is not free to edit on its own: `test/manifest.test.mjs` parses the fenced `Reader list` block in root `COMPATIBILITY.md` (` ` rows) and compares the `npm` rows against `readers()`, so that block is machine-read and reformatting it into prose or a table breaks the test. It also compares each declared `package` and `version` against the installed alias's own `package.json`, read off disk because neither published package exports `package.json` (`import('/package.json')` is `ERR_PACKAGE_PATH_NOT_EXPORTED`).