Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
7 changes: 7 additions & 0 deletions Scripts/Fixtures/test-suite-contract-ledger.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -2817,6 +2817,13 @@ root/RepoPromptTests.WorkspaceRootTargetSeedPlanManifestTests/testManifestAuthen
root/RepoPromptTests.WorkspaceRootTargetSeedPlanManifestTests/testManifestRejectsDuplicateByteExactPathWithoutPublishing root Tests/RepoPromptTests/WorkspaceContext/WorkspaceRootTargetSeedPlanManifestTests.swift RepoPromptTests.WorkspaceRootTargetSeedPlanManifestTests testManifestRejectsDuplicateByteExactPathWithoutPublishing WorkspaceContext workspace_root_target_plan.duplicate_rejection duplicate,byte_exact,transactional_publication protocol_negative root_swiftpm routine 2 WorkspaceRootTargetSeedPlanManifestStore Duplicate paths fail without publication. Duplicate paths could make reconciliation ambiguous. 0.002000 temp_directory;spill_io manifest_store test_case+lease_cleanup retain 0 Ephemeral plan schema with inventory compatibility v4; no adapter.
root/RepoPromptTests.WorkspaceRootTargetSeedPlanManifestTests/testManifestScaleStreamsAreBounded root Tests/RepoPromptTests/WorkspaceContext/WorkspaceRootTargetSeedPlanManifestTests.swift RepoPromptTests.WorkspaceRootTargetSeedPlanManifestTests testManifestScaleStreamsAreBounded WorkspaceContext target_seed_plan_manifest.routine_boundary manifest_streaming,seeded_inventory,routine_boundary stress root_swiftpm routine 2 WorkspaceRootTargetSeedPlanManifestStore 10K target seed plans stream sorted overlay records with exact footer/reader counts and bounded buffered/resident path bytes. Target seed plan manifests could regress bounded streaming behavior in routine overlay publication. 1.0 routine_scale temporary_artifacts test_case retain 0 routine 10K boundary contract replacing the default 100K run
root/RepoPromptTests.WorkspaceRootTargetSeedPlanManifestTests/testManifestScaleStreamsOneHundredThousandOrMillionWhenEnabled root Tests/RepoPromptTests/WorkspaceContext/WorkspaceRootTargetSeedPlanManifestTests.swift RepoPromptTests.WorkspaceRootTargetSeedPlanManifestTests testManifestScaleStreamsOneHundredThousandOrMillionWhenEnabled WorkspaceContext target_seed_plan_manifest.scale_100k_or_million manifest_streaming,seeded_inventory,scale_gate,million_opt_in stress root_swiftpm scale 3 WorkspaceRootTargetSeedPlanManifestStore 100K target seed plans, or 1M when RPCE_RUN_MILLION_ENTRY_TESTS=1, stream sorted records with exact footer counts and bounded buffered/resident path bytes. Target seed plan manifests could buffer too much data or corrupt counts during large overlay publication. 6.0 scale,opt_in temporary_artifacts test_case retain 0 gated behind RPCE_RUN_SCALE_TESTS=1 for 100K; RPCE_RUN_MILLION_ENTRY_TESTS=1 still selects the million-entry lane
root/RepoPromptTests.WorkspaceSaveCoordinatorTests/testCloseBoundaryFlushesPendingDebouncedSave root Tests/RepoPromptTests/Workspaces/WorkspaceSaveCoordinatorTests.swift RepoPromptTests.WorkspaceSaveCoordinatorTests testCloseBoundaryFlushesPendingDebouncedSave Workspaces workspace_save.close_flushes_debounce close_boundary,debounce,durability async_persistence_integration root_swiftpm integration 1 SyntheticWorkspaceSaveFixture Window-close preparation followed by the explicit boundary flush commits a pending debounced workspace save. Closing during the debounce interval could discard the latest workspace state. 0.300000 filesystem;workspace_manager;async_tasks GlobalCustomStorageURL;WorkspaceDiskWriter test_case+tearDown_writer_reset+defer_storage_cleanup retain 0 PR #469 close durability contract
root/RepoPromptTests.WorkspaceSaveCoordinatorTests/testCloseBoundaryRetriesDirtyVersionAfterEarlierFailure root Tests/RepoPromptTests/Workspaces/WorkspaceSaveCoordinatorTests.swift RepoPromptTests.WorkspaceSaveCoordinatorTests testCloseBoundaryRetriesDirtyVersionAfterEarlierFailure Workspaces workspace_save.close_retries_dirty_failure close_boundary,writer_failure,dirty_version,bounded_retry async_persistence_integration root_swiftpm integration 2 SyntheticWorkspaceSaveFixture,InjectedAtomicWriteFailure A failed save leaves its version dirty, and the later close boundary retries and commits after the writer recovers. Idle coordinator cleanup could otherwise hide an unsaved dirty version from termination durability. 0.300000 filesystem;workspace_manager;async_tasks GlobalCustomStorageURL;WorkspaceDiskWriter test_case+tearDown_writer_reset+defer_storage_cleanup retain 0 PR #469 failed-save close retry contract
root/RepoPromptTests.WorkspaceSaveCoordinatorTests/testFailedAtomicWriteIsObservableAndDoesNotAdvanceSavedVersion root Tests/RepoPromptTests/Workspaces/WorkspaceSaveCoordinatorTests.swift RepoPromptTests.WorkspaceSaveCoordinatorTests testFailedAtomicWriteIsObservableAndDoesNotAdvanceSavedVersion Workspaces workspace_save.failure_does_not_advance_version writer_failure,retry,durability_outcome async_persistence_integration root_swiftpm integration 2 SyntheticWorkspaceSaveFixture,InjectedAtomicWriteFailure Injected atomic-write failure returns failed without advancing the saved version; a healthy explicit retry commits the current state. A swallowed write failure could falsely mark in-memory workspace state durable and suppress retry. 0.100000 filesystem;workspace_manager;async_tasks GlobalCustomStorageURL;WorkspaceDiskWriter test_case+tearDown_writer_reset+defer_storage_cleanup retain 0 PR #469 pre-serialization save coalescing failure contract
root/RepoPromptTests.WorkspaceSaveCoordinatorTests/testFailedThenSuccessfulPayloadsReceiveTheirOwnOutcomes root Tests/RepoPromptTests/Workspaces/WorkspaceSaveCoordinatorTests.swift RepoPromptTests.WorkspaceSaveCoordinatorTests testFailedThenSuccessfulPayloadsReceiveTheirOwnOutcomes Workspaces workspace_writer.failed_then_successful_receipts per_payload_receipt,failed_then_successful,drain_failure async_persistence_integration root_swiftpm integration 2 InjectedAtomicWriteFailure,WorkspaceSavePreparationGate A failed payload reports failed, the following payload reports committed, and the drain barrier preserves the failure. A later successful drain result could mask an earlier caller's failed payload. 0.050000 filesystem;async_tasks WorkspaceDiskWriter test_case+tearDown_writer_reset+defer_storage_cleanup retain 0 PR #469 per-payload writer receipt contract
root/RepoPromptTests.WorkspaceSaveCoordinatorTests/testMutationDuringPreparationProducesOneFollowUpAndFlushCommitsNewestState root Tests/RepoPromptTests/Workspaces/WorkspaceSaveCoordinatorTests.swift RepoPromptTests.WorkspaceSaveCoordinatorTests testMutationDuringPreparationProducesOneFollowUpAndFlushCommitsNewestState Workspaces workspace_save.single_followup_for_newer_state preparation_gate,request_sequence,boundary_flush,latest_state async_persistence_integration root_swiftpm integration 2 SyntheticWorkspaceSaveFixture,WorkspaceSavePreparationGate A newer version arriving during blocked preparation causes exactly one follow-up preparation, and boundary flush returns only after that later request is atomically readable. Version-only coalescing could drop the final save-time prompt or selection snapshot, while recursive retry could amplify preparation. 0.300000 filesystem;workspace_manager;async_tasks GlobalCustomStorageURL;WorkspaceDiskWriter test_case+tearDown_writer_reset+defer_storage_cleanup retain 0 PR #469 pre-serialization save coalescing latest-state loop
root/RepoPromptTests.WorkspaceSaveCoordinatorTests/testRapidRequestsCoalesceBeforeRepresentativeWorkspacePreparation root Tests/RepoPromptTests/Workspaces/WorkspaceSaveCoordinatorTests.swift RepoPromptTests.WorkspaceSaveCoordinatorTests testRapidRequestsCoalesceBeforeRepresentativeWorkspacePreparation Workspaces workspace_save.preparation_single_flight rapid_requests,representative_payload,safe_metrics,pre_serialization bounded_performance_contract root_swiftpm integration 1 SyntheticWorkspaceSaveFixture One hundred deferred requests plus a flush produce one preparation and one atomic write for a 25-tab payload while reporting only structural counts. Late-only coalescing could repeatedly decode and encode a compose-tab-heavy workspace and recreate the CPU amplification. 0.300000 filesystem;workspace_manager;async_tasks GlobalCustomStorageURL;WorkspaceDiskWriter test_case+tearDown_writer_reset+defer_storage_cleanup retain 0 PR #469 pre-serialization save coalescing representative fixture
root/RepoPromptTests.WorkspaceSaveCoordinatorTests/testSuccessfulThenFailedPayloadsReceiveTheirOwnOutcomes root Tests/RepoPromptTests/Workspaces/WorkspaceSaveCoordinatorTests.swift RepoPromptTests.WorkspaceSaveCoordinatorTests testSuccessfulThenFailedPayloadsReceiveTheirOwnOutcomes Workspaces workspace_writer.successful_then_failed_receipts per_payload_receipt,successful_then_failed,drain_failure async_persistence_integration root_swiftpm integration 2 InjectedAtomicWriteFailure,WorkspaceSavePreparationGate A committed payload reports committed, the following payload reports failed, and the drain barrier reports the failure. A final failure could be incorrectly attributed to an earlier successful payload or lost at drain completion. 0.050000 filesystem;async_tasks WorkspaceDiskWriter test_case+tearDown_writer_reset+defer_storage_cleanup retain 0 PR #469 per-payload writer receipt contract
root/RepoPromptTests.WorkspaceSearchServiceTests/testPathMatchWarmupPreservesLookupBehavior root Tests/RepoPromptTests/WorkspaceContext/Search/WorkspaceSearchServiceTests.swift RepoPromptTests.WorkspaceSearchServiceTests testPathMatchWarmupPreservesLookupBehavior WorkspaceContext unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.003000 unreviewed retain_pending_review 0 initial census source line 311
root/RepoPromptTests.WorkspaceSearchServiceTests/testSearchCatalogGenerationChangesOnRootLoadDeltaAndUnload root Tests/RepoPromptTests/WorkspaceContext/Search/WorkspaceSearchServiceTests.swift RepoPromptTests.WorkspaceSearchServiceTests testSearchCatalogGenerationChangesOnRootLoadDeltaAndUnload WorkspaceContext unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.005500 unreviewed retain_pending_review 0 initial census source line 15
root/RepoPromptTests.WorkspaceSearchServiceTests/testWorkspaceSearchServiceCatchesUpWhenEventPrecedesSubscription root Tests/RepoPromptTests/WorkspaceContext/Search/WorkspaceSearchServiceTests.swift RepoPromptTests.WorkspaceSearchServiceTests testWorkspaceSearchServiceCatchesUpWhenEventPrecedesSubscription WorkspaceContext unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.016000 unreviewed retain_pending_review 0 initial census source line 227
Expand Down
8 changes: 7 additions & 1 deletion Sources/RepoPrompt/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,13 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate {
await WindowStatesManager.shared.persistWindowSessionImmediately(reason: "appShouldTerminate")
}

// 3) Shut down agent processes and MCP tools on the main actor WITHOUT blocking.
// 3) Flush already-captured workspace saves before window teardown can release them.
let workspaceSavesFlushed = await WindowStatesManager.shared.flushAllWorkspaceSavesBeforeTermination()
if !workspaceSavesFlushed {
print("💾 App termination exhausted the bounded workspace-save retry.")
}

// 4) Shut down agent processes and MCP tools on the main actor WITHOUT blocking.
// Kill Claude CLI and Codex app-server processes BEFORE stopping MCP servers,
// so child processes are terminated and reaped rather than orphaned on quit.
await WindowStatesManager.shared.shutdownAllAgentSessions()
Expand Down
4 changes: 4 additions & 0 deletions Sources/RepoPrompt/App/WindowState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,10 @@ class WindowState: ObservableObject {

func tearDown() async {
beginClose()
let workspaceSaveOutcome = await workspaceManager.flushPendingWorkspaceSavesBeforeClose()
if workspaceSaveOutcome == .failed || workspaceSaveOutcome == .cancelled {
print("💾 Window close could not flush all workspace saves: \(workspaceSaveOutcome)")
}
await promptManager.gitViewModel.shutdownForWindowClose()

let isAppTermination = WindowStatesManager.shared.isTerminating
Expand Down
14 changes: 14 additions & 0 deletions Sources/RepoPrompt/App/WindowStateManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,20 @@ class WindowStatesManager: ObservableObject {

// MARK: - MCP Server Coordination

/// Establishes a durability barrier for saves already captured by each window.
/// Windows are flushed serially so two views of the same workspace do not prepare
/// competing termination snapshots concurrently.
func flushAllWorkspaceSavesBeforeTermination() async -> Bool {
var allSucceeded = true
for window in allWindows {
let outcome = await window.workspaceManager.flushPendingWorkspaceSavesBeforeClose()
if outcome == .failed || outcome == .cancelled {
allSucceeded = false
}
}
return allSucceeded
}

/// Stops MCP servers in **all** windows (unconditionally).
/// During teardown, "running" state can be stale; we just want tools off.
func stopAllServers() async {
Expand Down
Loading
Loading