Skip to content

feat(pg): Version-aware Argument citation API (L-R-1 #634, PR 6) - #949

Merged
Fooftilly merged 1 commit into
masterfrom
claude/project-thread-o9yqq7
Oct 28, 2026
Merged

Fooftilly merged 1 commit into
masterfrom
claude/project-thread-o9yqq7

Conversation

@Fooftilly

@Fooftilly Fooftilly commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

This is L-R-1 PR 6. On PostgreSQL, a citation can now name the exact Version (Manifestation) it cites. Before this change, only the legacy shape {work_id, pages} was accepted, and a pinpoint on a Work with several Versions was refused with ARGUMENT_SOURCE_VERSION_REQUIRED. The existing citation code in backend/domain/research/arguments.py is extended; there is no second citation system.

Request contract

The same entry points accept it: create_argument(sources=…) and replace_argument_sources(…) on the facade, and CREATE_ARGUMENT / SET_ARGUMENT_SOURCES in sync. A list comes in one of two shapes. If any entry carries manifestation_id, the list is Version-aware.

Shape Entry Rules
Version-aware (new) {work_id, manifestation_id, pages} manifestation_id is a Version id, or null for the Work in general. The list is stored exactly as given, in order. A Work may appear several times (Work-level, several Versions, several pinpoints). Only an exact repeat of (work_id, manifestation_id, pages) is refused (#60 §8.5).
Legacy (unchanged) {work_id, pages} A replaced entry that matches exactly one stored row keeps that row's Version. A new pinpoint is pinned to the Work's only Version. An ambiguous entry gives ARGUMENT_SOURCE_VERSION_REQUIRED (409). A repeated Work is still refused, as on SQLite.

A list that mixes the two shapes is refused with invalid_sources.

Refusals of a Version-aware list. HTTP-style errors are ResearchError codes; sync results are uppercase codes.

Case Code Status
The Version doesn't exist in this Library (another Library's id reads the same) argument_source_version_not_found / ARGUMENT_SOURCE_VERSION_NOT_FOUND 404
The Version belongs to a different Work argument_source_version_mismatch / ARGUMENT_SOURCE_VERSION_MISMATCH 409
The cited Work is missing work_not_found (unchanged) 404
Mixed shapes; manifestation_id neither a string nor null; exact repeat invalid_sources 400
Bad pages invalid_pages (unchanged) 400

In sync, each client-checkable shape error is INVALID_ENVELOPE. That covers an extra key, an untrimmed id, an exact repeat, more than MAX_SOURCES entries, and bad pages.

Response contract

Every citation row in Argument detail and the Argument list carries manifestation_id, which is null for a Work-level citation. That is enough to send a pinned citation back without losing its identity. All other fields are unchanged.

Revisions and conflicts

The list is still one aggregate, argument-sources/<A>, and it advances only on a change.

  • Pin-only change: with a Version-aware list, changing only a pin counts as a change.
  • Stale base: for a Version-aware operation, SET_ARGUMENT_SOURCES compares against the stored list including pins. The same pins with a stale base converge; a different pin conflicts with REVISION_CONFLICT.

Concurrency

  • Lock order: the named Works are held FOR KEY SHARE, then the named Versions, by id, then the Argument row. This is documented in backend/domain/research/__init__.py.
  • Version move: a move updates the Version's key, so it waits for the citing command. Once it lands, the citation follows the Version via ON UPDATE CASCADE. A citation that waited on a move re-reads after the retry and is refused with MISMATCH.
  • Version delete: deleting a pinned Version is refused by the foreign key argument_sources_manifestation_fkey.
  • Isolation and Work merge: CREATE_ARGUMENT and SET_ARGUMENT_SOURCES stay SERIALIZABLE peer writers of a Work merge. A pinned citation added after a merge's snapshot makes the merge fail with 40001, and the retry sees it.
  • Rollback: each replacement runs in one transaction, so a refusal changes nothing.

Related issues

Refs #634

Scope / non-goals

  • No HTTP route switch. server.py still serves the Research routes from SQLite, so the live API does not accept the new shape yet.
  • No Pydantic DTO or OpenAPI fragment for Argument sources. A typed contract describing a shape the live (SQLite) route doesn't accept would be wrong. It belongs with the Research route cutover, and the contract above is what it would encode.
  • No frontend, no SQLite change.
  • There is no PostgreSQL command yet to delete or move a single Version. The tests use SQL to show the foreign key and cascade behavior those commands will meet.

Validation

Local results

Check Result
Full tests/pg (scripts/test-postgres) 1238 tests OK
Parity (scripts/test-postgres --parity) 327 OK: SQLite leg 163/163, PostgreSQL leg OK
tests/pg/test_arguments.py + test_arguments_sync.py 55 OK, 17 of them new
SQLite: test_argument_sync, test_work_identity, test_contract_parity, test_research_network, test_engineering_invariants 241 passed
Ruff, Pyright (typed slice and changed files), scripts/check_invariants.py clean

New tests, mapped to the requested cases:

Case Test
One Version test_a_work_with_one_version
Several Versions test_a_work_with_several_versions_is_cited_version_by_version
Non-primary Version kept across pages and order changes test_a_non_primary_version_survives_a_change_of_pages_or_order
Version of another Work or Library test_a_version_missing_or_of_another_work_or_library_refuses_the_list
Delete or move of a cited Version test_a_pinned_version_cannot_be_deleted_and_follows_a_move; races test_a_version_move_waits_for_the_citation_pinning_it, test_a_citation_racing_a_move_of_its_version, test_a_version_delete_waits_for_the_citation_pinning_it_and_is_refused
Concurrent changes and merges test_two_version_aware_lists_of_one_argument_queue, test_a_citation_after_a_merge_snapshot_fails_the_merge_and_its_retry_sees_it, sync test_two_lists_on_one_base_one_lands_the_other_conflicts
Unambiguous legacy requests existing legacy tests, plus test_a_legacy_list_cannot_rewrite_a_list_citing_one_work_twice
Ambiguous legacy requests existing test_a_version_the_legacy_list_cannot_name_refuses_the_list
Revision conflict and rollback test_create_and_replace_with_pins_and_the_decision_table, test_a_version_refusal_is_ledgered_and_changes_nothing
Sync envelope test_the_envelope_of_a_version_aware_list

Races hold one transaction open until the other is observed waiting in pg_locks. The merge race uses events; no test depends on timing. E2E was not run locally because no route or frontend code changed.

GitHub CI

Reported separately once the run finishes.

Migration / compatibility impact

  • No schema change. The argument_sources table from feat(pg): Research schema on PostgreSQL (L-R-1 #634, PR 1) #936 already has the identity this PR uses.
  • Legacy requests: these behave exactly as before. A legacy client cannot rewrite a list that cites one Work twice, which only a Version-aware client can create, but it can still clear it.
  • Documented differences from SQLite:
    • PostgreSQL citation rows carry manifestation_id. The parity comparator drops that key on both sides.
    • The two new refusal codes exist only on PostgreSQL.

Checklist

  • Relevant tests/checks were run and are listed above.
  • Documentation updated (AGENTS.md lane line, lock order, facade and handler docstrings, work-identity-model §13.2 row).
  • UI evidence (n/a).
  • No unrelated changes.

Generated by Claude Code


Devin Review


Play the walkthrough    Follow each request

Summary by CodeRabbit

  • New Features
    • Citations can now be pinned to a specific Version of a Work, and the same Work can be cited multiple times when the citations differ.
    • Citation results now include the selected Version. Citations without a Version remain supported, with pinpoint citations requiring an unambiguous Version.
  • Bug Fixes
    • Invalid, missing, or mismatched Version selections are now rejected, and citations remain protected from conflicting changes during updates.
  • Documentation
    • Updated guidance describes Version-aware citations and their validation rules.

A citation list may name each cited Version explicitly:
[{work_id, manifestation_id, pages}], manifestation_id null for the Work
in general. Such a list is stored as given, may cite one Work several
times (only exact repeats are refused), and refuses a Version this Library
lacks (ARGUMENT_SOURCE_VERSION_NOT_FOUND, 404) or one of another Work
(ARGUMENT_SOURCE_VERSION_MISMATCH, 409). Legacy {work_id, pages} lists keep
their pin resolution. The named Versions are held FOR KEY SHARE after the
Works, so a Version move or delete waits for the command. Citation rows
read back carry manifestation_id. The PostgreSQL sync handler validates the
Version-aware envelope for CREATE_ARGUMENT and SET_ARGUMENT_SOURCES and
compares pins when deciding a stale base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017jgbjrTtg1i9iUrndjRxvp
@sourcery-ai

This comment has been minimized.

@coldtea-pr-lens

coldtea-pr-lens Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Architecture

Architecture diagram for Fooftilly/PRKS at 7d8a901

Play the walkthrough


Data flow

Data flow diagram for Fooftilly/PRKS at 7d8a901

Follow each request


Review

✨ Nothing flagged · reviewed 7d8a901


Tip

Click the link under each diagram to open it on a canvas you can zoom, pan and step through

🪧 More tips
  • Run npx skills add coldteadotai/pr-lens, then tell your coding agent: "Diagram the change you just made with PR Lens and attach it to the pull request."
  • Run npx @coldtea/pr-lens-cli analyze --base origin/main on a branch, then npx @coldtea/pr-lens-cli render .pr-lens/graph.json. Same lenses, your own model key, before the pull request exists
  • The diagrams are links. Click one to open it on the canvas, then press W or click play to walk through the change
  • Open a diagram on the canvas, then press W or click play to walk through the change one step at a time
  • The CLI's render reads .github/pr-lens.yml and applies your renames, exclusions and lane pins at draw time
  • Set github.comment.collapsed: true in .github/pr-lens.yml to fold the comment behind one View architecture… row. Drawing still runs as before
  • Set github.draw: on-demand in .github/pr-lens.yml and PR Lens stops drawing pull requests on its own. Comment @pr-lens draw on a pull request when you want that one drawn
  • Add .github/workflows/pr-lens.yml with coldteadotai/pr-lens/packages/action@v0 and your model provider's key as its api-key to run PR Lens from your own CI. Any /chat/completions endpoint works
  • After a push, tick Redraw to draw the latest commit
  • Switch GitHub to dark mode and the diagrams follow. The moving dots are this pull request's data in motion
  • Set github.description: false in .github/pr-lens.yml to stop PR Lens adding buttons to your PR descriptions

Thanks for using PR Lens! It's free for OSS. If it helped, a star helps other teams find it.  Star PR Lens on GitHub

@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 2bc1236 and 7d8a901.

Files selected for processing: 9
  • AGENTS.md
  • backend/domain/research/__init__.py
  • backend/domain/research/arguments.py
  • backend/pg_facade/research.py
  • backend/sync/handlers/argument.py
  • docs/work-identity-model.md
  • tests/pg/test_arguments.py
  • tests/pg/test_arguments_sync.py
  • tests/test_arguments_parity.py

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

Walkthrough

PostgreSQL Argument citations now support entries that identify a Version, alongside the legacy Work-and-pages shape. Sync handlers validate Version-aware lists, and the Research domain handles Version pin checks, locking, and citation readback.

Changes: Version-aware citation support

Layer / File(s) Summary
Citation validation, locking, and persistence
backend/domain/research/arguments.py, backend/domain/research/__init__.py, tests/pg/test_arguments.py, tests/test_arguments_parity.py, AGENTS.md
The Research domain accepts Version-aware citation entries, checks that pins exist and belong to the named Work, and includes Version IDs in citation readback. Legacy lists retain their existing behavior, including pinning pinpoint citations only when a Work has one Version. Tests cover validation, Version changes, concurrency, and parity normalization.
Sync source-list validation and revisions
backend/sync/handlers/argument.py, backend/pg_facade/research.py, tests/pg/test_arguments_sync.py, docs/work-identity-model.md
Sync handlers validate Version-aware source-list entries and reject malformed envelopes. Tests cover Version refusals, pin-aware revision comparisons, and concurrent updates. The facade and model documentation describe the new citation shape.

Priority: Normal

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

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Client as Sync client
  participant Handler as Argument sync handler
  participant Domain as Research arguments
  participant PostgreSQL
  Client->>Handler: Submit CREATE_ARGUMENT or SET_ARGUMENT_SOURCES
  Handler->>Handler: Validate source-list envelope
  Handler->>Domain: Pass validated payload
  Domain->>PostgreSQL: Lock cited Works and Versions
  Domain->>PostgreSQL: Store or replace citation rows
  PostgreSQL-->>Domain: Return database result
  Domain-->>Handler: Return result or refusal
  Handler-->>Client: Return operation response
Loading

Possibly related issues

  • Fooftilly/PRKS issue 41 requests citations that select a specific edition, version, or translation. This change adds Version-aware Argument citations.
  • Fooftilly/PRKS issue 614 identifies L-R-1 Research as an unfinished PostgreSQL release slice. This change implements a feature within that slice.

Possibly related PRs

  • Fooftilly/PRKS#938: This PR extends the PostgreSQL Argument implementation and citation-list contracts introduced there.

Suggested labels: enhancement, area:architecture, area:sync

Suggested reviewers: claude

Merge Risk: Minimal · up to 7d8a9

No merge-blocking issue was established; the change is ready for normal checks.

Pre-merge checks: Passed 7 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 42.11% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 7 passed
Check name Status Explanation
Title check Passed The title clearly and concisely identifies the PostgreSQL feature added: Version-aware Argument citation support.
Description check Passed The description is complete and directly matches the template. It explains the change, related issue, scope, compatibility impact, risks, validation results, and checklist status.
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.
Prks Engineering Invariants Passed PASS. The changed behavior stays within the existing PostgreSQL transition and research/sync modules. AGENTS.md and README.md state that PostgreSQL is not the active runtime and that SQLite remain…
Ui Design Contract Passed The pull request has no user-visible frontend changes. The authoritative diff changes only backend Python files, documentation, and tests. Therefore, the DESIGN.md interaction contract does not apply.
Offline And Sync Coherence Passed PASS. The PR changes PostgreSQL Argument citation handling, not the current SQLite/browser offline path. The rollout status already marks Arguments and Stances as durable. The PostgreSQL sync handler …
Full details: Prks Engineering Invariants

Explanation

PASS. The changed behavior stays within the existing PostgreSQL transition and research/sync modules. AGENTS.md and README.md state that PostgreSQL is not the active runtime and that SQLite remains authoritative; this PR does not change the server, SQLite code, or route cutover. No schema files, storage paths, logging, credentials, or security controls changed. The new citation behavior also matches the documented research invariant for Version-aware citations and exact-duplicate handling.

Full details: Offline And Sync Coherence

Explanation

PASS. The PR changes PostgreSQL Argument citation handling, not the current SQLite/browser offline path. The rollout status already marks Arguments and Stances as durable. The PostgreSQL sync handler preserves the documented aggregate model and adds explicit envelope validation, version refusals, stale-base convergence/conflict handling, and transactional create/replace behavior. New tests cover malformed envelopes, ledgered refusals with no mutation, stale pin conflicts, merged Works, and concurrent version-aware updates. The PR does not change service-worker, durable-queue, recovery, or offline projection code.

Full details: Docstring Coverage

Explanation

Docstring coverage is 42.11% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 19 functions across 7 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.

❤️ Share

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

@chatgpt-codex-connector

This comment has been minimized.

@Fooftilly Fooftilly moved this to Review in PRKS Roadmap Oct 27, 2026

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Fooftilly
Fooftilly merged commit e337b74 into master Oct 28, 2026
36 checks passed
@Fooftilly
Fooftilly deleted the claude/project-thread-o9yqq7 branch October 28, 2026 06:13
@github-project-automation github-project-automation Bot moved this from Review to Done in PRKS Roadmap Oct 28, 2026
@Fooftilly Fooftilly mentioned this pull request Oct 28, 2026
9 tasks

This branch was previously deployed

1 inactive deployment
project-automation — 7d8a9011 Deployed Oct 27, 2026 by Fooftilly via sync #1753
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