perf/resilience: reduce OTP polling latency and add runtime resilience gates#493
Merged
Merged
Conversation
- Fix HidEnumerationTests: correct RequiresHardware trait, replace broken DeviceId prefix filter with SupportsConnection, strengthen trivially-true assertions, show Windows advisory only when 0 devices - Fix HidDeviceListenerDisposalTests: narrow catch(Exception) to catch(PlatformNotSupportedException), remove duplicate platform implementation test, add state assertions to idempotent tests - Fix HidDeviceListenerIntegrationTests: narrow exception handling, fix Create_StatusIsStarted (never called Start), start listener before testing for spurious events and disposal, remove duplicate Dispose_MultipleTimes test - Add HidInterfaceClassifierTests: 9 unit tests covering Classify, IsSupported, and GetReportType for FIDO, OTP, and Unknown types Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Base automatically changed from
codex-core-layout-refactor
to
yubikit-composite-device-new
June 25, 2026 08:53
feat: Use full PIV application identifier
Co-authored-by: Dennis Dyallo <DennisDyallo@users.noreply.github.com>
feat: Align CTAP status values with CTAP 2.2
SetFeatureReport and SetOutputReport were throwing InvalidOperationException for caller-supplied buffers of the wrong length, which misrepresents the failure as object state rather than invalid input. Switch to ArgumentException and include expected vs actual lengths for actionable diagnostics. Co-authored-by: Dennis Dyallo <DennisDyallo@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a “runtime resilience” workflow for the SDK (primarily Core) by fixing a major OTP HID latency regression, hardening SmartCard listener retry/lifecycle behavior, and adding fast no-hardware gates (plus supporting benchmarks/docs) to prevent these bug classes from returning.
Changes:
- Add a new
dotnet toolchain.cs -- resilience --fasttarget that runsCategory=RuntimeResiliencegates in Core and wire it into CI. - Fix OTP HID write-ready polling to check readiness immediately and only poll while busy; add a no-hardware regression guard.
- Add SmartCard listener resilience seams/tests (fault injection, backoff boundedness, context lifecycle invariants) plus a lightweight static scanner gate.
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| toolchain.cs | Adds resilience target and --fast flag wiring for fast runtime-resilience gates. |
| src/WebAuthn/src/Extensions/PreviewSign/PreviewSignErrors.cs | Updates CTAP error mapping for PuatRequired and adds PinTokenExpired mapping. |
| src/WebAuthn/src/Client/WebAuthnClient.cs | Updates CTAP→WebAuthn error mapping to include PuatRequired/PinTokenExpired. |
| src/Fido2/tests/Yubico.YubiKit.Fido2.UnitTests/CtapExceptionTests.cs | Adds CTAP 2.2 status value/message assertions. |
| src/Fido2/tests/Yubico.YubiKit.Fido2.IntegrationTests/FidoMakeCredentialTests.cs | Updates PIN/UV-required integration test naming and expected status handling. |
| src/Fido2/src/Ctap/CtapStatus.cs | Renames/adds CTAP status codes (PuatRequired, PinTokenExpired) and updates spec references. |
| src/Fido2/src/Ctap/CtapException.cs | Updates exception message mapping for new/renamed CTAP statuses. |
| src/Core/tests/Yubico.YubiKit.Core.UnitTests/Transports/SmartCard/DesktopSmartCardDeviceListenerSCardErrorTests.cs | New no-hardware SmartCard fault-injection tests for backoff/recovery and context disposal invariants. |
| src/Core/tests/Yubico.YubiKit.Core.UnitTests/RuntimeResilience/RuntimeResilienceStaticScanTests.cs | Adds static “risky loop shape” scanner tests plus current-source smoke scan. |
| src/Core/tests/Yubico.YubiKit.Core.UnitTests/Protocols/Otp/Hid/OtpHidProtocolTests.cs | Adds runtime-resilience timing guard for sleep-first OTP regression. |
| src/Core/src/Transports/SmartCard/ISCardApi.cs | Introduces injectable PC/SC seam (ISCardApi + NativeSCardApi) for deterministic tests. |
| src/Core/src/Transports/SmartCard/DesktopSmartCardDeviceListener.cs | Refactors listener to use seam, adds failure handling/backoff/recovery, and improves lifecycle cleanup. |
| src/Core/src/Sessions/ApplicationIds.cs | Updates PIV AID constant to full form. |
| src/Core/src/Protocols/Otp/Hid/OtpHidProtocol.cs | Fixes OTP HID ready-to-write polling (immediate check + busy-only polling). |
| src/Core/CLAUDE.md | Documents listener/native retry-loop resilience expectations and recommended gate command. |
| docs/plans/runtime-resilience-harness.md | Adds a phased plan for resilience gates/diagnostics workflow. |
| docs/learnings/runtime-resilience/README.md | Adds index for runtime-resilience phase learnings. |
| docs/learnings/runtime-resilience/phase-01-smartcard-listener.md | Phase 1 closeout: SmartCard listener recovery learnings. |
| docs/learnings/runtime-resilience/phase-02-otp-polling.md | Phase 2 closeout: OTP polling regression learnings. |
| docs/learnings/runtime-resilience/phase-03-static-scanner.md | Phase 3 closeout: static scanner learnings. |
| docs/learnings/runtime-resilience/phase-04-smartcard-context-leak.md | Phase 4 closeout: SmartCard context leak invariant learnings. |
| docs/learnings/runtime-resilience/phase-05-minimal-fast-runner.md | Phase 5 closeout: minimal runner learnings. |
| docs/learnings/runtime-resilience/phase-06-diagnostics-project-deferred.md | Phase 6 closeout: diagnostics project deferral rationale. |
| docs/learnings/runtime-resilience/phase-07-audit-skill-deferred.md | Phase 7 closeout: audit skill deferral rationale. |
| Directory.Packages.props | Adds central package version for BenchmarkDotNet. |
| benchmarks/Yubico.YubiKit.PerformanceBenchmarks/Yubico.YubiKit.PerformanceBenchmarks.csproj | New BenchmarkDotNet harness project definition. |
| benchmarks/Yubico.YubiKit.PerformanceBenchmarks/Program.cs | Implements discovery/management benchmarks over SmartCard/FIDO/OTP. |
| AGENTS.md | Adds guidance to run resilience gates when touching Core runtime loops. |
| .github/workflows/build.yml | Adds CI step for dotnet toolchain.cs -- resilience --fast. |
feat: Add Windows HID transport
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Category=RuntimeResiliencegates and a fast local/CI runner:dotnet toolchain.cs -- resilience --fast.Why This Expanded
The original benchmark work found and fixed a large OTP latency defect. Follow-up profiling showed no other obvious 10x foreground performance target, but it exposed a more important adjacent class: runtime resilience and flakiness risks around native listener loops, stale handles, retry storms, and lifecycle cleanup.
This PR now turns that discovery into a repeatable agent/human workflow: find a real or seeded bug class, add a deterministic no-hardware invariant, red-green prove the detector, review proof quality, document learnings, commit the phase, and only then move on.
Benchmark Findings
OTP HID after the polling fix:
CreateManagementSessionOverOtpHid: ~1.039 s to ~28.0 ms, about 37x faster.GetDeviceInfoOverOtpHid: ~2.075 s to ~55.9 ms, about 37x faster.Other profiled paths after cleanup:
scdaemon: connect ~6.1 ms, select management ~7.2 ms, get info ~21.1 ms.Conclusion: BenchmarkDotNet remains the discovery tool. Confirmed hotspots get converted into cheap deterministic gates rather than requiring BDN in normal CI.
Runtime Resilience Gates
Added a fast no-hardware runtime resilience suite covering:
SCardGetStatusChange/SCardListReadersfailures.Start(); Stop(); Start(); Stop(); Dispose();.The suite is selected with
Category=RuntimeResilienceand runs through:AI/Human Collaboration Workflow
This SDK is expected to be developed heavily by AI agents and humans together, so the work optimizes for shared executable context:
dotnet toolchain.cs -- resilience --fast.docs/learnings/runtime-resilience/explaining what was found, how it was found, what worked, what failed, what review changed, and what remains risky.docs/plans/runtime-resilience-harness.md: implement, verify, review when relevant, write learnings, commit, then continue.CI Integration
The main workflow now includes a dedicated resilience step after unit tests and before packing:
This keeps resilience failures visible as their own CI signal instead of hiding them inside generic unit-test output.
Red-Green Proof
The fast runner was deliberately tested against seeded regressions:
dotnet toolchain.cs -- resilience --fastfail with both static scanner and timing-guard failures.Phase Decisions
Verification
Local verification before updating this PR:
dotnet toolchain.cs -- resilience --fastdotnet toolchain.cs -- test --project Core --filter "Category!=RequiresUserPresence"dotnet format --verify-no-changesTests.TestProjectIL2026/IL3050warnings remain unrelated.Earlier verification also included:
dotnet toolchain.cs -- build --project Coredotnet toolchain.cs -- pack --package-version 2.0.0-preview.perf.1Notes
.gitignoreanddocs/plans/composite-device/rust-csharp-composite-comparison.mdremain unrelated local worktree changes and are not part of this PR.