Skip to content

ci: bring the decent_exposure privacy gate to the mobile-API branch - #2542

Open
zancas wants to merge 10 commits into
devfrom
ci/decent-exposure-2509
Open

zancas wants to merge 10 commits into
devfrom
ci/decent-exposure-2509

Conversation

@zancas

@zancas zancas commented Jul 26, 2026 •

Copy link
Copy Markdown
Member

Source crates — code

Package Change type Files Lines +/−
zingolib migration visibility ceiling and interior demotions 6 +50 / −47
zingolib visibility demotions (wallet/legacy.rs) 1 +37 / −34
zingo-cli visibility demotions 2 +13 / −13
libtonode-tests forbid(unsafe_code) attribute and an import move 1, shares lib.rs below +4 / −2
Subtotal 10 +104 / −96

Source crates — documentation

Package Change type Files Lines +/−
zingolib_testutils crate documentation 1 +15 / −0
zingo-netutils item documentation 2 +15 / −0
libtonode-tests crate documentation 1 +11 / −0
Subtotal 4 +41 / −0

Tooling and CI

Package Change type Files Lines +/−
tools/workbench public-api goldens (generated pins) 10 +2456 / −0
tools/workbench gate driver, decent-exposure (new binary) 1 +285 / −0
CI workflows decent_exposure job in ci-pr.yaml 1 +60 / −0
Subtotal 12 +2801 / −0

Grand total: 26 files, +2946 / −96.

This PR brings the decent_exposure privacy gate of #2541 to the mobile-API branch, so #2509's surface is policed by the same three layers before it merges: rustc's unreachable_pub at deny, rustc's missing_docs at deny, and cargo public-api against per-package goldens. The CI arrangement is the one #2541 describes — the other jobs start optimistically in parallel and share warm caches, and a privacy failure reaps the entire run.

The stack begins with the visibility-ceiling commit already prepared on chore/2509-visibility: every wallet/migration submodule declaration becomes pub(crate), so the facade re-export block in migration.rs is the module's whole public surface. The gate's first layer then flagged the 21 interior items the ceiling left plain pub that the facade does not re-export; a follow-up commit demotes each to its real reach — fifteen to pub(crate) for their cross-module callers, five to private. No demotion surfaced dead code, and no item visible through the facade changes.

The three gate commits are cherry-picks from #2541, and the final commit re-pins the zingolib golden for this branch: the ceiling and the demotions remove more public API than the mobile work adds, so the pinned surface nets 125 lines smaller — 322 items leave, 197 join. That golden diff is the reviewable record of exactly what #2509 plus this stack does to zingolib's public face. The gate passes all three layers locally on the result.

🤖 Generated with Claude Code

zancas and others added 10 commits July 25, 2026 20:07
…ion ADR to 0018

ADR 0009 asserted that allow_v6_transactions vanished before any release
carried it, leaving the wallet file format unchanged. ADR 0015 (PR #2529,
merged to dev) supersedes that ruling: landing in dev ships the format, so
version 42 names two on-disk layouts and the reader disambiguates them.
The corrected sentence defers the format detail to ADR 0015.

Because dev owns number 0015 for the wallet-format ADR, the branch-local
migration ADR takes the next free number, 0018. The citations in ADR 0016
and in the quick_immediate_migration doc comment follow it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The concurrent mobile batch commit (f48b15c) introduced the part-anchors
ADR as 0018 while the migration ADR was being renumbered to the same value.
The part-anchors ADR keeps 0018, since the pushed branch already cites it
by that number throughout migrate.rs and the migration store; the
branch-local migration ADR takes 0019, and the citations in ADR 0016 and
the quick_immediate_migration doc comment follow it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The citation in ADR 0016 and the doc-comment path in
quick_immediate_migration still named 0018 after the renumber; both now
point at 0019-immediate-migration-is-send-shaped.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every wallet/migration submodule declaration becomes pub(crate), so the
re-export block in migration.rs is the module's whole public surface and
a new public name is a deliberate act there rather than a side effect of
pub in a submodule. Items inside the submodules keep plain pub under the
ceiling, and helpers used only within their own file lose visibility
entirely: estimated_unix_at, random_target_in_bucket, bucket_at_or_after,
immediate_migration_fee, and the EXPIRY_MODULUS, EXPIRY_WINDOW, and
ANCHOR_AGE_CAP constants are now private, and previous_boundary is
test-gated, which the dead-code lint exposed once its pub mask came off.

The facade sheds the names nothing outside the crate uses: due_now_parts,
plan_schedule, upcoming_windows, and window_timeline demote to pub(crate)
re-exports, immediate_migration_fee and estimated_unix_at leave the block,
and bucket_index and PartAssessment join it, the former because the
libtonode migration tests call it (their import moves off the now-capped
schedule module path), the latter because ReconcileReport's public field
already exposed it without a nameable path. Three items the ceiling cannot
reach because their carriers are public tighten individually:
PartRecord::anchor_bucket, PartRecord::unassign, and
LightWallet::bound_note_confirmed_at become pub(crate).

The compiler adjudicated every step: cargo check --workspace
--all-targets, clippy, and fmt are clean, and an unreachable_pub sweep
confirms the mobile-facing LightClient surface and its status, handle,
plan, and report types keep their reachability unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
rustc's unreachable_pub lint reported 43 pub items that no external crate
could ever reach: 30 in zingolib/src/wallet/legacy.rs, whose parent module
is pub(crate), and 13 in zingo-cli, whose commands module is private. Each
item now carries the smallest visibility that keeps the crate compiling.
In legacy.rs the split was decided by the one out-of-module consumer, the
v0/v32 wallet-file readers in disk.rs: the types and readers they drive
keep pub(crate), items that only appear in those signatures match them at
pub(crate), and everything referenced solely within the module becomes
private. In zingo-cli the bin target's actual imports decided: the five
functions main.rs uses stay pub, three items lib.rs uses become
pub(crate), and the rest become private.

No demotion surfaced dead code, so nothing was deleted or test-gated. The
change prepares the tree for the decent_exposure CI gate, which holds
-D unreachable_pub over every package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
rustc's missing_docs lint found six gaps: the libtonode-tests and
zingolib_testutils crates had no crate-level documentation, and
zingo-netutils left GrpcIndexer::new and the three GetClientError variants
undocumented. Each now states what the item actually does, read from the
code rather than assumed. libtonode-tests also gains the
forbid(unsafe_code) attribute every sibling crate already carries.

The change prepares the tree for the decent_exposure CI gate, which holds
-D missing_docs over every package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
decent_exposure joins the PR workflow as its privacy authority. The other
jobs start optimistically in parallel with it and share warm build caches,
but when it fails, its final step cancels the entire run, so every
in-flight job is reaped and its runner returns to the pool. The job
carries actions: write for exactly that cancellation. The nightly
workflow inherits the arrangement by calling ci-pr.yaml.

The job enforces maximum item privacy in three layers, in order: rustc's
unreachable_pub at deny, so nothing is public by accident; rustc's
missing_docs at deny, so publicity always costs a written sentence; and
cargo public-api against per-package goldens, so a change to any public
surface is a reviewed diff rather than a side effect.

The driver lives in the workbench crate as decent-exposure. It derives the
package list from the root workspace manifest and appends the two
standalone workspaces, so a new root member is gated automatically. Each
layer runs against every package's lib target. The goldens under
tools/workbench/goldens/public-api/ pin the default-feature surface of all
ten packages; regenerate them with
`cargo run --manifest-path tools/workbench/Cargo.toml --bin decent-exposure -- --bless`
and review the diff. CI pins cargo-public-api 0.50.2, the version the
goldens were generated with, installed through the taiki-e/install-action
already used by the coverage workflow.

(cherry picked from commit 0e5172e and adapted to this branch)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The crate visibility ceiling on the migration submodules left their
interior items plain pub, and rustc's unreachable_pub flags the 21 of
them the facade does not re-export publicly. Each now carries its real
reach: fifteen become pub(crate) because other modules in the crate call
them (the schedule arithmetic parts.rs and reconcile.rs lean on, the
store read/write pair disk.rs drives, and the facade's own pub(crate)
re-exports), and five become private because only their own module uses
them (AnchorFloor's bucket helpers, draw_anchor_age, the ProveOnce type
alias, and the free plan_immediate_migration behind the LightClient
method of the same name). The facade block in migration.rs is untouched,
and no item visible through it changes.

No demotion surfaced dead code. The change satisfies the decent_exposure
gate's first layer on this branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The golden cherry-picked from dev pins dev's surface. On this branch the
visibility ceiling and the interior demotions removed more public API
than the mobile work added, so the re-pin nets 125 lines smaller: 322
items leave the pinned surface and 197 join it. The decent-exposure gate
passes all three layers against the result.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from fix/ironwood-split-mobile to dev July 26, 2026 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant