You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 00a4752
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: BRANCH_DETAILS.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ Pre-cold-storage binaries cannot read histories already moved into `archive.sqli
11
11
- Permanent thread deletion checks eligibility before writer quiescence under the storage tree lock. Obsolete jobs preserve live resources, remove only mismatched stale cold bundles before clearing their queue marker, and do nothing when no deletion marker remains. Permanent thread deletion removes the shell, event stream, command receipts, every thread-owned projection/runtime/checkpoint row, attachments, terminal history, provider logs and rotations, and any cold-archive manifest/chunks. Exact attachment ownership metadata remains available until external attachment and provider-log cleanup succeeds, after which the SQL and cold-chunk rows are removed; cross-thread plan references are cleared rather than leaving dangling ids, and the durable cleanup-queue entry is retained until filesystem cleanup and free-page reclamation succeed so interrupted deletes retry safely. Startup recovery also rediscovers soft-deleted shells that have not reached the cleanup queue, covering commits made while the lifecycle reactor was offline.
12
12
- Project removal in `apps/web/src/components/settings/ProjectSettingsPanel.tsx` applies the archived-history warning and command options from `apps/web/src/components/settings/ProjectSettingsPanel.logic.ts` independently to each selected project member. Members with known live threads use forced deletion; when a member has no live shell threads, it requests archived-thread deletion without force because archived shells are excluded from normal navigation state. The server removes archived cold bundles but still rejects any unseen live thread. Project removal emits the same per-thread deletion events for archived shells, so already-cold threads pass through the durable lifecycle worker and lose their hot shell, archive manifest, and compressed chunks before cleanup completes. `apps/web/src/components/settings/ProjectSettingsPanel.logic.test.ts` covers standalone and grouped warning text and the per-member thread partitioning that selects each member's command options; `apps/server/src/orchestration/decider.delete.test.ts` covers the archived-only live-thread precondition; and `apps/server/src/orchestration/Layers/ThreadDeletionReactor.test.ts` covers the forced-project decider, lifecycle reactor, and cold-storage boundary together.
13
13
- Archive/delete filesystem work runs through a durable background lifecycle queue. Thread creation is refused retryably while a soft-deleted shell or durable cleanup-queue entry remains, before any replacement event commits; the command id remains retryable after cleanup. WebSocket creation and first-prompt bootstrap wait for in-flight same-ID lifecycle work before dispatching creation, preserving draft retries without blocking the engine worker. A completed wait does not bypass the durable creation check when failed cleanup still has a marker. Unrelated bulk archives do not hold that creation wait; callers that require all lifecycle work to finish can still drain the whole queue. Archive and restore lifecycle work is serialized per project tree, with reference-counted idle-lock eviction after the final user or waiter. Archive eligibility and provider/terminal/log-writer shutdown run under that same tree lock, so a queued archive job that becomes stale after unarchive cannot stop the newly active session or delete its terminal history. Archive creation commits the complete compressed bundle, including its restore metadata, before the main-only destructive transaction deletes hot rows and marks the manifest `cleanup_pending`. This ordering avoids relying on atomic commits across attached databases when the hot database uses WAL. Destructive attachment/log cleanup must finish before the manifest becomes `cold`. Destructive archive transitions require terminal/log-writer shutdown before chunk creation and recheck the archived shell inside the hot-row deletion transaction; `archiveThread` normalizes typed quiesce failures to `ThreadColdStorageError` at the archive operation boundary. Provider shutdown also remains required while the projected session is `starting` or `running`; a missing provider binding is terminal for a settled or absent projected session, allowing legacy subagent shells that never owned a provider binding to move cold safely. Successful provider quiescence explicitly clears process-local background-liveness state, including the no-binding settled-shell path where no later `session.exited` event is guaranteed. The cold-storage boundary chunks child projections only after its caller-provided quiescence effect completes; pending-input coverage verifies that restored session and activity rows reflect stopped and resolved values while the lightweight hot shell retains a zero pending-input count. Failed lifecycle work requests one coalesced delayed rescan of the durable pending state instead of being dropped or introducing a permanent polling loop, enqueue interruptions release their deduplication reservation, and restart recovery preserves the same retry boundary. Filesystem failures other than a genuinely missing directory also keep retry state. Incomplete cleanup, including a `cleanup_pending` manifest whose archived shell has already been removed, and archived shells missing a manifest resume after restart without rebuilding an already durable bundle.
14
-
- For unarchive commands targeting an existing archived shell, `apps/server/src/orchestration/Layers/OrchestrationEngine.ts` restores `cold` or `cleanup_pending` bundles before domain dispatch and treats an existing `restored` manifest as an in-flight or post-commit reservation without replaying its stale chunks over live rows or attachments. Active or absent targets reach the decider without claiming restore ownership, so domain rejections retain their rejected receipt and remain rejected on command-id retry. A transient restore failure re-archives any tree members restored before the failure and returns an error without writing a rejected command receipt, so the same command id can retry after storage recovers. A rejected or failed command re-archives the restored rows and files, while a successful command finalizes the cold bundle only when that request actually performed a restore; this prevents unrelated or already-hot unarchive commands from deleting archive data. The transaction and the in-memory restore-ownership handoff form one uninterruptible boundary, so cancellation cannot surface after SQL commits but before ownership transfers and roll back accepted data. Once SQL restoration commits, later publication or metrics failures cannot re-archive the restored data. Retrying an accepted unarchive receipt retries idempotent bundle finalization for that same thread. Unarchive events also queue lifecycle cleanup, and startup discovery includes restored manifests on active shells. That cleanup removes only the eligible thread's stale bundle before any provider or terminal shutdown, preserves process-owned restore reservations, and retries failures through the existing coalesced rescan. Finalization and archive-abort cleanup commit cold-row deletions before removing the main manifest, so a partial cleanup retains its discovery marker. A finalization failure does not change an already-committed unarchive result.
14
+
- For unarchive commands targeting an existing, undeleted archived shell, `apps/server/src/orchestration/Layers/OrchestrationEngine.ts` restores `cold` or `cleanup_pending` bundles before domain dispatch and treats an existing `restored` manifest as an in-flight or post-commit reservation without replaying its stale chunks over live rows or attachments. Active, absent, or deleted targets reach the decider without claiming restore ownership. Domain rejections retain their rejected receipt and remain rejected on command-id retry; deleted archives stay cold for permanent cleanup without recreating attachment files. A transient restore failure re-archives any tree members restored before the failure and returns an error without writing a rejected command receipt, so the same command id can retry after storage recovers. A rejected or failed command re-archives the restored rows and files, while a successful command finalizes the cold bundle only when that request actually performed a restore; this prevents unrelated or already-hot unarchive commands from deleting archive data. The transaction and the in-memory restore-ownership handoff form one uninterruptible boundary, so cancellation cannot surface after SQL commits but before ownership transfers and roll back accepted data. Once SQL restoration commits, later publication or metrics failures cannot re-archive the restored data. Retrying an accepted unarchive receipt retries idempotent bundle finalization for that same thread. Unarchive events also queue lifecycle cleanup, and startup discovery includes restored manifests on active shells. That cleanup removes only the eligible thread's stale bundle before any provider or terminal shutdown, preserves process-owned restore reservations, and retries failures through the existing coalesced rescan. Finalization and archive-abort cleanup commit cold-row deletions before removing the main manifest, so a partial cleanup retains its discovery marker. A finalization failure does not change an already-committed unarchive result.
15
15
- Unarchive reserves still-hot archived rows with a `restored` manifest before releasing the archive-tree lock. This prevents queued lifecycle work from moving those rows cold between the restore check and the unarchive command commit; command failure archives the reserved rows, while success removes the reservation. Process-local restore ownership distinguishes those live reservations from `restored` manifests abandoned by a crashed process, which startup lifecycle discovery moves cold again while the shell remains archived. If storage cannot restore or reserve the archived conversation, the command is rejected before an active-shell event can commit.
16
16
- A `restored` manifest protects only the archive epoch that an unarchive command reserved. If post-commit bundle finalization fails and the user later archives the thread again, the newer shell timestamp lets lifecycle work replace the stale manifest and move the conversation cold.
17
17
- Migration `055_ThreadStorageLifecycle` follows `054_ProjectionThreadsAutoSettleDisabledAt` in the canonical migration manifest. It creates the archive manifest, maintenance, and cleanup-queue schema and queues existing archived and deleted threads.
0 commit comments