feat(drift-trio): phase C — scenarios S2–S8 + re-resolve-on-fiber hooks
drift-trio-scenarios.spec.ts: disjoint ping-pong, same-item interleave (no settle between bursts), conflict pairs (move-vs-delete / value-vs-value / move-vs-move — winner is CRDT policy, asserted only as convergence + drift silence), undo storm, 12-edit burst churn, late-joiner adopt, and Ctrl+S mid peer burst; per-tool adapters, marker-waits before every sweep (finding #7). S4 exposed finding #9: mutation hooks resolved item pointers at call time and committed later on the fiber — a remote remove in between frees the pointer (doApplyItems) and the commit resurrects the deleted item. Phase-B mutation hooks now re-resolve by uuid ON the fiber; a vanished item makes the mutation lose silently. applyDeltaToY's concurrent update/delete verified coherent (full resurrect or full remove) — no shared change needed. trio.ts: TabSet oracles + exported startV2 for duo/late-join composition. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G5cAM9M6q34n5X4dbrfVvi
This commit is contained in:
parent
71a575d2d8
commit
23b0f43e73
4 changed files with 456 additions and 31 deletions
|
|
@ -1518,15 +1518,23 @@ bool schCollabTestMoveSchItem( std::string aId, int aDx, int aDy )
|
|||
if( !fr )
|
||||
return false;
|
||||
|
||||
SCH_SHEET_PATH path;
|
||||
SCH_ITEM* item = fr->Schematic().ResolveItem( KIID( wxString::FromUTF8( aId.c_str() ) ),
|
||||
&path, /*allowNull*/ true );
|
||||
|
||||
if( !item )
|
||||
SCH_SHEET_PATH probe;
|
||||
if( !fr->Schematic().ResolveItem( KIID( wxString::FromUTF8( aId.c_str() ) ), &probe,
|
||||
/*allowNull*/ true ) )
|
||||
return false;
|
||||
|
||||
SCH_SCREEN* screen = path.LastScreen();
|
||||
fr->CallAfter( [fr, item, screen, aDx, aDy]() { collabTestMove( fr, item, screen, aDx, aDy ); } );
|
||||
// Re-resolve when the deferred body runs: a remote remove can apply in
|
||||
// between and the captured pointer would be dangling — the commit would
|
||||
// resurrect a deleted item (drift-trio S4 move-vs-delete). Vanished =>
|
||||
// the move loses, silently.
|
||||
fr->CallAfter( [fr, aId, aDx, aDy]() {
|
||||
SCH_SHEET_PATH path;
|
||||
SCH_ITEM* item = fr->Schematic().ResolveItem( KIID( wxString::FromUTF8( aId.c_str() ) ),
|
||||
&path, /*allowNull*/ true );
|
||||
|
||||
if( item )
|
||||
collabTestMove( fr, item, path.LastScreen(), aDx, aDy );
|
||||
} );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1544,16 +1552,21 @@ bool schCollabTestMirrorSchItem( std::string aId, bool aHorizontal )
|
|||
if( !item )
|
||||
return false;
|
||||
|
||||
SCH_SCREEN* screen = path.LastScreen();
|
||||
pcbjam_collab::runOnFiber( fr, [fr, aId, aHorizontal]() { // re-resolve on the fiber (S4)
|
||||
SCH_SHEET_PATH path;
|
||||
SCH_ITEM* live = fr->Schematic().ResolveItem( KIID( wxString::FromUTF8( aId.c_str() ) ),
|
||||
&path, /*allowNull*/ true );
|
||||
|
||||
if( !live )
|
||||
return;
|
||||
|
||||
pcbjam_collab::runOnFiber( fr, [fr, item, screen, aHorizontal]() {
|
||||
SCH_COMMIT commit( fr );
|
||||
commit.Modify( item, screen );
|
||||
commit.Modify( live, path.LastScreen() );
|
||||
|
||||
if( aHorizontal )
|
||||
item->MirrorHorizontally( item->GetPosition().x );
|
||||
live->MirrorHorizontally( live->GetPosition().x );
|
||||
else
|
||||
item->MirrorVertically( item->GetPosition().y );
|
||||
live->MirrorVertically( live->GetPosition().y );
|
||||
|
||||
commit.Push( wxT( "Collab test mirror" ) );
|
||||
} );
|
||||
|
|
|
|||
|
|
@ -2111,12 +2111,16 @@ std::string pcbCollabTestAddZone( int aX1, int aY1, int aX2, int aY2, std::strin
|
|||
bool pcbCollabTestFlipBoardItem( std::string aId )
|
||||
{
|
||||
PCB_EDIT_FRAME* fr = pcbFrame();
|
||||
BOARD_ITEM* item = testResolve( fr, aId );
|
||||
|
||||
if( !item )
|
||||
if( !testResolve( fr, aId ) )
|
||||
return false;
|
||||
|
||||
pcbjam_collab::runOnFiber( fr, [fr, item]() {
|
||||
pcbjam_collab::runOnFiber( fr, [fr, aId]() { // re-resolve on the fiber (S4)
|
||||
BOARD_ITEM* item = testResolve( fr, aId );
|
||||
|
||||
if( !item )
|
||||
return;
|
||||
|
||||
BOARD_COMMIT commit( fr );
|
||||
commit.Modify( item );
|
||||
item->Flip( item->GetPosition(), FLIP_DIRECTION::LEFT_RIGHT );
|
||||
|
|
@ -2135,14 +2139,19 @@ bool pcbCollabTestSetFootprintField( std::string aId, std::string aField, std::s
|
|||
if( !item || item->Type() != PCB_FOOTPRINT_T )
|
||||
return false;
|
||||
|
||||
FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
|
||||
wxString text = wxString::FromUTF8( aText.c_str() );
|
||||
bool isRef = ( aField == "Reference" );
|
||||
wxString text = wxString::FromUTF8( aText.c_str() );
|
||||
bool isRef = ( aField == "Reference" );
|
||||
|
||||
if( !isRef && aField != "Value" )
|
||||
return false;
|
||||
|
||||
pcbjam_collab::runOnFiber( fr, [fr, fp, text, isRef]() {
|
||||
pcbjam_collab::runOnFiber( fr, [fr, aId, text, isRef]() { // re-resolve on the fiber (S4)
|
||||
BOARD_ITEM* live = testResolve( fr, aId );
|
||||
|
||||
if( !live || live->Type() != PCB_FOOTPRINT_T )
|
||||
return;
|
||||
|
||||
FOOTPRINT* fp = static_cast<FOOTPRINT*>( live );
|
||||
BOARD_COMMIT commit( fr );
|
||||
commit.Modify( fp );
|
||||
|
||||
|
|
@ -2160,12 +2169,16 @@ bool pcbCollabTestSetFootprintField( std::string aId, std::string aField, std::s
|
|||
bool pcbCollabTestSetBoardItemLocked( std::string aId, bool aLocked )
|
||||
{
|
||||
PCB_EDIT_FRAME* fr = pcbFrame();
|
||||
BOARD_ITEM* item = testResolve( fr, aId );
|
||||
|
||||
if( !item )
|
||||
if( !testResolve( fr, aId ) )
|
||||
return false;
|
||||
|
||||
pcbjam_collab::runOnFiber( fr, [fr, item, aLocked]() {
|
||||
pcbjam_collab::runOnFiber( fr, [fr, aId, aLocked]() { // re-resolve on the fiber (S4)
|
||||
BOARD_ITEM* item = testResolve( fr, aId );
|
||||
|
||||
if( !item )
|
||||
return;
|
||||
|
||||
BOARD_COMMIT commit( fr );
|
||||
commit.Modify( item );
|
||||
item->SetLocked( aLocked );
|
||||
|
|
@ -2179,12 +2192,18 @@ bool pcbCollabTestSetBoardItemLocked( std::string aId, bool aLocked )
|
|||
bool pcbCollabTestMoveBoardItem( std::string aId, int aDx, int aDy )
|
||||
{
|
||||
PCB_EDIT_FRAME* fr = pcbFrame();
|
||||
BOARD_ITEM* item = testResolve( fr, aId );
|
||||
|
||||
if( !item )
|
||||
if( !testResolve( fr, aId ) )
|
||||
return false;
|
||||
|
||||
pcbjam_collab::runOnFiber( fr, [fr, item, aDx, aDy]() { collabTestMove( fr, item, aDx, aDy ); } );
|
||||
// Re-resolve ON the fiber: a remote remove can apply between scheduling
|
||||
// and running, and doApplyItems FREES removed items — a captured pointer
|
||||
// would be dangling and the commit would resurrect a deleted item
|
||||
// (drift-trio S4 move-vs-delete). Vanished => the move loses, silently.
|
||||
pcbjam_collab::runOnFiber( fr, [fr, aId, aDx, aDy]() {
|
||||
if( BOARD_ITEM* item = testResolve( fr, aId ) )
|
||||
collabTestMove( fr, item, aDx, aDy );
|
||||
} );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue