refactor(pgp): fold uselesskey-pgp-native content into uselesskey-pgp::native#599
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors the PGP native adapter logic by moving the PgpNativeExt trait and its implementations from uselesskey-pgp-native into a new native feature-gated module within uselesskey-pgp, converting the former into a deprecated compatibility shim. The reviewer suggested enhancing the panic messages in the new native module by including the underlying pgp crate error to provide better context for debugging parsing failures.
| fn secret_key(&self) -> SignedSecretKey { | ||
| SignedSecretKey::from_bytes(Cursor::new(self.private_key_binary())) | ||
| .expect("failed to parse uselesskey PGP private key bytes") | ||
| } |
There was a problem hiding this comment.
When parsing fails, it is helpful to include the underlying error message from the pgp crate to aid in debugging, especially if this is triggered by a malformed fixture or an unexpected change in the parser's behavior.
| fn secret_key(&self) -> SignedSecretKey { | |
| SignedSecretKey::from_bytes(Cursor::new(self.private_key_binary())) | |
| .expect("failed to parse uselesskey PGP private key bytes") | |
| } | |
| fn secret_key(&self) -> SignedSecretKey { | |
| SignedSecretKey::from_bytes(Cursor::new(self.private_key_binary())) | |
| .unwrap_or_else(|e| panic!("failed to parse uselesskey PGP private key bytes: {e}")) | |
| } |
| fn public_key(&self) -> SignedPublicKey { | ||
| SignedPublicKey::from_bytes(Cursor::new(self.public_key_binary())) | ||
| .expect("failed to parse uselesskey PGP public key bytes") | ||
| } |
There was a problem hiding this comment.
Including the parser error in the panic message provides better context for failures.
| fn public_key(&self) -> SignedPublicKey { | |
| SignedPublicKey::from_bytes(Cursor::new(self.public_key_binary())) | |
| .expect("failed to parse uselesskey PGP public key bytes") | |
| } | |
| fn public_key(&self) -> SignedPublicKey { | |
| SignedPublicKey::from_bytes(Cursor::new(self.public_key_binary())) | |
| .unwrap_or_else(|e| panic!("failed to parse uselesskey PGP public key bytes: {e}")) | |
| } |
| fn secret_key_armor(&self) -> SignedSecretKey { | ||
| let (key, _) = SignedSecretKey::from_armor_single(Cursor::new(self.private_key_armored())) | ||
| .expect("failed to parse armored uselesskey PGP private key"); | ||
| key | ||
| } |
There was a problem hiding this comment.
Including the parser error in the panic message provides better context for failures.
| fn secret_key_armor(&self) -> SignedSecretKey { | |
| let (key, _) = SignedSecretKey::from_armor_single(Cursor::new(self.private_key_armored())) | |
| .expect("failed to parse armored uselesskey PGP private key"); | |
| key | |
| } | |
| fn secret_key_armor(&self) -> SignedSecretKey { | |
| let (key, _) = SignedSecretKey::from_armor_single(Cursor::new(self.private_key_armored())) | |
| .unwrap_or_else(|e| panic!("failed to parse armored uselesskey PGP private key: {e}")); | |
| key | |
| } |
| fn public_key_armor(&self) -> SignedPublicKey { | ||
| let (key, _) = SignedPublicKey::from_armor_single(Cursor::new(self.public_key_armored())) | ||
| .expect("failed to parse armored uselesskey PGP public key"); | ||
| key | ||
| } |
There was a problem hiding this comment.
Including the parser error in the panic message provides better context for failures.
| fn public_key_armor(&self) -> SignedPublicKey { | |
| let (key, _) = SignedPublicKey::from_armor_single(Cursor::new(self.public_key_armored())) | |
| .expect("failed to parse armored uselesskey PGP public key"); | |
| key | |
| } | |
| fn public_key_armor(&self) -> SignedPublicKey { | |
| let (key, _) = SignedPublicKey::from_armor_single(Cursor::new(self.public_key_armored())) | |
| .unwrap_or_else(|e| panic!("failed to parse armored uselesskey PGP public key: {e}")); | |
| key | |
| } |
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (15)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…::native Inverts the dep direction between `uselesskey-pgp` and `uselesskey-pgp-native`. The `PgpNativeExt` trait and its impls now live in `crates/uselesskey-pgp/src/native.rs`, gated behind a new `native` Cargo feature on `uselesskey-pgp`. `uselesskey-pgp-native` becomes a thin re-export shim re-exporting `uselesskey_pgp::native::*` to preserve the v0.7.x public-internal surface for downstream pinners. This matches the v0.7.0 fold pattern established for `core-cache`, `core-id`, `core-hash`, `core-factory`, `token-spec`, etc. and the v0.8.0 hmac-spec fold (#595) where the owner fixture crate took canonical ownership and the previously-published microcrate became a compatibility shim. Mechanical changes: - New `crates/uselesskey-pgp/src/native.rs` carries `PgpNativeExt`, the impl block on `PgpKeyPair`, and the inline `#[cfg(test)] mod tests` block. - `crates/uselesskey-pgp/src/lib.rs` declares `#[cfg(feature = "native")] pub mod native;` and re-exports `PgpNativeExt` at the top level under the same gate. - `uselesskey-pgp/Cargo.toml` adds a `native` Cargo feature (`native = []`); the `pgp` dep is already a required workspace dep for keygen, so no new dep surface is added. The `pgp` 0.19 pin moves into `[workspace.dependencies]` so the two crates share the same version line. - `uselesskey-pgp-native/Cargo.toml` drops its direct `pgp = ...` dep, adds `uselesskey-pgp.workspace = true` with `features = ["native"]`, and routes its `pgp-native` feature to a no-op marker (the surface is unconditionally re-exported because `uselesskey-pgp` is now a hard dep with `native` already enabled). `pgp` remains as a dev-dep for the integration test and example. - `uselesskey-pgp-native/src/lib.rs` is now a thin shim: `pub use uselesskey_pgp::native::*;`. - `xtask::PUBLISH_CRATES` moves `uselesskey-pgp-native` to a post-`uselesskey-pgp` slot under a "PGP compatibility shim" comment, matching `uselesskey-core-hmac-spec` after the hmac fold. - `xtask::plan::dependents()` makes `uselesskey-pgp-native` a leaf and adds it as a `uselesskey-pgp` dependent (so a pgp source change still triggers a shim rebuild). A new `pgp_native_change_is_isolated_to_shim` test mirrors the `core_hmac_spec_change_is_isolated_to_shim` shape. - `docs/explanation/architecture.md`, `docs/metadata/workspace-docs.json`, and `docs/reference/support-matrix.md` describe the crate as a compatibility shim. Both `uselesskey-pgp` and `uselesskey-pgp-native` bump to 0.7.2 so the shim's re-export of `uselesskey_pgp::native` resolves against a registry version that ships the new path: `cargo publish --dry-run -p uselesskey-pgp-native` would otherwise resolve against the published v0.7.1 of `uselesskey-pgp`, which does not have `native`, and fail with `E0432: unresolved imports`. Downstream consumers (`uselesskey`, `uselesskey-interop-tests`) keep their existing `version = "0.7.1"` constraint — cargo treats it as `^0.7.1`, which accepts v0.7.2. This is PR-3 of the v0.8.0 SRP collapse lane. The shim crate stays published for v0.7.x compat and is scheduled for removal in PR-4.
Adds an optional `version_override` field to
`DependencySnippet::dependencies[]` in `docs/metadata/workspace-docs.json`
so a single dependency in a rendered snippet can be pinned to a
post-`release_version` bump. Used here to render
`uselesskey-pgp-native = { version = "0.7.2" }` while the rest of the
snippet continues to use the `release_version` (0.7.1).
This matches the publish-preflight `doc-versions` invariant after the
pgp-native fold (#PR-3 of the v0.8.0 SRP collapse lane) bumped
`uselesskey-pgp-native` to 0.7.2 alongside `uselesskey-pgp`.
- `xtask::docs_sync::SnippetDependency` gains
`version_override: Option<String>`; `render_single_dependency_snippet`
prefers it when present.
- `docs/metadata/workspace-docs.json` sets the override on the
`pgp-native adapter` snippet entry.
- Regenerated `README.md`, `crates/uselesskey/README.md`, and
`docs/how-to/choose-features.md` via `cargo xtask docs-sync`.
bf26db6 to
ce122dc
Compare
Removes 29 fully-folded published-internal compatibility shim crates that v0.7.x carried as one-minor-release re-export shells over content already owned by the public crates' `srp::*` modules. This is PR-4 of the v0.8.0 SRP collapse lane and is a BREAKING change for downstream consumers that pinned the shim crate names directly. Scope (29 crates removed) Core internals (11) -> uselesskey_core::srp::*: - uselesskey-core-cache, -factory, -hash, -id, -seed, -sink - uselesskey-core-keypair, -keypair-material - uselesskey-core-negative, -negative-der, -negative-pem JWK internals (5) -> uselesskey_jwk::srp::*: - uselesskey-core-kid, -jwk, -jwk-builder, -jwk-shape, -jwks-order Token internals (4) -> uselesskey_token::srp::*: - uselesskey-core-base62, -token, -token-shape, uselesskey-token-spec X.509 internals (5) -> uselesskey_x509::srp::*: - uselesskey-core-x509, -x509-spec, -x509-derive, -x509-negative, -x509-chain-negative Folded standalones (3): - uselesskey-core-hmac-spec -> uselesskey_hmac::srp::spec (content moved in v0.7.2 via #595; shim removed here) - uselesskey-core-rustls-pki -> uselesskey_rustls::srp::pki (content moved in v0.7.2 via #598; shim removed here) - uselesskey-pgp-native -> uselesskey_pgp::native (feature = "native"; content moved in v0.7.2 via #599; shim removed here) Conditional duplicate (1): - uselesskey-jose-openid was byte-equal to uselesskey_jsonwebtoken::JwtKeyExt; consumers should depend on uselesskey-jsonwebtoken directly. Why The v0.7.x line shipped each fold (#595, #598, #599 and predecessors) with the shim crate retained as a published-internal re-export so v0.7.x downstreams kept compiling for one minor release. v0.8.0 removes the shims per the SRP collapse plan; crate-surface contracts at the module boundary instead of the crate boundary. The published surface goes from 51 to 22 public + 3 workspace-internal. Migration Downstream consumers should: - Replace `uselesskey-core-<x>` imports with `uselesskey_core::srp::<x>` - Replace `uselesskey-core-jwk*` / `-kid` / `-jwks-order` with `uselesskey_jwk` - Replace `uselesskey-token-spec` / `-core-base62` / `-core-token*` with `uselesskey_token` and its `srp::*` modules - Replace `uselesskey-core-x509*` with `uselesskey_x509::srp::*` - Replace `uselesskey-core-hmac-spec` with `uselesskey_hmac::srp::spec` - Replace `uselesskey-core-rustls-pki` with `uselesskey_rustls::srp::pki` - Replace `uselesskey-pgp-native` with `uselesskey-pgp` `features = ["native"]` - Replace `uselesskey-jose-openid::JoseOpenIdKeyExt` with `uselesskey_jsonwebtoken::JwtKeyExt` A full crate-to-module mapping table will land in docs/how-to/migrate-from-v0.7.md (PR-5). What this PR touches - Removes 29 crate directories from `crates/`. - Updates the workspace `Cargo.toml` `[workspace] members` and `[workspace.dependencies]` to drop the removed names. - Updates `xtask` PUBLISH_CRATES, MUTANT_CRATES, dependents() map, `compatibility_shim_owner` callsite, `is_adapter_crate` allowlist, and the public-surface internal-shard guard. - Removes the `is_adapter_crate` jose-openid / pgp-native entries. - Removes obsolete plan.rs tests that pinned per-shim impact propagation. - Migrates internal callers (`uselesskey-rsa/-ecdsa/-ed25519` keypair.rs, `uselesskey-bdd-steps`, all `fuzz/fuzz_targets/*.rs`) to canonical owner-crate `srp::*` paths. - Regenerates `docs/metadata/workspace-docs.json`, the support matrix, and the public-surface survey. - Resets `policy/no-panic-baseline.toml` to reflect the new module topology (deleted crate paths drop out; their canonical owner-crate paths are now the only home of the same panic-family findings). - Notes the cleanup in `CHANGELOG.md` `[Unreleased] Removed`, `AGENTS.md`, `CLAUDE.md`, and `docs/explanation/architecture.md`. Cross-refs: #595 (HmacSpec fold), #598 (rustls-pki fold), #599 (pgp-native fold) shipped the content moves; this PR removes the now-empty shim crates. Validation - `cargo check --workspace --all-targets --all-features` clean. - `cargo test --workspace --all-features --exclude uselesskey-bdd --no-run` clean. - `cargo fmt --check` clean. - `cargo clippy --workspace --all-targets --all-features -- -D warnings` clean (only `aws-lc-rs` build-script `NASM found` informational note). - `cargo xtask public-surface` clean: 35 workspace crates (17 public + 7 adapter + 11 workspace-only; 0 published-internals remaining). - `cargo xtask docs-sync --check` clean. - `cargo xtask check-file-policy` clean (844 files matched, 0 unmatched). - `cargo xtask no-blob` clean. - `cargo xtask typos` clean. - `cargo xtask publish-preflight` clean through every step except the expected uncommitted-tree check. - `cargo test -p xtask -- --test-threads=1` all 245 tests pass; one pre-existing parallel-mode CWD test-isolation flake is unrelated to this PR.
* chore(workspace): remove folded internal shim crates Removes 29 fully-folded published-internal compatibility shim crates that v0.7.x carried as one-minor-release re-export shells over content already owned by the public crates' `srp::*` modules. This is PR-4 of the v0.8.0 SRP collapse lane and is a BREAKING change for downstream consumers that pinned the shim crate names directly. Scope (29 crates removed) Core internals (11) -> uselesskey_core::srp::*: - uselesskey-core-cache, -factory, -hash, -id, -seed, -sink - uselesskey-core-keypair, -keypair-material - uselesskey-core-negative, -negative-der, -negative-pem JWK internals (5) -> uselesskey_jwk::srp::*: - uselesskey-core-kid, -jwk, -jwk-builder, -jwk-shape, -jwks-order Token internals (4) -> uselesskey_token::srp::*: - uselesskey-core-base62, -token, -token-shape, uselesskey-token-spec X.509 internals (5) -> uselesskey_x509::srp::*: - uselesskey-core-x509, -x509-spec, -x509-derive, -x509-negative, -x509-chain-negative Folded standalones (3): - uselesskey-core-hmac-spec -> uselesskey_hmac::srp::spec (content moved in v0.7.2 via #595; shim removed here) - uselesskey-core-rustls-pki -> uselesskey_rustls::srp::pki (content moved in v0.7.2 via #598; shim removed here) - uselesskey-pgp-native -> uselesskey_pgp::native (feature = "native"; content moved in v0.7.2 via #599; shim removed here) Conditional duplicate (1): - uselesskey-jose-openid was byte-equal to uselesskey_jsonwebtoken::JwtKeyExt; consumers should depend on uselesskey-jsonwebtoken directly. Why The v0.7.x line shipped each fold (#595, #598, #599 and predecessors) with the shim crate retained as a published-internal re-export so v0.7.x downstreams kept compiling for one minor release. v0.8.0 removes the shims per the SRP collapse plan; crate-surface contracts at the module boundary instead of the crate boundary. The published surface goes from 51 to 22 public + 3 workspace-internal. Migration Downstream consumers should: - Replace `uselesskey-core-<x>` imports with `uselesskey_core::srp::<x>` - Replace `uselesskey-core-jwk*` / `-kid` / `-jwks-order` with `uselesskey_jwk` - Replace `uselesskey-token-spec` / `-core-base62` / `-core-token*` with `uselesskey_token` and its `srp::*` modules - Replace `uselesskey-core-x509*` with `uselesskey_x509::srp::*` - Replace `uselesskey-core-hmac-spec` with `uselesskey_hmac::srp::spec` - Replace `uselesskey-core-rustls-pki` with `uselesskey_rustls::srp::pki` - Replace `uselesskey-pgp-native` with `uselesskey-pgp` `features = ["native"]` - Replace `uselesskey-jose-openid::JoseOpenIdKeyExt` with `uselesskey_jsonwebtoken::JwtKeyExt` A full crate-to-module mapping table will land in docs/how-to/migrate-from-v0.7.md (PR-5). What this PR touches - Removes 29 crate directories from `crates/`. - Updates the workspace `Cargo.toml` `[workspace] members` and `[workspace.dependencies]` to drop the removed names. - Updates `xtask` PUBLISH_CRATES, MUTANT_CRATES, dependents() map, `compatibility_shim_owner` callsite, `is_adapter_crate` allowlist, and the public-surface internal-shard guard. - Removes the `is_adapter_crate` jose-openid / pgp-native entries. - Removes obsolete plan.rs tests that pinned per-shim impact propagation. - Migrates internal callers (`uselesskey-rsa/-ecdsa/-ed25519` keypair.rs, `uselesskey-bdd-steps`, all `fuzz/fuzz_targets/*.rs`) to canonical owner-crate `srp::*` paths. - Regenerates `docs/metadata/workspace-docs.json`, the support matrix, and the public-surface survey. - Resets `policy/no-panic-baseline.toml` to reflect the new module topology (deleted crate paths drop out; their canonical owner-crate paths are now the only home of the same panic-family findings). - Notes the cleanup in `CHANGELOG.md` `[Unreleased] Removed`, `AGENTS.md`, `CLAUDE.md`, and `docs/explanation/architecture.md`. Cross-refs: #595 (HmacSpec fold), #598 (rustls-pki fold), #599 (pgp-native fold) shipped the content moves; this PR removes the now-empty shim crates. Validation - `cargo check --workspace --all-targets --all-features` clean. - `cargo test --workspace --all-features --exclude uselesskey-bdd --no-run` clean. - `cargo fmt --check` clean. - `cargo clippy --workspace --all-targets --all-features -- -D warnings` clean (only `aws-lc-rs` build-script `NASM found` informational note). - `cargo xtask public-surface` clean: 35 workspace crates (17 public + 7 adapter + 11 workspace-only; 0 published-internals remaining). - `cargo xtask docs-sync --check` clean. - `cargo xtask check-file-policy` clean (844 files matched, 0 unmatched). - `cargo xtask no-blob` clean. - `cargo xtask typos` clean. - `cargo xtask publish-preflight` clean through every step except the expected uncommitted-tree check. - `cargo test -p xtask -- --test-threads=1` all 245 tests pass; one pre-existing parallel-mode CWD test-isolation flake is unrelated to this PR. * xtask: drop deleted crates from pr-test orchestration PR #602 removed 29 shim crates but missed updating the xtask pr step's per-crate test target list. CI hit `error: cannot specify features for packages outside of workspace` for `uselesskey-core-base62`. Update the test-targets list to match the post-deletion workspace. The impacted-crates set is derived from the path components of changed files (`crates/<name>/...`); paths to deleted crates still appear in the diff and leaked into the test target list. Filter the list to crates whose `crates/<name>/Cargo.toml` still exists in the worktree, naturally dropping any crate the PR deletes. Add unit tests covering the deleted-shim and stale-dir cases. * chore(fuzz): migrate remaining fuzz targets off deleted shim crates The shim deletion PR caught 24 of the fuzz targets but left 24 others still importing the removed crate names (core_factory_cache, core_x509_negative, fuzz_factory_concurrent, etc.), which broke `cargo fuzz build`. Re-point every remaining fuzz target at canonical owner-crate paths: - uselesskey-core-factory -> uselesskey_core::Factory - uselesskey-core-id -> uselesskey_core::{ArtifactId, DerivationVersion, Seed} + uselesskey_core::srp::identity::derive_seed - uselesskey-core-hash -> uselesskey_core::srp::hash::* - uselesskey-core-keypair-material -> uselesskey_core::srp::keypair_material::* - uselesskey-core-negative-pem -> uselesskey_core::negative::* - uselesskey-core-token-shape -> uselesskey_token::srp::shape::* - uselesskey-core-kid -> uselesskey_jwk::srp::kid::* - uselesskey-core-jwk-shape -> uselesskey_jwk::{AnyJwk, Jwks, ...} - uselesskey-core-jwks-order -> uselesskey_jwk::srp::ordering::* - uselesskey-core-x509-spec / x509-negative -> uselesskey_x509::{X509Spec, ChainSpec, X509Negative, ChainNegative, KeyUsage, NotBeforeOffset} * xtask(test): make publish_order_is_topological CWD-independent The test invoked `cargo metadata` without --manifest-path. When run in parallel with other xtask tests that change CWD, cargo metadata could not locate the working directory. Pass --manifest-path derived from CARGO_MANIFEST_DIR so the test is deterministic under --test-threads > 1. * xtask(test): set explicit current_dir on cargo metadata invocation The prior --manifest-path fix wasn't sufficient because cargo's working-directory lookup happens before it reads --manifest-path. When a parallel test deletes its tempdir, the process CWD becomes invalid and cargo aborts with "Could not locate working directory" before even seeing the --manifest-path arg. Explicitly set Command::current_dir to the workspace root so cargo does not depend on the process CWD at all.
Why
PR-3 of the v0.8.0 SRP collapse lane (after #595 hmac-spec, #598 rustls-pki).
uselesskey-pgp-nativeis in the per-crate survey's "conditional" bucket — content (~50 LoC ofPgpNativeExtimpls) is valuable, but doesn't justify a separately published adapter crate carrying its ownpgp0.19 public-dep surface. Folding it intouselesskey-pgp::native(under a newnativeCargo feature) collapses the publish graph and keeps the supportedPgpNativeExtsurface co-located with thePgpKeyPairit adapts.The shim crate stays published as a v0.7.x compat re-export; deletion is PR-4 of this lane.
What
crates/uselesskey-pgp/src/native.rscarriesPgpNativeExt, the impl block onPgpKeyPair, and the inline#[cfg(test)] mod tests.crates/uselesskey-pgp/src/lib.rsdeclares#[cfg(feature = "native")] pub mod native;and re-exportsPgpNativeExtat the top level under the same gate.uselesskey-pgp/Cargo.tomladds thenative = []feature (off by default). Thepgp0.19 pin moves into[workspace.dependencies]so the two crates share one version line.uselesskey-pgp-native/Cargo.tomldrops its directpgp = ...dep, addsuselesskey-pgp.workspace = truewithfeatures = ["native"], and routes itspgp-nativefeature to a no-op marker.uselesskey-pgp-native/src/lib.rsis nowpub use uselesskey_pgp::native::*;.xtask::PUBLISH_CRATESmovesuselesskey-pgp-nativeto a post-uselesskey-pgpslot under a "PGP compatibility shim" comment, matchinguselesskey-core-hmac-specafter refactor(hmac): fold core-hmac-spec content into uselesskey-hmac::srp::spec #595.xtask::plan::dependents()makesuselesskey-pgp-nativea leaf and adds it as auselesskey-pgpdependent. Newpgp_native_change_is_isolated_to_shimtest mirrors thecore_hmac_spec_change_is_isolated_to_shimshape.uselesskey-pgpanduselesskey-pgp-nativebump to 0.7.2 so the shim'spub use uselesskey_pgp::native::*;resolves against a registry version that ships the new path (otherwise dry-run resolves to v0.7.1 and failsE0432: unresolved imports).docs/explanation/architecture.md,docs/metadata/workspace-docs.json, anddocs/reference/support-matrix.mddescribe the crate as a compatibility shim.[Unreleased] / Changedentry added.Doc-versions plumbing
The shim bumps to 0.7.2 while the rest of the workspace stays at 0.7.1. This breaks the existing
publish-preflight doc-versionscheck, which expects every workspace-crate mention in a README snippet to matchcargo metadata. Followup commit adds an opt-inversion_override: Option<String>field toDependencySnippet::dependencies[]inworkspace-docs.json, threaded throughxtask::docs_sync::render_single_dependency_snippet. Thepgp-native adaptersnippet entry sets the override to 0.7.2; the rest of the snippet continues to render againstrelease_version(0.7.1). README.md, crates/uselesskey/README.md, and docs/how-to/choose-features.md regenerated viacargo xtask docs-sync.Out of scope
PgpNativeExt(unchanged — same 4 methods, same return types).Test plan
cargo build --workspace --all-targets --all-features --lockedcargo test -p uselesskey-pgp --all-features(native test runs; round-trip fingerprint check passes)cargo test -p uselesskey-pgp --no-default-features(verifiesnativefeature is off-by-default and the keygen surface stands alone)cargo test -p uselesskey-pgp-native --all-features(shim integration test still resolves through the re-export)cargo xtask publish-check(clean;uselesskey-pgp-nativeskipped per usualworkspace dep not yet on crates.iopattern matching refactor(hmac): fold core-hmac-spec content into uselesskey-hmac::srp::spec #595)cargo xtask publish-preflight(57 passed, 0 failed;doc-versionsclean after theversion_overrideplumbing)cargo fmt --checkcargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo xtask scanner-safe-reference --checkcargo xtask docs-sync --check(clean)cargo xtask typoscargo test -p xtask docs_sync(newdependency_snippet_version_override_winstest covers the override path)