Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8f9d508
fix(transactions): prevent nested transaction from destroying outer E…
smarcet Apr 20, 2026
1664a15
fix(transactions): harden root/nested split against phantom writes an…
smarcet Jul 10, 2026
210f2cd
fix(tx): remove DBAL savepoints, propagate native isRollbackOnly guard
smarcet Jul 10, 2026
801669b
test: nested-transaction rollback coverage for SummitOrderService, Su…
smarcet Jul 10, 2026
9a33863
test: HTTP-level exception-branch coverage for order/attendee ticket …
smarcet Jul 10, 2026
48ab18c
docs(adr): nested transaction rollback safety decision record
smarcet Jul 10, 2026
af0f739
test: nested-transaction rollback coverage for remaining ADR-003 gaps
smarcet Jul 10, 2026
6123534
docs(adr): update nested-tx rollback ADR with remaining test coverage
smarcet Jul 10, 2026
fbd08d0
docs(adr): remove docs/plans references from ADR-003
smarcet Jul 10, 2026
d5388d0
chore: add nested-tx rollback test classes to push.yml CI matrix
smarcet Jul 10, 2026
006e3f0
docs(adr): clarify origin/main's actual pre-branch behavior in ADR-003
smarcet Jul 10, 2026
d0a3f2a
fix(transactions): never retry ambiguous commits, refuse closed-EM re…
smarcet Jul 10, 2026
ef5f96e
fix(transactions): discard broken manager/connection when rollback fails
smarcet Jul 10, 2026
ef92755
refactor(transactions): deduplicate failure-cleanup paths in Doctrine…
smarcet Jul 10, 2026
9e83515
fix(transactions): surface ambiguous commit failures as AmbiguousComm…
smarcet Jul 10, 2026
531ceae
fix(registration): log-and-skip failing rows in CSV ticket data import
smarcet Jul 11, 2026
1042e94
fix(transactions): never classify rollback-only commit failures as am…
smarcet Jul 11, 2026
6b25453
docs(adr): log AmbiguousCommitException consumer wiring as known gap
smarcet Jul 11, 2026
ff25ef1
fix(registration): keep import file when a CSV row commit outcome is …
smarcet Jul 11, 2026
39aa137
fix(summit): re-fetch summit per row in processEventData CSV import
smarcet Jul 11, 2026
95c589b
test(imports): volume coverage for every CSV import service method
smarcet Jul 11, 2026
40fdbcc
fix(registration): support guest buyers on the reserve saga
smarcet Jul 11, 2026
15efcd1
test(orders): revive reserve API tests and cover the order cancel end…
smarcet Jul 11, 2026
5a2ff78
fix(auth): flush email invalidation before reassigning a colliding email
smarcet Jul 11, 2026
7aa806a
chore(ci): run MemberServiceTest in the integration matrix
smarcet Jul 11, 2026
b5c0c2b
chore(ci): wire Stripe test credentials from repository secrets
smarcet Jul 11, 2026
aedd59e
fix(summit): keep import files when a row commit outcome is unknown
smarcet Jul 11, 2026
3b796f5
fix(registration): normalize owner email comparison in ReserveOrderTask
smarcet Jul 11, 2026
3abcca3
refactor(imports): drop AmbiguousCommitException special-casing from …
smarcet Jul 12, 2026
8dc9062
chore(ci): read all Stripe test credentials from repository secrets
smarcet Jul 12, 2026
9291321
test(payments): keep Stripe webhook creation out of CI runs
smarcet Jul 12, 2026
c452755
docs(adr): move SponsorUserSync pair to unreachable after #582 group …
smarcet Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ jobs:
- { name: "SummitOrderServiceTest", filter: "--filter SummitOrderServiceTest" }
- { name: "SummitRSVPServiceTest", filter: "--filter SummitRSVPServiceTest" }
- { name: "SummitRSVPInvitationServiceTest", filter: "--filter SummitRSVPInvitationServiceTest" }
- { name: "SummitServiceTest", filter: "--filter SummitServiceTest" }
- { name: "SpeakerServiceRegistrationTest", filter: "--filter SpeakerServiceRegistrationTest" }
- { name: "PresentationServiceTest", filter: "--filter PresentationServiceTest" }
- { name: "SummitPromoCodeServiceTest", filter: "--filter SummitPromoCodeServiceTest" }
- { name: "SummitScheduleSettingsServiceTest", filter: "--filter SummitScheduleSettingsServiceTest" }
- { name: "SummitSelectedPresentationListServiceTest", filter: "--filter SummitSelectedPresentationListServiceTest" }
- { name: "SelectionPlanOrderExtraQuestionTypeServiceTest", filter: "--filter SelectionPlanOrderExtraQuestionTypeServiceTest" }
- { name: "SummitSelectionPlanServiceTest", filter: "--filter SummitSelectionPlanServiceTest" }
- { name: "SummitRegistrationInvitationServiceTest", filter: "--filter SummitRegistrationInvitationServiceTest" }
- { name: "SummitSubmissionInvitationServiceTest", filter: "--filter SummitSubmissionInvitationServiceTest" }
- { name: "MemberServiceTest", filter: "--filter MemberServiceTest" }
- { name: "EntityModelUnitTests", filter: "tests/Unit/Entities/" }
- { name: "ModelUnitTests", filter: "tests/Unit/Models/" }
- { name: "AuditUnitTests", filter: "tests/Unit/Audit/" }
Expand Down Expand Up @@ -105,17 +116,19 @@ jobs:
REGISTRATION_DEFAULT_LIVE_STRIPE_PRIVATE_KEY:
REGISTRATION_DEFAULT_LIVE_STRIPE_PUBLISHABLE_KEY:
REGISTRATION_DEFAULT_LIVE_WEBHOOK_SECRET:
REGISTRATION_DEFAULT_TEST_STRIPE_PRIVATE_KEY: sk_test_12345
REGISTRATION_DEFAULT_TEST_STRIPE_PUBLISHABLE_KEY: pk_12345
REGISTRATION_DEFAULT_TEST_WEBHOOK_SECRET: whsec_12345
REGISTRATION_DEFAULT_TEST_STRIPE_PRIVATE_KEY: ${{ secrets.TEST_STRIPE_SECRET_KEY || 'sk_test_12345' }}
REGISTRATION_DEFAULT_TEST_STRIPE_PUBLISHABLE_KEY: ${{ secrets.TEST_STRIPE_PUBLISHABLE_KEY || 'pk_12345' }}
REGISTRATION_DEFAULT_TEST_WEBHOOK_SECRET: ${{ secrets.TEST_STRIPE_WEBHOOK_SECRET || 'whsec_12345' }}
TEST_STRIPE_SECRET_KEY: ${{ secrets.TEST_STRIPE_SECRET_KEY || 'sk_test_dummy_key' }}
TEST_STRIPE_PUBLISHABLE_KEY: ${{ secrets.TEST_STRIPE_PUBLISHABLE_KEY || 'pk_test_dummy_key' }}
BOOKABLE_ROOMS_DEFAULT_PAYMENT_PROVIDER: Stripe
BOOKABLE_ROOMS_DEFAULT_STRIPE_TEST_MODE: true
BOOKABLE_ROOMS_DEFAULT_LIVE_STRIPE_PRIVATE_KEY:
BOOKABLE_ROOMS_DEFAULT_LIVE_STRIPE_PUBLISHABLE_KEY:
BOOKABLE_ROOMS_DEFAULT_LIVE_WEBHOOK_SECRET:
BOOKABLE_ROOMS_DEFAULT_TEST_STRIPE_PRIVATE_KEY: sk_test_12345
BOOKABLE_ROOMS_DEFAULT_TEST_STRIPE_PUBLISHABLE_KEY: pk_12345
BOOKABLE_ROOMS_DEFAULT_TEST_WEBHOOK_SECRET: whsec_12345
BOOKABLE_ROOMS_DEFAULT_TEST_STRIPE_PRIVATE_KEY: ${{ secrets.TEST_STRIPE_SECRET_KEY || 'sk_test_12345' }}
BOOKABLE_ROOMS_DEFAULT_TEST_STRIPE_PUBLISHABLE_KEY: ${{ secrets.TEST_STRIPE_PUBLISHABLE_KEY || 'pk_12345' }}
BOOKABLE_ROOMS_DEFAULT_TEST_WEBHOOK_SECRET: ${{ secrets.TEST_STRIPE_WEBHOOK_SECRET || 'whsec_12345' }}
REGISTRATION_VALIDATE_TICKET_TYPE_REMOVAL: false
MEMCACHED_SERVER_HOST: 127.0.0.1
MEMCACHED_SERVER_PORT: 11211
Expand Down
400 changes: 400 additions & 0 deletions adr/003-nested-transaction-rollback-safety.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions app/Models/Foundation/Main/Member.php
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@ public function setTeamMemberships($team_memberships)
public function setGroups($groups)
{
$this->groups = $groups;
$this->groupMembershipCache = [];
}

/**
Expand Down Expand Up @@ -2059,6 +2060,7 @@ public function getMembershipType(): ?string

public function clearGroups():void{
$this->groups->clear();
$this->groupMembershipCache = [];
}
/**
* @param Group $group
Expand All @@ -2067,13 +2069,17 @@ public function add2Group(Group $group)
{
if ($this->groups->contains($group)) return;
$this->groups->add($group);
// belongsToGroup() memoizes per instance against the DB; a mutation makes
// that memo stale (the fresh row/removal becomes visible after flush).
$this->groupMembershipCache = [];
}

public function removeFromGroup(Group $group)
{
if (!$this->groups->contains($group)) return;
$this->groups->removeElement($group);
//$group->removeMember($this);
$this->groupMembershipCache = [];
}

/**
Expand Down
7 changes: 7 additions & 0 deletions app/Models/OAuth2/ResourceServerContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,13 @@ private function syncMemberFields(Member $member, ?string $user_email, ?string $
$member_by_email->getId()
));
$member_by_email->setEmail(sprintf("%s-invalid@invalid", $member_by_email->getId()));
// Member.Email is unique: flush the invalidation NOW, inside the still-open
// transaction, so the resolved member's own email UPDATE below can never be
// ordered first by the UnitOfWork and hit the unique index while the former
// owner still holds the email. Rolled back with the transaction if anything
// later fails. Same flush-now idiom as MemberService::registerExternalUser's
// twin guard (add($entity, true)).
$this->member_repository->add($member_by_email, true);
}
$member->setEmail($user_email);
}
Expand Down
Loading
Loading