Skip to content

Confirm whether dependency/connection writes bump a card's resource version — if so, flush card PATCHes before them #107

Description

@thewrz

This was written agentically; verify its assertions and edit accordingly:

Follow-up from #105 (PR #106). #105 fixed the recovery (the 409/428 retry could not validate a /description op, so the run aborted). This issue is about the cause of the staleness.

The suspicion

sync.main() snapshots each card version (list_cards, or _created_card_snapshot for a card created this run), then:

  • step 3 sync_child_connections -> POST /card/connections
  • step 4 sync_dependencies -> POST /card/dependency
  • step 5 flush -> ONE versioned PATCH /card/{id} per card, using the pre-POST version

In the live 2026-07-29 failure the dependency POST was the only write that touched the card between its snapshot refetch and the flush, and the server reported actualValue: 2 against a sent 1. If those POSTs bump the card resource version, then every run where a card both gains/loses a dependency (or child connection) and carries queued ops eats a failed PATCH + a refetch + a retry, plus a note ... version bumped by an unrelated change line that is not true — the run bumped it itself.

Not yet measured, so this is a hypothesis: an asynchronous server-side bump shortly after card creation would produce the same evidence.

Work

  1. Probe live (fact-finding, throwaway card): read version, POST /card/dependency, read version again; same for POST /card/connections. Record the result in docs/API-VALIDATION.md (the issue Version-conflict retry can never recover a card carrying a /description op — apply run aborts on HTTP 428 #105 section flags this as UNCONFIRMED).
  2. If confirmed, prevent rather than recover, the same reasoning as intake._card_for_link_write: move the step-5 flush ahead of steps 3-4 (nothing in those steps reads what the flush writes; poisoned_card_ids/managed_card_ids are already computable right after step 2), or refetch the version for just the affected cards. Verify the poisoned-card hold and the step-6 comment-sync clean gate are unaffected.
  3. If not confirmed, record what actually bumps it and close — Version-conflict retry can never recover a card carrying a /description op — apply run aborts on HTTP 428 #105 recovery already keeps such runs correct.

Depends on #107 if the probe should live in smoke.py.

Testing

  • Live version probe across a dependency POST and a connections POST, recorded in docs/API-VALIDATION.md
  • If reordering: a sync.main() test asserting no card is written between its snapshot and its flush PATCH
  • Poisoned-card hold + comment-sync gate unchanged
  • Full suite green
  • Live --apply run shows no version bumped note where none is warranted

🤖 Co-authored by Claude Opus 5.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions