unbridled-41 opened a new pull request, #4754:
URL: https://github.com/apache/rocketmq-dashboard/pull/4754

   Fixes #4753.
   
   ## Problem
   
   The AI conversation retention pass deletes the conversation, its runs and 
its events, but never the conversation's agent workspace — while the on-request 
delete does. The workspace is the child's `HOME` 
(`home/.claude/projects/<cwd>/<session-id>.jsonl`), so "expired" conversations 
keep their full agent transcript on disk under an id nothing refers to any more.
   
   - `AiConversationService.java:509-515` — `deleteCascade` deletes events, 
runs, conversations and nothing else; its only caller is 
`deleteExpiredConversations` (`:480-497`), reached from the scheduled 
`purgeExpired` (`:412-426`).
   - `AiConversationService.java:290` — `delete(Long, String)` calls 
`workspace.delete(conversationId)`.
   - `RmqctlWorkspace.java:61-70` and `:212-216` — the layout documents `home/` 
as "the child's HOME, per conversation and persistent" and the materialisation 
as leaving it alone, so the transcript is exactly what survives.
   - `AiConversationProperties.java:37` — `retentionDays = 90`; 
`application.yml:119` — `workspace-dir: 
${STUDIO_AI_CONVERSATION_WORKSPACE_DIR:/tmp/rocketmq-studio-ai}`, so on a 
deployment that points it at a persistent volume the directories accumulate 
without bound.
   
   ## Root cause
   
   The retention cascade was written as a database cascade ("no FK constraints 
here, so the cascade is this method") and the workspace, a filesystem side 
effect owned by another class, was only remembered by the interactive delete 
path.
   
   ## Fix
   
   `deleteCascade` now removes the workspace of every purged conversation 
**after** the rows are gone (4 lines), the order `delete(Long, String)` uses: a 
crash in between leaves a directory that is still reachable, whereas removing 
the transcript of a conversation that is still visible is not recoverable. The 
class javadoc's "Retention cascades by hand" note now says the workspace goes 
with the rows.
   
   ## Tests
   
   Red before the fix:
   
   ```
   $ cd server && mvn -B -ntp -o test -Dtest=AiConversationServiceTest
   [ERROR] ...retentionShouldRemoveTheWorkspaceOfEveryPurgedConversationTest 
<<< FAILURE!
   Wanted but not invoked:
   [ERROR] Tests run: 19, Failures: 1, Errors: 0, Skipped: 0        # BUILD 
FAILURE
   ```
   
   After the fix:
   
   ```
   $ mvn -B -ntp -o test -Dtest=AiConversationServiceTest
   [INFO] Tests run: 19, Failures: 0, Errors: 0, Skipped: 0         # BUILD 
SUCCESS
   
   $ mvn -B -ntp -o test -Dtest='org.apache.rocketmq.studio.ops.ai.**'
   [INFO] Tests run: 904, Failures: 0, Errors: 0, Skipped: 0        # BUILD 
SUCCESS
   ```
   
   The new test uses an `InOrder` verification (`deleteByIds` before 
`workspace.delete(1)`, `(2)`, `(3)`), so it fails both if the workspace is 
never removed and if it is removed before the rows; the pre-existing 
`deleteShouldCascadeChildrenFirstAndRemoveTheWorkspaceTest` keeps the 
interactive path's ordering pinned.
   
   ## Duplicate check
   
   Searched open PRs (205) and issues (214) for `workspace`, `retention`, 
`purge`, `transcript`, `orphan`, `disk`, `cleanup`, plus the commit index: the 
retention hits are #4647/#4648 (alert snapshot retention) and the AI hits 
(#4685, #4693, #4696, #4707, #4715, #4631, #4665, #4738, #4740) are all about 
the run/stream lifecycle or the CLI session. Nothing covers the workspace 
lifecycle, and `git log -S "deleteCascade" -- AiConversationService.java` shows 
the cascade has been DB-only since it was introduced.
   
   ## Base branch
   
   Targets the development trunk `rocketmq-studio`. GitHub interprets closing 
keywords only for pull requests that target the repository default branch, and 
that is still `master`, so merging will not close #4753 automatically — it 
needs to be closed by hand.
   
   ## Risk
   
   Low: the same side effect the interactive delete already performs, on a path 
where the conversation is already gone. `RmqctlWorkspace.delete` never throws 
(its javadoc: "a stale directory on a deleted volume must not turn a successful 
delete into a 500"), so a filesystem problem cannot fail the retention pass.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to