fix: guard buddy_hatch + repair duplicate companion rows#140
fix: guard buddy_hatch + repair duplicate companion rows#140TheMichaelFeng wants to merge 1 commit into
Conversation
|
hi @TheMichaelFeng thank you for the submission! based on the review from gpt5.5, This doesn’t fully fix the reported bug. SELECT ... LIMIT 1 removes only one companion, but affected databases may already contain several rows. After deleting A and inserting C, B remains and may still be returned by LIMIT 1. Could you address these issues: |
The earlier delete-before-insert approach was incomplete on two fronts, both raised in review: 1. It only removed ONE existing row (SELECT ... LIMIT 1), so databases already carrying several companions (from the original bare-INSERT hatch bug) could still surface a stale row after hatching. 2. It made buddy_hatch silently destructive. Onboarding rescues a buddy and then tells the user to hatch; following that instruction wiped the rescued buddy and its history. This replaces that approach: - buddy_hatch now refuses when a companion already exists, directing the user to buddy_respawn first. Hatching is never silently destructive; releasing a buddy stays an explicit, separate action. - repairDuplicateCompanions() runs as a startup migration in initDb() and collapses ALL duplicate rows down to one canonical buddy — the most-progressed (highest-XP) row, ties broken toward the earliest. Legacy child rows are cleared before their parent (the legacy tables have no ON DELETE CASCADE) and the whole collapse runs in a single transaction so a crash mid-repair can't leave the DB half-collapsed. - Tests cover multi-row repair (survivor selection, orphan cleanup, tie breaking, initDb integration) and rescue-then-hatch (the rescued buddy survives; hatch is refused while it exists).
e4a69a0 to
793c6f5
Compare
|
I have reworked it and pushed to the same branch: 1: 2: Legacy dupes are handled separately. A new 3: The collapse is transactional. It runs inside 4: Added tests for the multi-row repair (survivor selection, orphan cleanup, tie-breaking, The PR description has the full rundown. Heads-up: there are bout 9 pre-existing test failures on |
|
Thanks for the detailed review; you were right on both counts. The first
pass was a band-aid. I've reworked it and force-pushed to the same branch.
What changed:
1. buddy_hatch no longer replaces silently. It now refuses when a companion
already exists and points the user to buddy_respawn first, so the
rescue-then-hatch onboarding flow can't wipe a rescued buddy.
2. Legacy duplicates are handled separately: a new
repairDuplicateCompanions()
startup migration in initDb() collapses ALL rows (not just one) down to
the
Most progressed buddy, highest XP, ties toward the earliest row.
3. That collapse runs in a single transaction, clearing child rows before
the
parent since the legacy tables have no ON DELETE CASCADE.
4. Added tests for the multi-row repair (survivor selection, orphan cleanup,
tie-breaking, initDb integration) and rescue-then-hatch behavior.
One heads-up: there are about 9 pre-existing test failures on master
(Penguin
animation width, doctor, a couple of reasoning detectors) that are
unrelated to
this change, I confirmed they fail on a clean master too. Glad to open a
separate issue/PR for those if it'd help.
The PR description is updated with the full rundown. Let me know if you'd
like anything adjusted.
Thanks,
Michael
…On Thu, 2 Jul 2026 at 05:14, Steven(Jieli) Wu ***@***.***> wrote:
*fiorastudio* left a comment (fiorastudio/buddy#140)
<#140 (comment)>
hi @TheMichaelFeng <https://github.com/TheMichaelFeng> thank you for the
submission! based on the review from gpt5.5,
This doesn’t fully fix the reported bug. SELECT ... LIMIT 1 removes only
one companion, but affected databases may already contain several rows.
After deleting A and inserting C, B remains and may still be returned by
LIMIT 1.
It also makes buddy_hatch silently destructive. After onboarding rescues a
buddy, the installer tells users to hatch one; following that instruction
would now delete the rescued buddy and its data.
Could you address these issues:
1/Prevent buddy_hatch from replacing an existing buddy without explicit
confirmation/buddy_respawn.
2/Repair legacy duplicate rows separately.
3/Wrap any replacement cleanup and insertion in a transaction.
4/Add tests for multiple existing rows and rescue-then-hatch behavior.
—
Reply to this email directly, view it on GitHub
<#140?email_source=notifications&email_token=BNNZVQ7I5JVXWGLF47ZIG535CV5LJA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIOBWGAYDSMZYGQ2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-4860093844>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BNNZVQ5H5FEZZEMOOY6JV6T5CV5LJAVCNFSNUABGKJSXA33TNF2G64TZHMYTEMBWGE4DMOJWHE5US43TOVSTWNBXG4ZTCMZUGMZTRILWAI>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/BNNZVQZ2TWFFYYYWBZMYJA35CV5LJA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIOBWGAYDSMZYGQ2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJKTGN5XXIZLSL5UW64Y>
and Android
<https://github.com/notifications/mobile/android/BNNZVQ4PYRTSA5UGIWCQOHL5CV5LJA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIOBWGAYDSMZYGQ2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>.
Download it today!
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
The bug
buddy_hatchdid a bare INSERT, and every read path usesSELECT ... FROM companions LIMIT 1with noORDER BY. So after hatching, SQLite could keep surfacing an older row, the new companion looked like it didn't save.Approach (reworked after review feedback)
The first pass (delete-before-insert) was incomplete: it only removed one row, and it made
buddy_hatchsilently destructive toward rescued buddies. This replaces it.buddy_hatchrefuses to overwrite. If a companion already exists, it returns early and tells the user tobuddy_respawnfirst. Onboarding's rescue-then-hatch flow can no longer destroy a rescued buddy. Releasing stays an explicit, separate action.Legacy duplicates are repaired separately. New
repairDuplicateCompanions()runs as a startup migration ininitDb(). It collapses all duplicate rows (not just one) down to the most-progressed buddy, highest XP, ties broken toward the earliest row (the one the buggyLIMIT 1read was already surfacing).The collapse is transactional. It runs inside
db.transaction(), clearing legacy child rows before their parent (those tables predateON DELETE CASCADE), so a crash mid-repair can't leave a half-collapsed DB.Tests
New
hatch-guard.test.tscovers repair (survivor selection, orphan cleanup, tie-breaking,initDb()integration, no-op at 0/1 rows) and rescue-then-hatch (the rescued buddy survives; hatch is refused while it exists).Note
~9 tests fail on
masterunrelated to this change (Penguin animation width, doctor, reasoning detectors), verified pre-existing by stashing this diff and re-running.