Repository navigation
feat(pg): Research Note indexing and reindex_work (L-R-1 #634, PR 4) - #940
Conversation
Every Research Note change (a note write, a Work create with a note, a Work delete) enqueues one research.reindex_work job in its own transaction. The job takes the per-Work reindex advisory lock, the Work row FOR KEY SHARE (a gone Work: its derived rows are deleted, nothing is written) and the note FOR SHARE, then replaces the Concept and Argument mentions and work_note_state in that transaction, resolving Concepts through concept_keys in one query. research_refs reports pending until the index is the current note's. A note change also creates the Concepts it names that do not exist yet, as SQLite does, in key order. The jobs worker imports the module to register the handler. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017jgbjrTtg1i9iUrndjRxvp
Paused-reindex races with a note edit, a first note and a Work delete, a reindex that starts during a delete, a stale job after the delete, repeated jobs, the Research-ACK fence, Concept auto-creation and Library isolation, all without sleeps. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017jgbjrTtg1i9iUrndjRxvp
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017jgbjrTtg1i9iUrndjRxvp
This comment has been minimized.
This comment has been minimized.
Architecture Data flow Review 1 finding · reviewed
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
| Layer / File(s) | Summary |
|---|---|
Reference creation and job enqueueing backend/domain/research/concepts.py, backend/domain/works/commands.py, backend/domain/works/notes.py, tests/pg/test_research_index.py, AGENTS.md, backend/domain/research/__init__.py |
Research Note writes create missing referenced Concepts under key locks and enqueue reindex jobs. Work creation with a note and Work deletion also enqueue jobs. PostgreSQL tests cover Concept creation, rollback, and key-lock behavior. |
Reindex execution and reference reads backend/domain/research/reindex.py, backend/jobs/worker.py, tests/pg/test_research_index.py, pyrightconfig.typed-slice.json, scripts/check_invariants.py, tests/test_engineering_invariants.py, backend/domain/research/__init__.py |
The registered job locks and reindexes a Work, replaces derived mentions and note state, and exposes existing references with a pending flag. Tests cover queued jobs, retries, concurrency, deletion, acknowledgements, and Library isolation. |
Priority:
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Feature
Sequence Diagram(s)
sequenceDiagram
participant NoteWrite as works.notes._set_locked
participant Concepts as research.concepts.ensure_referenced
participant Enqueue as research.reindex.enqueue
participant Worker as jobs.worker
participant RunJob as research.reindex.run_reindex
participant Reindex as research.reindex.reindex_work
participant PostgreSQL
NoteWrite->>Concepts: Ensure referenced Concepts
NoteWrite->>Enqueue: Queue reindex job
Worker->>RunJob: Dispatch registered job
RunJob->>Reindex: Reindex Work
Reindex->>PostgreSQL: Lock Work and note, replace derived rows and note state
Possibly related issues
Fooftilly/PRKS#614— This PR implements the PostgreSQL Research Note indexing andresearch.reindex_workjob described as part of the tracker, but does not complete the full tracker.
Possibly related PRs
- Fooftilly/PRKS#937: Adds PostgreSQL Concept storage and the identity index used to create and resolve Concepts from note references.
- Fooftilly/PRKS#938: Adds PostgreSQL Argument storage and reference APIs used by the derived note index.
Suggested reviewers: claude
Merge Risk: 
Minimal · previous review 6956f
-
No unresolved issue in the supplied evidence prevents merging after normal checks.
Pre-merge checks: 
7 · 
1
-
Failed checks: 1 warning Passed checks: 7 passed-
Full details: Description check
-
Explanation
The description is complete and structured according to the repository template. It explains the changes, related issue, scope, validation results, compatibility impact, risks, follow-up work, and checklist status. It also explains why E2E was not run.
Full details: Prks Engineering Invariants
-
Explanation
PASS. The changed PostgreSQL research behavior matches the applicable repository rules.
backend/domain/research/reindex.pywrites only the derived mention/state tables, while note and concept/argument deletion checks use canonicalwork_notescontent.notes.pyandworks/commands.pycreate referenced Concepts in the same transaction and enqueue indexing; private notes and ordinary prose are not indexed. The new queries retainlibrary_idscoping, and no changed code logs private research content.README.md,SECURITY.md, and backend/frontend rules contain no conflicting requirement.
Full details: Ui Design Contract
-
Explanation
PASS: The pull request changes only backend research/work code, job registration, configuration, documentation, and tests. It does not change frontend files, routes, styles, or other user-visible UI interactions. The UI design contract is therefore not applicable.
Full details: Offline And Sync Coherence
-
Explanation
PASS: The PR changes PostgreSQL persistence and its sync handler, but it does not change the browser offline runtime, service worker, or durable-operation protocol. The documented PostgreSQL design explicitly requires an unchanged ACK, a post-commit reindex, and a
pendingfence. The new sync test verifies the ACK shape, queued job, pending-to-ready transition, exact replay, and no-op behavior. Additional tests cover at-least-once reruns, note edits, Work deletion, stale jobs, and Library isolation. The behavior remains coherent with the current local-first rules.
-
Full details: Docstring Coverage
-
Explanation
Docstring coverage is 26.19% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 42 functions across 9 files. (2 skipped: 2 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
Finishing Touches: 💡 1
-
📝 Generate docstrings: 💡
-
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
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.
Comment @coderabbitai help to get the list of available commands.
…names As a note write does (concepts.ensure_referenced), so the reindex finds every named Concept and the fence never settles on a missing link. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017jgbjrTtg1i9iUrndjRxvp
A Research Note naming several missing Concepts writes several keys, and an identity write taking two of them in the other order deadlocked with it on the key index. ensure_referenced now takes the Library's Concept key lock before it creates any, as set_identity does; a test pins that a rename waits on that lock. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017jgbjrTtg1i9iUrndjRxvp
|
@coderabbitai review |
✅ Action performed
|
Fooftilly
left a comment
There was a problem hiding this comment.
PR #940: one actionable P2 concurrency finding remains. Please address the inline comment and add the proposed regression test before merging. CI and previous review findings were clean on 6956f46, but they do not cover this interleaving.
ensure_referenced read the key owners, then held the named Concepts FOR KEY SHARE afterwards: a Concept delete committing in that gap left the note naming nothing while its index settled with pending=false. The owners are now the Concepts the write holds (one FOR KEY SHARE query over concept_keys); the rest are created under the key lock and held, and a key whose Concept is deleted again before it is held is created once more. Two deterministic race tests pause the note write just after and just before the hold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017jgbjrTtg1i9iUrndjRxvp
Summary
This is L-R-1 PR 4. It adds the PostgreSQL derived index of Research Notes and the
research.reindex_workjob, followingdocs/postgresql-architecture.md§8.4 and the #647 correction.Enqueue. Every command that changes a Research Note enqueues one
research.reindex_workjob in its own transaction, so the job is claimable only after that change commits. The commands are:notes._set_locked, used bySET_WORK_RESEARCH_NOTEand the facade);Jobs are never coalesced. A queued twin could be claimed before the enqueuing transaction commits, and a first note (an insert) would then be invisible to it.
The job (
backend/domain/research/reindex.py) runs one READ COMMITTED transaction through the existing jobs runtime, so it is lease- and epoch-fenced. It:pg_advisory_xact_lockon the Work's reindex key (Library + Work);FOR KEY SHARE. It waits for a Work delete in flight, and a later delete waits for it. If the Work is gone, the job deletes its mention andwork_note_staterows and writes nothing;FOR SHARE. A note edit in flight is waited for, and a later edit waits;derived.concept_mentions,derived.argument_mentionsandderived.work_note_statein the same transaction.Indexing details:
concept_keysin one query, with no Python scan.research_index.sync_workdoes on SQLite.reindex_work(scope, work_id)is the per-Work primitive the L-S-1 rebuild can reuse.Research-ACK fence.
SET_WORK_RESEARCH_NOTEACK is unchanged (same result shape and codes) and no longer waits for the index.research_refs(scope, work_id)returns{concepts, arguments, pending}.pendingstays true until the storedwork_note_statehash is the current note's.Concept auto-creation. This is SQLite's
ensure_concepts_for_names, the markup side effect of a note write, needed so mentions match SQLite.lock_keys, the oneset_identitytakes) and creates them in key order. A note writes several keys, so without this lock a rename taking two of them in the opposite order could deadlock with it (Codex finding, fixed in 6956f46). Notes that name missing Concepts therefore serialize with identity writes; notes that only name existing Concepts take no key lock.FOR KEY SHARE, as before. This is compatible with an identity write'sFOR NO KEY UPDATE.Registration.
backend/jobs/worker.pyimports the module to register the handler, as it does forjobs.prune.Related issues
Refs #634
Scope / non-goals
Not in this PR:
rebuild-derived --only research, L-S-1);Validation
Local results
tests/pg(scripts/test-postgres), at 8d8ca1bscripts/test-postgres --parity), at 8d8ca1btests/pg/test_research_index.pytests/test_engineering_invariants.pyreindex.pyadded) and default configscripts/check_invariants.pyThe required regression scenarios are covered with events and lock-waiter checks, never sleeps:
test_a_note_edited_while_a_reindex_of_the_old_note_is_paused: the edit waits onFOR SHARE; the final mentions match the new notetest_a_first_note_created_while_a_reindex_that_found_no_note_is_pausedtest_a_work_deleted_while_a_reindex_of_it_is_paused_leaves_no_rowsandtest_a_reindex_that_starts_during_a_delete_writes_nothingtest_a_stale_reindex_after_the_delete_cannot_recreate_rowstest_running_the_job_again_or_twice_changes_nothingandtest_a_job_run_again_after_its_commit_changes_nothingtest_concept_and_argument_references_become_mentions, plus auto-creation, a Work created with a note (including an unknown Concept) and a cleared notetest_the_research_ack_does_not_wait_for_the_index: ACK shape,pendinguntil the job runs, ledger replay, unchanged bodytest_libraries_index_their_own_notes, including a job naming another Library's Worktest_a_note_creating_several_concepts_and_a_rename_take_the_key_lock_first: the rename waits on the advisory key lock and then refuses cleanly; without the fix it waits on the key indexE2E was not run locally because no frontend or route code changed.
GitHub CI
Reported separately once the run on the final head finishes.
Migration / compatibility impact
derivedtables from130_research.sql.update_work_metadata(text_content=…)now auto-creates Concepts, as the SQLite HTTP note path (save_work_notes) does.add_workstores the note without doing so; the PG path follows the note-write rule so every Concept a note names exists.Risks / follow-up
Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_017jgbjrTtg1i9iUrndjRxvp
Summary by CodeRabbit
New Features
Bug Fixes