Summary
cargo xtask check-no-panic-family runs in no-new-debt mode against policy/no-panic-baseline.toml and currently reports 150 new debt sites, 0 stale, 0 expired (the original brief said 149; the live count from a fresh run on main is 150). The new debt clusters heavily in test code: 46 in crates/uselesskey-cli/tests/cli.rs alone, and the majority of the remaining 104 are in #[cfg(test)] mod tests { ... } sections inside production source files (e.g. seed.rs, sink.rs, cache.rs, xtask/src/main.rs). A small minority (~15) appears to be genuine production-path expect/panic! (Serialize::to_value, downcast_or_panic) that should either propagate Result or move into the receipted allowlist as infallible_invariant. The lane is open — no other open issue tracks this — and per docs/NO_PANIC_POLICY.md the baseline is expected to burn down toward zero, not be refreshed to absorb new debt.
Snapshot
no-panic: 4307 finding(s); 0 allowlisted; 4157 baselined; 150 new-debt; 54 stale-baseline; 0 expired (mode=no-new-debt; baseline=2485/2539)
Error: no-panic policy is no-new-debt and there are 150 new debt site(s), 0 stale allowlist entries, and 0 expired allowlist entries
(Reports: target/no-panic.md, target/no-panic.json.)
Top files by site count
Derived by cross-referencing target/policy-proposed/no-panic-proposed-allowlist.toml (cargo xtask no-panic propose) against policy/no-panic-baseline.toml.
| File |
Count |
Dominant family |
Notes |
crates/uselesskey-cli/tests/cli.rs |
46 |
expect (46) |
pure integration-test expect-ladder |
xtask/src/main.rs |
31 |
expect (22), unwrap (9) |
all inside #[cfg(test)] mod tests |
crates/uselesskey-jwk/src/srp/shape.rs |
15 |
expect (15) |
~8 production to_value/Display::fmt + 7 in #[test] |
crates/uselesskey-token/src/srp/shape.rs |
15 |
expect (15) |
mix of production JWT split/decode + tests |
crates/uselesskey-core/src/srp/seed.rs |
10 |
unwrap (10) |
all in #[cfg(test)] mod tests |
crates/uselesskey-core/src/srp/sink.rs |
8 |
unwrap (8) |
all in #[cfg(test)] mod tests |
crates/uselesskey-core/src/srp/cache.rs |
6 |
unwrap (4), panic_macro (1), expect (1) |
one production downcast_or_panic (L174) + 5 test |
crates/uselesskey-jwk/src/srp/kid.rs |
4 |
expect (4) |
test code |
crates/uselesskey-core/src/srp/factory.rs |
3 |
panic_macro (2), expect (1) |
review for infallible_invariant |
crates/uselesskey-x509/src/srp/derive.rs |
2 |
expect (1), unwrap (1) |
review |
crates/uselesskey-jwk/src/srp/builder.rs |
2 |
expect (2) |
review |
crates/uselesskey-core/src/srp/negative/pem.rs |
2 |
expect (1), unreachable (1) |
review |
crates/uselesskey-token/tests/negative_fixtures.rs |
2 |
unwrap (2) |
test code |
crates/uselesskey-core/src/srp/negative/der.rs |
1 |
unreachable (1) |
review |
crates/uselesskey-jwk/tests/negative_fixtures.rs |
1 |
expect (1) |
test code |
crates/uselesskey-x509/src/srp/spec/chain_spec.rs |
1 |
unreachable (1) |
review |
xtask/src/public_surface.rs |
1 |
expect (1) |
review |
Cluster breakdown
By family:
expect: 110
unwrap: 34
panic_macro: 3
unreachable: 3
By crate (entries / dominant cluster):
crates/uselesskey-cli: 46 — 100% integration-test expect in tests/cli.rs
xtask/src: 32 — 31 in #[cfg(test)] mod tests, 1 production in public_surface.rs
crates/uselesskey-core: 30 — all but cache.rs:174 (downcast_or_panic) and small bits in factory.rs/negative/* are unit tests
crates/uselesskey-jwk: 22 — ~8 production Serialize::to_value/Display::fmt expect, rest are unit tests
crates/uselesskey-token: 17 — mix of production JWT split/decode expect and unit tests
crates/uselesskey-x509: 3 — review individually (derive.rs, spec/chain_spec.rs)
By suspected root cause:
- Test code that should use
uselesskey-test-support helpers (ensure!, ensure_eq!, require_some, require_ok) — ~135 of 150 sites. This is the dominant cluster across cli.rs, xtask/src/main.rs#[cfg(test)], and the #[cfg(test)] mod tests blocks in seed.rs, sink.rs, cache.rs, jwk/src/srp/shape.rs, token/src/srp/shape.rs, kid.rs, etc.
- Production code that needs case-by-case review — ~15 of 150 sites. Examples:
crates/uselesskey-core/src/srp/cache.rs:174 — documented panic! in downcast_or_panic (infallible_invariant candidate)
crates/uselesskey-jwk/src/srp/shape.rs:53,90,328,339,371,382,419,430 — serde_json::to_value(self).expect(...) / to_string(self).expect(...) on a struct whose Serialize impl is total (either drop the expect and document, or move to allowlist as infallible_invariant)
crates/uselesskey-token/src/srp/shape.rs:191,294-296,315,316,319,328,353,361,373,374,604,609,610 — JWT segment split/base64-decode helpers in production; some should propagate Result, others may be invariants on caller-controlled input
crates/uselesskey-core/src/srp/factory.rs:* — 2 panic_macro + 1 expect (review)
crates/uselesskey-core/src/srp/negative/{pem,der}.rs, crates/uselesskey-x509/src/srp/{derive,spec/chain_spec}.rs — handful of unreachable!/expect in negative-fixture builders (likely fixture classification)
- Genuinely pre-existing debt that should have stayed in the baseline — 0 expected; if any surface during phase 1, allowlist them with owner+reason+expiry rather than refreshing the baseline.
Burndown plan
- Phase 1 — Migrate test-code
expect/unwrap to uselesskey-test-support fallible helpers. Target: 0 sites in the test-code cluster (~135 sites). Convert affected tests to -> TestResult<()> and use require_ok, require_some, ensure!, ensure_eq!. One PR per crate to keep diffs reviewable. After each crate PR, run cargo xtask no-panic baseline to drop the now-resolved entries from policy/no-panic-baseline.toml (the refresh refuses to absorb new debt — it only removes vanished ones).
- Phase 2 — Review remaining production-code sites case-by-case (~15 sites). For each: prefer converting to
?-propagated errors with thiserror-shaped types; for genuinely total operations (e.g. Serialize impls on known structs, downcast_or_panic documented as an API contract), add a receipted entry to policy/no-panic-allowlist.toml with classification (infallible_invariant / production / fixture), owner, explanation, and expires.
- Phase 3 — Flip
policy/clippy-lints.toml panic_family_level from warn to deny. This is Stage C in the [stage] block (currently current = "A", description references warn-stage panic-family lints burning down to deny). Gate: mode = "no-new-debt" checker exits 0, baseline has burned down to entries that are exclusively infallible_invariant/production and have been promoted into the allowlist with receipts. At that point the baseline can be retired and the checker can advance to mode = "blocking".
- Optional intermediate —
cargo xtask no-panic baseline to refresh the snapshot only after a deliberate burndown PR (it auto-drops vanished entries). Do NOT use --reset for this lane; that resets the snapshot and absorbs all new debt, which is what we are explicitly trying to avoid.
Crates / PR slicing
Each crate with more than 5 sites is a candidate for its own PR:
crates/uselesskey-cli — 46 sites, 1 file (tests/cli.rs). Largest single-PR win.
xtask/src — 32 sites (31 in main.rs#[cfg(test)], 1 production in public_surface.rs). Single PR.
crates/uselesskey-core — 30 sites across seed.rs, sink.rs, cache.rs, factory.rs, negative/{pem,der}.rs. Test-cluster (test modules) is a clean PR; the small production-path subset (cache.rs:174, factory.rs panics) can be a follow-up PR that adds receipted allowlist entries.
crates/uselesskey-jwk — 22 sites in shape.rs, kid.rs, builder.rs, tests/negative_fixtures.rs. Mix of test + production; split into test-migration PR and production-receipts PR.
crates/uselesskey-token — 17 sites in shape.rs and tests/negative_fixtures.rs. Same split as uselesskey-jwk.
Crates with 5 sites or fewer (crates/uselesskey-x509, crates/uselesskey-core/src/srp/negative/*) can be cleaned up in a single mop-up PR after the big ones land.
Acceptance criteria
cargo xtask check-no-panic-family exits 0 with mode = "no-new-debt", i.e. 0 new-debt, 0 stale, 0 expired.
- All production-path findings either no longer panic or have a receipted allowlist entry (
owner is not FIXME, explanation is concrete, expires is set).
- After phase 3,
policy/clippy-lints.toml panic_family_level = "deny" and [stage].current = "C".
References
docs/NO_PANIC_POLICY.md — definition, stages, identity model, classifications, modes.
policy/no-panic-allowlist.toml — receipted authoritative ledger.
policy/no-panic-baseline.toml — snapshot, should burn down to zero.
policy/clippy-lints.toml — panic_family_level = "warn", [stage] block (current = "A", description = "Warn-stage panic-family lints; semantic no-panic checker advisory; bare-allow blocking.").
crates/uselesskey-test-support — ensure!, ensure_eq!, require_ok, require_some, TestResult.
- Generated artifacts (re-run
cargo xtask check-no-panic-family + cargo xtask no-panic propose to refresh): target/no-panic.md, target/no-panic.json, target/policy-proposed/no-panic-proposed-allowlist.toml.
Not in scope
This issue is the burndown plan and tracking ticket, not a code change. No files in this repository should be edited as part of resolving this issue — the PRs that execute the plan are the deliverables. In particular: do not run cargo xtask no-panic baseline --reset to paper over the symptom; the entire point of the no-new-debt mode is to force the burndown.
Summary
cargo xtask check-no-panic-familyruns inno-new-debtmode againstpolicy/no-panic-baseline.tomland currently reports 150 new debt sites, 0 stale, 0 expired (the original brief said 149; the live count from a fresh run onmainis 150). The new debt clusters heavily in test code: 46 incrates/uselesskey-cli/tests/cli.rsalone, and the majority of the remaining 104 are in#[cfg(test)] mod tests { ... }sections inside production source files (e.g.seed.rs,sink.rs,cache.rs,xtask/src/main.rs). A small minority (~15) appears to be genuine production-pathexpect/panic!(Serialize::to_value,downcast_or_panic) that should either propagateResultor move into the receipted allowlist asinfallible_invariant. The lane is open — no other open issue tracks this — and perdocs/NO_PANIC_POLICY.mdthe baseline is expected to burn down toward zero, not be refreshed to absorb new debt.Snapshot
(Reports:
target/no-panic.md,target/no-panic.json.)Top files by site count
Derived by cross-referencing
target/policy-proposed/no-panic-proposed-allowlist.toml(cargo xtask no-panic propose) againstpolicy/no-panic-baseline.toml.crates/uselesskey-cli/tests/cli.rsexpect(46)expect-ladderxtask/src/main.rsexpect(22),unwrap(9)#[cfg(test)] mod testscrates/uselesskey-jwk/src/srp/shape.rsexpect(15)to_value/Display::fmt+ 7 in#[test]crates/uselesskey-token/src/srp/shape.rsexpect(15)crates/uselesskey-core/src/srp/seed.rsunwrap(10)#[cfg(test)] mod testscrates/uselesskey-core/src/srp/sink.rsunwrap(8)#[cfg(test)] mod testscrates/uselesskey-core/src/srp/cache.rsunwrap(4),panic_macro(1),expect(1)downcast_or_panic(L174) + 5 testcrates/uselesskey-jwk/src/srp/kid.rsexpect(4)crates/uselesskey-core/src/srp/factory.rspanic_macro(2),expect(1)infallible_invariantcrates/uselesskey-x509/src/srp/derive.rsexpect(1),unwrap(1)crates/uselesskey-jwk/src/srp/builder.rsexpect(2)crates/uselesskey-core/src/srp/negative/pem.rsexpect(1),unreachable(1)crates/uselesskey-token/tests/negative_fixtures.rsunwrap(2)crates/uselesskey-core/src/srp/negative/der.rsunreachable(1)crates/uselesskey-jwk/tests/negative_fixtures.rsexpect(1)crates/uselesskey-x509/src/srp/spec/chain_spec.rsunreachable(1)xtask/src/public_surface.rsexpect(1)Cluster breakdown
By family:
expect: 110unwrap: 34panic_macro: 3unreachable: 3By crate (entries / dominant cluster):
crates/uselesskey-cli: 46 — 100% integration-testexpectintests/cli.rsxtask/src: 32 — 31 in#[cfg(test)] mod tests, 1 production inpublic_surface.rscrates/uselesskey-core: 30 — all butcache.rs:174(downcast_or_panic) and small bits infactory.rs/negative/*are unit testscrates/uselesskey-jwk: 22 — ~8 productionSerialize::to_value/Display::fmtexpect, rest are unit testscrates/uselesskey-token: 17 — mix of production JWT split/decodeexpectand unit testscrates/uselesskey-x509: 3 — review individually (derive.rs,spec/chain_spec.rs)By suspected root cause:
uselesskey-test-supporthelpers (ensure!,ensure_eq!,require_some,require_ok) — ~135 of 150 sites. This is the dominant cluster acrosscli.rs,xtask/src/main.rs#[cfg(test)], and the#[cfg(test)] mod testsblocks inseed.rs,sink.rs,cache.rs,jwk/src/srp/shape.rs,token/src/srp/shape.rs,kid.rs, etc.crates/uselesskey-core/src/srp/cache.rs:174— documentedpanic!indowncast_or_panic(infallible_invariantcandidate)crates/uselesskey-jwk/src/srp/shape.rs:53,90,328,339,371,382,419,430—serde_json::to_value(self).expect(...)/to_string(self).expect(...)on a struct whose Serialize impl is total (either drop theexpectand document, or move to allowlist asinfallible_invariant)crates/uselesskey-token/src/srp/shape.rs:191,294-296,315,316,319,328,353,361,373,374,604,609,610— JWT segment split/base64-decode helpers in production; some should propagateResult, others may be invariants on caller-controlled inputcrates/uselesskey-core/src/srp/factory.rs:*— 2panic_macro+ 1expect(review)crates/uselesskey-core/src/srp/negative/{pem,der}.rs,crates/uselesskey-x509/src/srp/{derive,spec/chain_spec}.rs— handful ofunreachable!/expectin negative-fixture builders (likelyfixtureclassification)Burndown plan
expect/unwraptouselesskey-test-supportfallible helpers. Target: 0 sites in the test-code cluster (~135 sites). Convert affected tests to-> TestResult<()>and userequire_ok,require_some,ensure!,ensure_eq!. One PR per crate to keep diffs reviewable. After each crate PR, runcargo xtask no-panic baselineto drop the now-resolved entries frompolicy/no-panic-baseline.toml(the refresh refuses to absorb new debt — it only removes vanished ones).?-propagated errors withthiserror-shaped types; for genuinely total operations (e.g.Serializeimpls on known structs,downcast_or_panicdocumented as an API contract), add a receipted entry topolicy/no-panic-allowlist.tomlwithclassification(infallible_invariant/production/fixture),owner,explanation, andexpires.policy/clippy-lints.tomlpanic_family_levelfromwarntodeny. This is Stage C in the[stage]block (currentlycurrent = "A", description references warn-stage panic-family lints burning down to deny). Gate:mode = "no-new-debt"checker exits 0, baseline has burned down to entries that are exclusivelyinfallible_invariant/productionand have been promoted into the allowlist with receipts. At that point the baseline can be retired and the checker can advance tomode = "blocking".cargo xtask no-panic baselineto refresh the snapshot only after a deliberate burndown PR (it auto-drops vanished entries). Do NOT use--resetfor this lane; that resets the snapshot and absorbs all new debt, which is what we are explicitly trying to avoid.Crates / PR slicing
Each crate with more than 5 sites is a candidate for its own PR:
crates/uselesskey-cli— 46 sites, 1 file (tests/cli.rs). Largest single-PR win.xtask/src— 32 sites (31 inmain.rs#[cfg(test)], 1 production inpublic_surface.rs). Single PR.crates/uselesskey-core— 30 sites acrossseed.rs,sink.rs,cache.rs,factory.rs,negative/{pem,der}.rs. Test-cluster (test modules) is a clean PR; the small production-path subset (cache.rs:174,factory.rspanics) can be a follow-up PR that adds receipted allowlist entries.crates/uselesskey-jwk— 22 sites inshape.rs,kid.rs,builder.rs,tests/negative_fixtures.rs. Mix of test + production; split into test-migration PR and production-receipts PR.crates/uselesskey-token— 17 sites inshape.rsandtests/negative_fixtures.rs. Same split asuselesskey-jwk.Crates with 5 sites or fewer (
crates/uselesskey-x509,crates/uselesskey-core/src/srp/negative/*) can be cleaned up in a single mop-up PR after the big ones land.Acceptance criteria
cargo xtask check-no-panic-familyexits 0 withmode = "no-new-debt", i.e.0 new-debt, 0 stale, 0 expired.owneris notFIXME,explanationis concrete,expiresis set).policy/clippy-lints.tomlpanic_family_level = "deny"and[stage].current = "C".References
docs/NO_PANIC_POLICY.md— definition, stages, identity model, classifications, modes.policy/no-panic-allowlist.toml— receipted authoritative ledger.policy/no-panic-baseline.toml— snapshot, should burn down to zero.policy/clippy-lints.toml—panic_family_level = "warn",[stage]block (current = "A",description = "Warn-stage panic-family lints; semantic no-panic checker advisory; bare-allow blocking.").crates/uselesskey-test-support—ensure!,ensure_eq!,require_ok,require_some,TestResult.cargo xtask check-no-panic-family+cargo xtask no-panic proposeto refresh):target/no-panic.md,target/no-panic.json,target/policy-proposed/no-panic-proposed-allowlist.toml.Not in scope
This issue is the burndown plan and tracking ticket, not a code change. No files in this repository should be edited as part of resolving this issue — the PRs that execute the plan are the deliverables. In particular: do not run
cargo xtask no-panic baseline --resetto paper over the symptom; the entire point of the no-new-debt mode is to force the burndown.