feat(phase-24): bug fixes & test infrastructure#361
Conversation
Entire-Checkpoint: 0e18909127ac
Entire-Checkpoint: 91206e852405
Entire-Checkpoint: ec369f57c490
Entire-Checkpoint: 720d72c3e359
Entire-Checkpoint: 910fde1575a2
…t-pool - Add createSdkContext() that builds SdkContext with axios 401 interceptor - Add reAuthenticate() helper for /auth/test-login token refresh - Shared refreshPromise coalesces concurrent 401 re-auth requests - Include THROTTLE_BYPASS header support for rate limit bypass
- loadBin now auto-repairs when resolveIpnsRecord returns null by publishing empty bin metadata with sequenceNumber 1 (was silently returning 0) - saveBinMetadata uses publishWithVerify for all bin IPNS publishes with resolve-back verification and exponential backoff retry (3 attempts) - Updated tests: auto-repair returns sequenceNumber 1 and calls publish - New tests: publishWithVerify retries on verify failure, auto-repair calls saveBinMetadata which uses publishWithVerify Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add sdk-core-workload.ts with IPNS publish, upload pipeline, folder read workloads - Add sdk-upload-pipeline.test.ts measuring encrypt + pin + IPNS publish latency - Add sdk-ipns-contention.test.ts measuring IPNS publish/resolve under concurrent load - Add sdk-folder-read.test.ts measuring IPNS resolve + IPFS fetch + decrypt latency - All scenarios use sdk-core directly without CipherBoxClient overhead - All scenarios use MetricsCollector + expectThresholdsPassed harness
- Remove export file recovery mode (radio buttons, file upload, JSON paste) - Consolidate 4-step flow into 2-step flow (Setup + Recover) - Move gateway inputs into Step 1 alongside private key input - Remove dead CSS classes (recovery-method, method-panel, migrated-notice, or-divider, file input styles) - Remove export-related JavaScript (parseExportFile, file upload handlers, method toggle) - Add data-testid attributes for Playwright E2E testing - Preserve all v2 blob recovery logic unchanged Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add 24-02-SUMMARY.md with execution results and deviation documentation - Update STATE.md with plan progress and metrics - Update ROADMAP.md with phase 24 plan progress - Mark TEST-01 and TEST-03 requirements complete
- Create recovery.spec.ts that seeds a real vault with an uploaded file via SDK test harness - Navigate to recovery.html, enter private key and localhost gateway URLs - Verify progress log shows the uploaded file name after recovery completes - Verify download button becomes visible on successful recovery - Proper cleanup: destroy client, zero key material, delete test account - Add @cipherbox/sdk to web-e2e devDependencies for test harness import Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ipHash
- validateDeviceRegistry now accepts v1 and v2: v1 migrated to v2 with
lenient ipHash (empty string -> zero placeholder), v2 validated strictly
- DeviceRegistryVersion type added ('v1' | 'v2') and exported from barrels
- useAuth.ts computes SHA-256 hash of '0.0.0.0' for ipHash instead of ''
- device-registry.service.ts creates new registries as v2 from the start
- METADATA_SCHEMAS.md updated with v2 version history per evolution protocol
- Tests: v1 empty ipHash migration, v1 valid ipHash preservation, v2 strict
validation rejects empty/invalid, round-trip tests updated for v2 output
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 5ccd2201275a
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 6e43032c9a7f
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: b0d0a05f9718
Entire-Checkpoint: d3b097785131
Entire-Checkpoint: 46963b24784e
- publishWithVerify: publish once, retry only verification (was re-publishing on every attempt) - useAuth ipHash: use existing computeIpHash() instead of inline SHA-256 - registry schema: extract validateRegistryShape() to deduplicate v1/v2 validation - load test workload: clear plaintext buffer after upload (security hygiene) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 37a5c20d356d
…-block access Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 99ffacf26a3a
|
Warning Rate limit exceeded
⌛ 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: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (25)
WalkthroughPhase 24 marks completion of bug fixes and test-infrastructure work: adds bin IPNS publish+verify and auto-repair, introduces device registry v1→v2 validation/migration and exports, adds headless sdk-core load workloads with 401 refresh handling, simplifies the web recovery UI to IPFS-direct v2, and adds Playwright E2E and Vitest load tests. Changes
Sequence Diagram(s)sequenceDiagram
participant LoadTest as Load Test Scenario
participant PoolClient as Pool Client
participant SdkContext as SDK Context
participant SdkCore as SDK Core
participant IpnsGateway as IPNS Gateway
LoadTest->>PoolClient: createClientPool()
PoolClient->>SdkContext: createSdkContext(pc)
SdkContext->>SdkCore: invoke sdk-core APIs (with dynamic token)
loop workload cycles
SdkCore->>IpnsGateway: createAndPublishIpnsRecord(...)
IpnsGateway-->>SdkCore: publish ack
SdkCore->>IpnsGateway: resolveIpnsRecord(ipnsName)
alt resolve succeeds
SdkCore-->>PoolClient: measure success
else resolve fails
SdkCore->>IpnsGateway: retry resolve (backoff) [publishWithVerify]
SdkCore-->>PoolClient: measure eventual outcome
end
end
LoadTest->>PoolClient: aggregateAndReport()
sequenceDiagram
participant App as Web App
participant DevRegistry as Device Registry Service
participant Core as Core Registry Schema
participant Ipns as IPNS Storage
App->>DevRegistry: initializeOrSyncRegistry()
DevRegistry->>Ipns: try resolve registry via IPNS
alt registry missing
DevRegistry->>Core: create initial registry (version: 'v2')
DevRegistry->>Ipns: encrypt+publish registry
else registry found (v1)
DevRegistry->>Core: validateDeviceRegistry(v1)
Core-->>DevRegistry: migrate to v2 (empty ipHash -> zero placeholder)
end
App->>DevRegistry: register device with computed ipHash
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
TypeScript doesn't propagate type narrowing across function boundaries, so obj.devices remains 'unknown' after validateRegistryShape validates it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: a3348518d54f
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
apps/web/public/recovery.html (1)
1102-1104: Consider clearer error message for base64 fallback removal.The nested try/catch for base64 parsing appears vestigial since the subtitle now states "Private key (hex)" and the regex on line 1099 only matches hex. The base64 fallback silently fails and shows a generic error message.
Since the UI explicitly requests hex input, consider simplifying to hex-only parsing with a clearer error:
♻️ Suggested simplification
- if (/^(0x)?[0-9a-fA-F]{64}$/.test(keyText)) { - privateKeyBytes = hexToBytes(keyText); - } else { - try { privateKeyBytes = hexToBytes(keyText); } - catch { try { privateKeyBytes = base64ToBytes(keyText); } - catch { showError(1, 'Could not parse private key. Provide 32 bytes as 64 hex characters.'); return; } } - } + try { + privateKeyBytes = hexToBytes(keyText); + } catch { + showError(1, 'Invalid private key format. Provide 32 bytes as 64 hex characters (with optional 0x prefix).'); + return; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/public/recovery.html` around lines 1102 - 1104, Remove the vestigial base64 fallback and make the error explicit: replace the nested try/catch that attempts base64ToBytes with a single attempt to parse hex via hexToBytes (operating on keyText -> privateKeyBytes) and on failure call showError with a clear message like "Could not parse private key: expected 32 bytes as 64 hex characters (hex only)". Ensure references to hexToBytes, privateKeyBytes, keyText and showError are updated so only hex parsing is attempted.tests/web-e2e/tests/recovery.spec.ts (1)
63-70: Consider using environment variables for gateway URLs.The gateway URLs are hardcoded to
http://localhost:8080. Per the Playwright config context (snippet 2), other tests may use different gateway configurations. Consider extracting these to environment variables for consistency:+const IPFS_GATEWAY = process.env.IPFS_GATEWAY ?? 'http://localhost:8080'; +const IPNS_GATEWAY = process.env.IPNS_GATEWAY ?? 'http://localhost:8080'; + // Set gateway URLs to localhost (Kubo + API) await page.locator('[data-testid="recovery-ipfs-gateway"]').clear(); -await page.locator('[data-testid="recovery-ipfs-gateway"]').fill('http://localhost:8080'); +await page.locator('[data-testid="recovery-ipfs-gateway"]').fill(IPFS_GATEWAY); await page.locator('[data-testid="recovery-ipns-gateway"]').clear(); -await page.locator('[data-testid="recovery-ipns-gateway"]').fill('http://localhost:8080'); +await page.locator('[data-testid="recovery-ipns-gateway"]').fill(IPNS_GATEWAY);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/web-e2e/tests/recovery.spec.ts` around lines 63 - 70, Replace the hardcoded gateway URLs with environment-driven values: read RECOVERY_IPFS_GATEWAY and RECOVERY_IPNS_GATEWAY (with a fallback to 'http://localhost:8080') and use those when filling the page.locator('[data-testid="recovery-ipfs-gateway"]') and page.locator('[data-testid="recovery-ipns-gateway"]') fields in recovery.spec.ts so the test respects Playwright/test config or CI-provided overrides.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/__tests__/registry.test.ts`:
- Around line 99-110: The test's round-trip currently only asserts version,
device count, one deviceId and sequenceNumber; update it to assert the entire
devices array and other top-level fields are preserved during v1->v2 migration:
after calling encryptRegistry/encryptRegistry (encryptRegistry, decryptRegistry,
generateTestKeypair, createTestRegistry), assert decrypted.version === 'v2' but
assert decrypted.devices deep-equals registry.devices (including publicKey,
status, timestamps, ipHash and any other device fields) and that all other
top-level fields (e.g., sequenceNumber and any metadata) match the original
registry; this ensures only version is changed and no device data is lost.
In `@packages/core/src/registry/schema.ts`:
- Around line 74-80: The current mapping for devices aggressively replaces any
malformed ipHash with the zero placeholder; limit the auto-heal to only the
legacy empty-string case: inside the devices mapping (the code that calls
validateDeviceEntryBase and computes ipHash on each DeviceEntry), if d.ipHash
=== '' set ipHash to '0'.repeat(64); else require ipHash to be 64 hex chars
(HEX_REGEX and length check) and if it fails throw/propagate a validation error
(do not silently replace). Keep validateDeviceEntryBase call and preserve the
DeviceEntry spread logic, but change the conditional logic that sets ipHash
accordingly.
In `@packages/sdk/src/bin/index.ts`:
- Around line 188-202: The auto-repair path publishes an empty bin whenever
loadBinMetadataInternal returns null, which can conflate true "IPNS not found"
with transient failures (e.g., BAD_GATEWAY) from resolveIpnsRecord and cause
data loss; change the logic so loadBinMetadataInternal (or resolveIpnsRecord)
returns a distinguishable result or throws a typed error for transient failures,
and only call deriveBinIpnsKeypair + saveBinMetadata to create an empty
RecycleBinMetadata (using BIN_METADATA_VERSION and sequenceNumber:1) when you
have a definitive "not found" result; for transient/errors propagate the error
(or abort the auto-repair) and emit a clear warning log mentioning the operation
(loadBinMetadataInternal / resolveIpnsRecord) so callers can retry instead of
silently overwriting existing bin data.
---
Nitpick comments:
In `@apps/web/public/recovery.html`:
- Around line 1102-1104: Remove the vestigial base64 fallback and make the error
explicit: replace the nested try/catch that attempts base64ToBytes with a single
attempt to parse hex via hexToBytes (operating on keyText -> privateKeyBytes)
and on failure call showError with a clear message like "Could not parse private
key: expected 32 bytes as 64 hex characters (hex only)". Ensure references to
hexToBytes, privateKeyBytes, keyText and showError are updated so only hex
parsing is attempted.
In `@tests/web-e2e/tests/recovery.spec.ts`:
- Around line 63-70: Replace the hardcoded gateway URLs with environment-driven
values: read RECOVERY_IPFS_GATEWAY and RECOVERY_IPNS_GATEWAY (with a fallback to
'http://localhost:8080') and use those when filling the
page.locator('[data-testid="recovery-ipfs-gateway"]') and
page.locator('[data-testid="recovery-ipns-gateway"]') fields in recovery.spec.ts
so the test respects Playwright/test config or CI-provided overrides.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a6ceaa05-9674-4c20-8d38-216822995159
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!**/pnpm-lock.yaml
📒 Files selected for processing (33)
.planning/PROJECT.md.planning/REQUIREMENTS.md.planning/ROADMAP.md.planning/STATE.md.planning/phases/24-bug-fixes-test-infrastructure/24-01-PLAN.md.planning/phases/24-bug-fixes-test-infrastructure/24-01-SUMMARY.md.planning/phases/24-bug-fixes-test-infrastructure/24-02-PLAN.md.planning/phases/24-bug-fixes-test-infrastructure/24-02-SUMMARY.md.planning/phases/24-bug-fixes-test-infrastructure/24-03-PLAN.md.planning/phases/24-bug-fixes-test-infrastructure/24-03-SUMMARY.md.planning/phases/24-bug-fixes-test-infrastructure/24-CONTEXT.md.planning/phases/24-bug-fixes-test-infrastructure/24-RESEARCH.md.planning/phases/24-bug-fixes-test-infrastructure/24-VALIDATION.md.planning/phases/24-bug-fixes-test-infrastructure/24-VERIFICATION.mdapps/web/public/recovery.htmlapps/web/src/hooks/useAuth.tsapps/web/src/services/device-registry.service.tsdocs/METADATA_SCHEMAS.mdpackages/core/src/__tests__/registry.test.tspackages/core/src/index.tspackages/core/src/registry/index.tspackages/core/src/registry/schema.tspackages/core/src/registry/types.tspackages/sdk/src/__tests__/bin.test.tspackages/sdk/src/bin/index.tstests/load/src/harness/client-pool.tstests/load/src/scenarios/sdk-folder-read.test.tstests/load/src/scenarios/sdk-ipns-contention.test.tstests/load/src/scenarios/sdk-upload-pipeline.test.tstests/load/src/workloads/byo-file-workload.tstests/load/src/workloads/sdk-core-workload.tstests/web-e2e/package.jsontests/web-e2e/tests/recovery.spec.ts
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #361 +/- ##
===========================================
+ Coverage 59.45% 69.56% +10.10%
===========================================
Files 126 105 -21
Lines 9213 6243 -2970
Branches 894 894
===========================================
- Hits 5478 4343 -1135
+ Misses 3530 1695 -1835
Partials 205 205
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
- Strengthen registry round-trip test assertions (toEqual full devices array) - Tighten v1->v2 migration: only auto-heal empty ipHash, reject other malformed values - Add warning log when bin auto-repair triggers for observability Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: ce99f1a94aea
There was a problem hiding this comment.
Pull request overview
This PR addresses two user-facing bugs (bin IPNS 404s and device registry format errors) while expanding the test infrastructure with headless sdk-core load scenarios and a Playwright E2E test for the simplified vault recovery tool.
Changes:
- Add bin IPNS auto-repair on missing record and introduce a publish-with-verify retry pattern, with updated unit tests.
- Introduce device registry v2 schema with lenient v1 migration + strict v2 validation; ensure new registries write v2 and
ipHashis always populated. - Add headless sdk-core load-test workloads/scenarios and a recovery tool E2E test; simplify
recovery.htmlto IPFS-direct-only flow.
Reviewed changes
Copilot reviewed 32 out of 34 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/web-e2e/tests/recovery.spec.ts | New Playwright E2E that seeds a vault via SDK harness and validates recovery.html output. |
| tests/web-e2e/package.json | Adds @cipherbox/sdk to support importing the SDK E2E harness. |
| tests/load/src/workloads/sdk-core-workload.ts | New headless workload helpers calling @cipherbox/sdk-core directly. |
| tests/load/src/workloads/byo-file-workload.ts | Fixes workload variable scoping for cid/pinSize. |
| tests/load/src/scenarios/sdk-upload-pipeline.test.ts | New headless upload pipeline load scenario with thresholds. |
| tests/load/src/scenarios/sdk-ipns-contention.test.ts | New headless IPNS publish/resolve contention scenario with thresholds. |
| tests/load/src/scenarios/sdk-folder-read.test.ts | New headless folder read-path scenario with thresholds. |
| tests/load/src/harness/client-pool.ts | Adds createSdkContext() + reactive 401 refresh via /auth/test-login with refresh coalescing. |
| pnpm-lock.yaml | Lockfile update for new workspace dependency. |
| packages/sdk/src/bin/index.ts | Adds publishWithVerify() and auto-repair path in loadBin(). |
| packages/sdk/src/tests/bin.test.ts | Updates/adds unit tests for auto-repair + verify retry behavior. |
| packages/core/src/registry/types.ts | Introduces DeviceRegistryVersion and updates registry typing for v1/v2. |
| packages/core/src/registry/schema.ts | Adds v1→v2 migration + strict v2 validation logic. |
| packages/core/src/registry/index.ts | Re-exports DeviceRegistryVersion. |
| packages/core/src/index.ts | Re-exports DeviceRegistryVersion from the top-level barrel. |
| packages/core/src/tests/registry.test.ts | Adds tests covering migration, strict v2 validation, and version handling. |
| docs/METADATA_SCHEMAS.md | Updates DeviceRegistry docs to v1/v2 and documents version history. |
| apps/web/src/services/device-registry.service.ts | Ensures newly created registries use version: 'v2'. |
| apps/web/src/hooks/useAuth.ts | Computes and supplies an ipHash instead of an empty string. |
| apps/web/public/recovery.html | Removes export-based recovery; consolidates to 2-step IPFS-direct recovery flow + testids. |
| .planning/phases/24-bug-fixes-test-infrastructure/24-VERIFICATION.md | Adds Phase 24 verification report. |
| .planning/phases/24-bug-fixes-test-infrastructure/24-VALIDATION.md | Adds Phase 24 validation strategy doc. |
| .planning/phases/24-bug-fixes-test-infrastructure/24-RESEARCH.md | Adds Phase 24 research notes and rationale. |
| .planning/phases/24-bug-fixes-test-infrastructure/24-CONTEXT.md | Adds Phase 24 context and decisions. |
| .planning/phases/24-bug-fixes-test-infrastructure/24-03-SUMMARY.md | Plan 03 execution summary (recovery tool + E2E). |
| .planning/phases/24-bug-fixes-test-infrastructure/24-03-PLAN.md | Plan 03 execution plan document. |
| .planning/phases/24-bug-fixes-test-infrastructure/24-02-SUMMARY.md | Plan 02 execution summary (headless load tests + auth refresh). |
| .planning/phases/24-bug-fixes-test-infrastructure/24-02-PLAN.md | Plan 02 execution plan document. |
| .planning/phases/24-bug-fixes-test-infrastructure/24-01-SUMMARY.md | Plan 01 execution summary (bin + registry fixes). |
| .planning/phases/24-bug-fixes-test-infrastructure/24-01-PLAN.md | Plan 01 execution plan document. |
| .planning/STATE.md | Updates current project state to Phase 24 execution progress. |
| .planning/ROADMAP.md | Marks Phase 24 complete and adds Phase 24 plan references. |
| .planning/REQUIREMENTS.md | Marks Phase 24 requirements as completed. |
| .planning/PROJECT.md | Updates “Last updated” text to reflect Phase 24 completion. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
loadBin now auto-repairs by publishing an empty bin (sequenceNumber: 1) instead of returning in-memory-only state (sequenceNumber: 0). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 2bfacfc945f8
- Remove vestigial base64 key parsing from recovery.html (hex-only for v2) - Use `string | undefined` instead of definite assignment for cid/pinSize - Mark phase 24 plan checkboxes as complete in ROADMAP.md - Fix phase 24 progress table column alignment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 321fdd002bac
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/sdk/src/bin/index.ts`:
- Around line 188-206: The integration test assertion for the auto-repair
behavior expects sequenceNumber 0 but the code (auto-publishing an empty bin via
deriveBinIpnsKeypair + saveBinMetadata which creates RecycleBinMetadata with
sequenceNumber: 1 and BIN_METADATA_VERSION) returns sequenceNumber 1; update the
integration test to assert expect(binState.sequenceNumber).toBe(1) so it matches
the implemented auto-repair behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 974d4c3a-10e0-4b67-82f7-916df753e8b6
📒 Files selected for processing (4)
packages/core/src/__tests__/registry.test.tspackages/core/src/registry/schema.tspackages/sdk/src/bin/index.tstests/sdk-e2e/src/suites/bin-operations.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/core/src/tests/registry.test.ts
…es-test-infrastructure # Conflicts: # .planning/PROJECT.md # .planning/REQUIREMENTS.md # .planning/ROADMAP.md # .planning/STATE.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: ad04870d8620
Summary
loadBinnow auto-repairs missing IPNS records viapublishWithVerify(publish + resolve-back verification with exponential backoff)ipHashwith zero placeholder), strict v2 validation, and SHA-256 ipHash computation inuseAuth/simplifyreview fixed: publish-once-verify-retry pattern,computeIpHashreuse,validateRegistryShapededup, buffer cleanup, pre-existing type error inbyo-file-workload.tsTest plan
@cipherbox/core— 171/171 tests pass (including 8 new registry v1→v2 migration tests)@cipherbox/sdk— 69/69 unit tests pass (including 4 new bin auto-repair + publishWithVerify retry tests)@cipherbox/crypto— 138/138 tests pass@cipherbox/sdk-core— 88/88 tests passrecovery.spec.tsE2E — requires API + IPFS running locally🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Documentation