Skip to content

fix(task): complete pending deletions with bounded retries - #2742

Merged
openai0229 merged 7 commits into
OtterMind:mainfrom
HandSonic:fix/task-artifact-delete-recovery
Sep 10, 2026
Merged

openai0229 merged 7 commits into
OtterMind:mainfrom
HandSonic:fix/task-artifact-delete-recovery

Conversation

@HandSonic

@HandSonic HandSonic commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Related issue

N/A — incomplete task/artifact deletion discovered during review of the task-system rebuild.

Summary

Deleting a completed task can stop halfway when the application exits or a filesystem operation fails. Persist the deletion request before changing the file or task record, then finish remaining work on startup.

All requests live in one task-artifact-deletions.json file containing a JSON array. Each item stores the task ID, original artifact path, unique staged path, attempt count, and last error. The deletion sequence is: save the intent and attempt count, stage the artifact, remove the task record, delete the staged artifact, then remove the completed queue item. After task removal, cleanup only touches the staged path, preserving any newer export at the original filename.

Startup makes one pass through the queue. Automatic processing stops at three total attempts, including the initial delete; exhausted entries retain their error. An explicit delete of a surviving task can try again. Already-completed entries are cleared even at the limit.

Service contracts live in domain-api: ArtifactService and TaskDeletionService. Their domain-core implementations are ArtifactServiceImpl for file operations and TaskDeletionServiceImpl for the deletion queue and retries. TaskServiceImpl and the task runtime consume these interfaces. PendingTaskDeletion holds per-operation state; JsonFileUtils handles JSON file I/O. The queue is loaded for each synchronized operation, without a persistent in-memory copy.

Affected surfaces

  • Frontend / Web
  • Backend / API / Storage
  • Database plugin / Driver
  • JCEF / Desktop packaging
  • CI / Build / Release
  • Documentation only

Verification

  • Clean affected-reactor build: 649 tests, zero failures, errors, or skips; all selected modules packaged successfully. Counts: tools 76, domain-api 27, SPI 143, domain-core 262, storage 59, web 82.
  • ArtifactServiceTest: 2 tests. TaskDeletionServiceImplTest: 21 tests covering queue persistence, restart recovery, retry limits, permanent failures, explicit retry, concurrent queue updates, same-name replacement preservation, queue I/O failures, corrupt queue preservation, and ownership/active-task rejection.
  • Additional regressions cover concurrent deletion of the same task, abrupt termination after staging, simultaneous task-storage and queue-write failure, and a failed attempt followed by a successful queue item. All four passed.
  • Separate JVMs using real FileTaskStorage verified recovery both before and after task-record removal, preserving a replacement export at the original path. Five successive JVM startups against a permanent failure stopped at attempt three and retained the error.
  • Spring integration test resolves both services through their domain-api interfaces and completes an actual file/task deletion.
  • JsonFileUtilsTest: 3 tests covering typed UTF-8 arrays, malformed/null input rejection, and preservation of the previous document when writing fails.
  • Executed with JBR 17 and an isolated test user.home:
mvn -B -o -f chat2db-community-server/pom.xml \
  -pl :chat2db-community-domain-core,:chat2db-community-storage,:chat2db-community-web -am \
  -Dmaven.test.skip=false -DskipTests=false \
  '-Dsurefire.includes=**/*Test.java' \
  -Dsurefire.failIfNoSpecifiedTests=false -Dmaven.test.failure.ignore=false \
  "-DargLine=-Duser.home=${TEST_HOME}" clean package
  • Diff whitespace checks passed. Task runner/context/manager changes are interface imports; their existing execution tests remain covered by the reactor run.
  • UI evidence: N/A — no frontend changes. Windows/Linux runtime verification was not performed locally.

Risk and compatibility

  • Public API or stored data: adds domain service interfaces and one JSON-array deletion queue under the existing environment directory. Task records and web response fields retain their format. The queued deletion proceeds forward; failures retain pending work instead of restoring deleted records or files.
  • Database or driver compatibility: N/A — local task storage and artifact files only.
  • Network, privacy, or security: existing task ownership and terminal-state checks remain in TaskServiceImpl before deletion. Downloads of a surviving task resolve its own staged artifact when present.
  • Community / Local / Pro boundary: follows the existing runtime environment path and TaskStorage contract.
  • Backward compatibility: released versions have no queue; an absent file means no pending work. Corrupt or unreadable queue data is preserved and prevents new destructive work.

Reviewer map

  • Start here: domain-api ArtifactService and TaskDeletionService contracts, then TaskDeletionServiceImpl.attemptDeletion and executeDeletion. ArtifactServiceImpl owns staging; PendingTaskDeletion owns attempt/error fields; JsonFileUtils owns JSON reading and replacement.
  • Failure condition: failure to persist intent prevents deletion. Execution errors are logged and saved on the pending item; queue I/O errors propagate without rolling back file/task operations. Exhausted entries remain for investigation.
  • Rollback or disable path: finish or inspect pending entries before reverting. Preserve the queue and staged files until the corresponding deletions are resolved.

Contributor declaration

  • I linked the Issue that defines this change.
  • I tested the affected behavior and reported the actual results above.
  • I did not include credentials, private data, or generated build output.
  • I disclosed substantial AI assistance below, or this PR contains no substantial AI-generated code.

AI assistance: implementation, review, simplification, interface extraction, and tests used AI assistance. The reported verification was executed locally.

@HandSonic
HandSonic requested review from a team and openai0229 as code owners August 23, 2026 18:54
@openai0229 openai0229 moved this to In Review in Chat2DB Community Aug 23, 2026
Deleting a task with a published artifact renames the file to a hidden
.task-delete-<uuid> staging name and physically deletes it only after the
task record removal commits. A crash inside that window left the staged
file orphaned forever (record gone) or the user's artifact stuck hidden
(record alive), with no recovery path - the events journal recovered
-event deletions only. Staged artifact deletions are now journaled to
task-artifact-deletions.json next to the task data and replayed at
startup: surviving task restores the artifact to its published name,
missing task sweeps the staged file.
@HandSonic
HandSonic force-pushed the fix/task-artifact-delete-recovery branch from 2df4940 to a11ada6 Compare September 3, 2026 21:41
@HandSonic

Copy link
Copy Markdown
Contributor Author

Exhaustive cross-PR integration update: the full 72-PR pairwise scan found the expected ArtifactService/test overlap among #2800, #2742, and #2802. I resolved it semantically in an isolated combined tree (atomic publication + durable deletion recovery + input cleanup), adapted the post-merge constructor/method signatures, and ran the full 9-module reactor: tools 68, domain-api 27, SPI 142, domain-core 256, storage 64, web 95 (1 environment skip), all with 0 failures/errors.

@openai0229 openai0229 changed the title fix(task): recover staged artifact deletions on startup fix(task): complete pending deletions with bounded retries Sep 10, 2026
@openai0229
openai0229 merged commit 80395a7 into OtterMind:main Sep 10, 2026
17 of 20 checks passed
@openai0229 openai0229 moved this from In Review to Done in Chat2DB Community Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants