Repository navigation
refactor(leaderboard): the #540 review: MemberDevices, DeviceRequest, typed labels and keys, ISO times - #1
Merged
LunarECL merged 5 commits intoOct 11, 2026
Conversation
…SO times (#540) From the review on #540: - DeviceLabel counts 1–40 in UTF-16 units, as the server's readLabel does, so a label with emoji isn't refused as 400 badLabel. - ServerTime reads only ISO 8601, the server's toISOString(); SQLite and Unix-time forms are refused rather than guessed. - MemberDevices owns which device joined, the first week and what was added since a device; Device.removed(by:at:) marks a removal. - Joining's steps carry their key, interval and summary, so the parallel optionals are gone; the public Joining is unchanged. - Device, DeviceRef and PendingDevice labels are DeviceLabel; removedBy and LeaderboardError.removed(by:) keep the DeviceRef. - design.md: §2 tree and a MemberDevices section, §2a's flow and joining states, §4 owners, §5 times and labels, §7 MachineIdentity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 tasks done
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
…cted wait (#540) The rest of the #540 review's point 3 and its small things: - DeviceKey names a device by the key it signs with: Device, DeviceRef, MemberDevices and shownDevices use it. Text only on the wire (the LeaderboardAPI calls, the JSON) and in settings.json, which is unchanged. - LeaderboardMembership takes the wait between a new device's asks as `sleep`, like `now`; a test pins that it waits the server's interval. - design.md: shownDevices as Set<DeviceKey>; DeviceKey in §7's pieces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Mac is either asking to be added to a member or a member's device, never both, so the request is its own aggregate, as device_requests is on the server, not a state of the membership (the #540 review's point 4). - DeviceRequest owns the code, the unsaved key, the wait at the server's interval, expiry and decline(); its stage is waiting, approved(member), joined, expired or declined. - LeaderboardMembership.requestToJoin(label:) answers the request, and is refused while this Mac is a member (alreadyJoined); join(through:sharing:) replaces confirmJoining. Joining and its JoiningState leave the membership. - design.md: the request in §2's tree and its own section, §2a's stages, §3's tells, §4's owner, §7's pieces; CANONICAL_MODEL's Leaderboard row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#540) - DeviceRequest.handOver() answers the member, the key and the member's /me answer and moves itself to joined, once; refused before approval. LeaderboardMembership.join(through:sharing:) checks its own rules, then tells it, instead of reading `approval` and setting `markJoined()`. - DeviceRequest's wait defaults to Task.sleep; the membership no longer carries a `sleep` it never used. - DeviceRequestTests builds the request directly: the wait, the member, expiry, decline and the hand-over. - design.md: DeviceRequest's laws and tells. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… internal device rules (#540) - LeaderboardMembership.removals(of:) answers the dialog's choices (Removal: keepingItsDays, deletingItsDays; none for a removed device), and remove(_:_:) takes one, refusing what it didn't offer (notOffered). It replaces offersDeletingDays(whenRemoving:) and the deletingDays flag. - MemberDevices' rules (joined, isInFirstWeek, added(since:), removing, adding, device(withKey:)) and Device.removed(by:at:) are internal: a view lists the devices and asks the membership, never the rules. - design.md: §2's tells and answers, §3's tells, §4's owner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Why
The review on #540 asked for the label count, the time form and three domain-design points. This is one way to do them, on top of your
4e14e21c, in five commits, so you can take it, change it, or close it. Nothing lands on your branch unless you merge this.What changed
DeviceLabelcounts 1–40 in UTF-16 units, as the server'sreadLabeldoes. A test with a family emoji (11 units) pins it.ServerTimereads only ISO 8601, with or without milliseconds (the server'stoISOString()). SQLite and Unix-time forms are refused. Your Export round-trip test now uses ISO inputs.MemberDevices(new) owns which device joined,isInFirstWeek(_:at:),added(since:),removing(_:by:at:)andadding(_:), each tested directly inMemberDevicesTests.Device.removed(by:at:)marks a removal. The membership asks it instead of comparing fields.Joining: a private
JoiningStatecarries the key, the interval and the approved summary in each step, replacingjoiningKey,joiningIntervalandapprovedSummary. The publicJoiningis unchanged, so no key reaches a view.Types:
Device,DeviceRefandPendingDevicelabels areDeviceLabel(a label the server would never send fails the answer).removedByandLeaderboardError.removed(by:)keep theDeviceRef.design.md:MemberDevicesandjoiningin the tree, and aMemberDevicessection.MachineIdentityin the diagram.DeviceKey(second commit) names a device by the key it signs with:Device,DeviceRef,MemberDevicesandshownDevicesuse it. It is text only on the wire (theLeaderboardAPIcalls, the JSON) and insettings.json, whose format is unchanged.The wait between a new device's asks is injected as
sleep, likenow. A test pins that it waits the server's interval between asks.DeviceRequest(third commit, the review's point 4). A design change, so the docs changed first, and I'd like your view on it. A Mac is either asking to be added to a member or a member's device, never both, so the request is its own aggregate, asdevice_requestsis on the server, not a state of the membership:DeviceRequestowns the code, the unsaved key, the wait at the server's interval, expiry anddecline(). Itsstageiswaiting,approved(member),joined,expiredordeclined.membership.requestToJoin(label:)answers the request, and is refused while this Mac is a member (alreadyJoined).membership.join(through:sharing:)replacesconfirmJoining.joiningandJoiningStateleave the membership.design.md: §2's tree and aDeviceRequestsection, §2a's stages, §3's tells, §4's owner and §7's pieces. AlsoCANONICAL_MODEL's Leaderboard row.join(through:)checks its own rules, then tells the requesthandOver(), which moves the request tojoineditself. The request owns its wait (defaultTask.sleep), so the membership no longer carries one.DeviceRequestTestsbuilds the request directly.Abilities, not rules (fifth commit).
membership.removals(of:)answers the dialog's choices (Removal:keepingItsDays,deletingItsDays; none for a removed device), andremove(_:_:)takes one, refusing what it didn't offer (notOffered). It replacesoffersDeletingDays(whenRemoving:)plus thedeletingDaysflag, so a view picks from what the domain offers instead of asking and passing a Bool.MemberDevices' rules andDevice.removed(by:at:)are internal: a view lists devices and asks the membership.How it was verified
swift test --filter LeaderboardTests: 193 tests passed.xcodebuild test -scheme ClaudeBaron macOS:TEST SUCCEEDED.check-docs --strictpasses.🤖 Generated with Claude Code