Skip to content

fix(local): keep a rename or note edit made while a document ingests - #2148

Merged
MODSetter merged 1 commit into
MODSetter:devfrom
Cedric921:fix/finish-job-keeps-edits
Oct 2, 2026
Merged

MODSetter merged 1 commit into
MODSetter:devfrom
Cedric921:fix/finish-job-keeps-edits

Conversation

@Cedric921

@Cedric921 Cedric921 commented Oct 2, 2026 •

Copy link
Copy Markdown

What

finish_job no longer writes back the row begin_job read. It writes the terminal status and error_message, plus only the columns its caller produced (**produced):

  • worker/ingestion/pipeline.py passes content=markdown for a FILE only, and no longer assigns document.content on the ORM object. A note's content is the user's and ingest only reads it; ingest never passes title.
  • worker/studio/job.py passes the title and content that persist() just set.

Why

Found in review of #2132. With expire_on_commit=False, the copy begin_job read was never refreshed, and finish_job wrote its title and content back:

  • A file renamed during its parse got its upload name back when ingest finished. Right after upload, while the parse runs, is exactly when people rename.
  • A note edited mid-ingest got its old text written over the edit, and the re-ingest the edit enqueued then indexed the old text, so the edit was lost.

update_document has no processing guard, unlike delete, and greying out the controls would only narrow the window, so the fix is at the cause, as the review suggested.

docs/architecture/documents.md: the ingest Finish step says what it writes and why an edit made meanwhile stands.

Unblocks #2132.

How to test

cd surfsense_local/backend
uv run pytest tests/integration/worker/test_edits_during_ingest.py   # 2 passed, both failed before
uv run pytest -m integration    # 484 passed
uv run pytest -m unit           # 3247 passed
uv run ruff check worker tests/integration/worker
cd ../.. && python scripts/check_docs.py

tests/integration/worker/test_edits_during_ingest.py commits an edit from a second session while the worker parses, as PATCH would:

  • A FILE renamed mid-parse keeps its new name, and its extracted text is stored.
  • A NOTE edited mid-ingest (new content, back to pending) ends, after the ingest the edit queued, with the new text stored and indexed. The old text's words no longer match in chunks_fts.

High-level PR Summary

This PR fixes a race condition where user edits (file renames or note content changes) made during document ingestion were being overwritten when the ingestion job completed. The fix modifies finish_job to only write back columns that the ingestion process actually produced, rather than writing back the entire row that was read at the start. For files, only the extracted markdown content is written; for notes, the user's content is preserved as it's never modified by ingestion. The change ensures that renames made right after upload (during parsing) and note edits made mid-ingest are both preserved.

⏱️ Estimated Review Time: 15-30 minutes

💡 Review Order Suggestion
Order File Path
1 surfsense_local/backend/tests/integration/worker/test_edits_during_ingest.py
2 surfsense_local/backend/worker/jobs.py
3 surfsense_local/backend/worker/ingestion/pipeline.py
4 surfsense_local/backend/worker/studio/job.py
5 docs/architecture/documents.md

Need help? Join our Discord

Summary by CodeRabbit

  • Bug Fixes
    • Notes keep their user-written content after ingestion, while file content continues to reflect extracted text.
    • Title changes and note edits made during ingestion are preserved. Updated note text is indexed in the follow-up ingest.
    • Successful artifact generation retains the document’s current title and content.

finish_job wrote back the title and content begin_job had read, so a
file renamed during its parse got its upload name back, and a note
edited mid-ingest got its old text written over the edit, which the
re-ingest the edit queued then indexed. finish_job now writes only the
status, the error and the columns its caller produced: ingest passes a
file's extracted text and never a note's or a title; Studio passes the
title and content it generated.
@vercel

vercel Bot commented Oct 2, 2026

Copy link
Copy Markdown

@Cedric921 is attempting to deploy a commit to the Rohan Verma's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (4)
.cursor/rules/ponytail.mdc — auto-discovered
docs/README.md — configured
CONTRIBUTING.md — configured
AGENTS.md — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: MODSetter/SurfSense/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: fe4521d4-d5a5-492e-93fe-7c45285188fa

📥 Commits

Reviewing files that changed from the base of the PR and between 4dc375e and cc50280.

📒 Files selected for processing (5)
  • docs/architecture/documents.md
  • surfsense_local/backend/tests/integration/worker/test_edits_during_ingest.py
  • surfsense_local/backend/worker/ingestion/pipeline.py
  • surfsense_local/backend/worker/jobs.py
  • surfsense_local/backend/worker/studio/job.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

Ingest completion now updates only fields produced by the job. File ingestion supplies extracted Markdown, while note ingestion preserves user-written content. Studio generation supplies the document’s current title and content. Integration tests and architecture documentation cover edits during ingestion.

Changes

Ingest completion updates

Layer / File(s) Summary
Write only produced fields
surfsense_local/backend/worker/jobs.py
finish_job accepts produced fields as keyword arguments. It updates those fields with status and error message instead of copying content and title from the document object.
Update job callers and validate edit handling
surfsense_local/backend/worker/ingestion/pipeline.py, surfsense_local/backend/worker/studio/job.py, surfsense_local/backend/tests/integration/worker/test_edits_during_ingest.py, docs/architecture/documents.md
Ingest supplies extracted Markdown only for files. Studio generation supplies the current title and content. Integration tests check that edits during ingestion remain intact and that a queued note ingest indexes updated text. The architecture documentation describes these behaviors.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to cc502

This change keeps a rename or note edit made while a document is being ingested, instead of overwriting it with stale values. No merge-blocking risk was found.

Security Architecture Review

Security architecture risk: 🔵 Low · up to cc502

The change reduces concurrent-edit data loss while retaining cancellation controls. Search freshness still depends on follow-up ingestion, and recovery after an interruption is not fully established.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated exposure concerns the targeted document and its derived chunks within the existing ingestion and studio flows. The inspected change does not establish additional cross-document authority or a new production entrypoint; broader security coverage remains incomplete.

Trust Boundaries and Controls

  • observed — The edit route permits content changes only for notes. Ingestion now respects that user-owned content while continuing to own extracted file content. Cancellation checks and the conditional terminal update remain in place.

Resilience and Maintainability Implications

  • inferred — Index freshness depends on the follow-up ingestion executing. The unchanged edit path commits before enqueueing, so the inspected evidence does not establish crash-safe handoff or recovery after queue loss. This is a pre-existing recovery limitation rather than a demonstrated security regression from the new completion contract.

Hardening Proposals

  • proposed — As a separate consistency improvement, bind indexed output and READY completion to a document revision so an older ingestion cannot advertise current readiness after a newer edit. Pair this with durable follow-up delivery or reconciliation if interruption-safe freshness is required.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 4 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: preserving renames and note edits made while a document is ingesting.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 4 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@MODSetter
MODSetter merged commit baf6d89 into MODSetter:dev Oct 2, 2026
22 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants