Skip to content

Verification: Kanban Cards with Deleted Items — Phase 1 (backend)

Acceptance criteria and test plan for Phase 1. Implementation detail is in specification.md. Each acceptance scenario (AC) maps to a goal success criterion (SC).

AC-1 — Scan/open a deleted-item card resolves (SC-1)

Section titled “AC-1 — Scan/open a deleted-item card resolves (SC-1)”

Given an item with a card, when the item is deleted and the card is then read (GET /v1/kanban-card/details?cardEId=…, by-record, and the deprecated …/{cardEId}/details), then the response is 200 with the item’s last-known name, item.retired = true, and item.provenance set — no 500. Covers a card retired via this build’s observer and a card whose item was deleted before this build shipped (pre-existing backlog).

AC-2 — List/by-status/by-page tolerates deleted-item cards (SC-2)

Section titled “AC-2 — List/by-status/by-page tolerates deleted-item cards (SC-2)”

Given a page that includes one or more deleted-item cards, when listWithDetails (generate / by-status / by-page) runs, then the full page returns 200 with each deleted-item card marked; a single deleted-item card never fails the page, and a genuinely-absent (corrupt) item is skipped, not page-failing.

AC-3 — Reference reports retired + provenance, correct even when cache is stale (SC-3)

Section titled “AC-3 — Reference reports retired + provenance, correct even when cache is stale (SC-3)”

Given a deleted-item card whose denormalized cache is stale or empty (e.g. legacy retired = false), when it is read, then the response’s item.retired and item.provenance are correct because they are coalesced from the resolved item record (the tombstone carries the deletion author/asOf). The cache is a hint, not the source of truth.

AC-4 — Cache self-heal on single-card read (SC-3)

Section titled “AC-4 — Cache self-heal on single-card read (SC-3)”

Given a stale-cache deleted-item card, when it is read via the single-card detail path, then the reference is persisted with the corrected retired/provenance/rId; a concurrent-version conflict is swallowed (the read still returns 200). List reads do not trigger inline heal writes.

AC-5 — Print reflects deletion on both surfaces (SC-4)

Section titled “AC-5 — Print reflects deletion on both surfaces (SC-4)”

Given a deleted item, when its kanban card is printed, then the card print succeeds and carries item_retired = true + last-update provenance. And when the item’s Label / Breadcrumb is printed, then it succeeds (no “Some items not found for printing”) and carries is_retired = true + provenance.

AC-6 — No regression for live items (SC-5)

Section titled “AC-6 — No regression for live items (SC-5)”

Given items that are not deleted, when cards are read/listed/printed, then behavior and contract are unchanged; new reference fields default to not-deleted (retired = false, provenance populated by the observer on the next update or null).

Unit / integration (operations + common-module, Kotest + ContainerizedPostgres)

Section titled “Unit / integration (operations + common-module, Kotest + ContainerizedPostgres)”
TestTargetValidatesAC
Provenance component round-tripprovenanceComponent (common-module)persists/reads updatedBy/updatedAt; requiring(...)null when both columns null, error when mixed
Reference round-trips deletion fieldsItemReferenceComponentretired/provenance persist & read back on kanban_card; default retired = falseAC-1/6
order_line migration safetyorder_linecolumns added (default/null); order read/write unaffectedAC-6
Propagation on deleteItemListener.deletedItemevery cardsForItem card gets retired = true, provenance from the delete record, refreshed rIdAC-1/3
Propagation on updateItemListener.updatedItemnon-delete update sets provenance (and retired = false) uniformlyAC-3/6
Single-card resolve — believed-live retiredServiceImpl.detailsForlegacy retired = false + deleted item → includeRetired = true resolves tombstone → marked correctly, no 500AC-1/3
Single-card resolve — known retiredServiceImpl.detailsForretired = truegetRecord(rId) tombstone path → marked correctlyAC-1
Coalesce-from-recordcomposeDetailsresponse retired/provenance come from the resolved record, overriding a stale cacheAC-3
Single-card healServiceImpl.detailsForstale single-card read persists corrected reference; injected ConflictingState is swallowed, read still 200AC-4
List tolerates deleted + missingServiceImpl.listWithDetailspage with retired-item cards resolves fully (partitioned); no inline heal writes; corrupt item skippedAC-2/4
Card print carries deletionKanbanCardPrinter.renderitem_retired/item_last_updated_by/item_last_updated_at from coalesced itemAC-5
Item print carries deletionItemPrinter.render + ItemPrintingServiceretired item resolves (not “not found”); is_retired/provenance populatedAC-5
TestMethodPathExpectedAC
Scan deleted-item cardGET/v1/kanban-card/details?cardEId=…200, item.retired = true, name presentAC-1
List with a deleted-item cardPOST/v1/kanban-card/details/generate200, page includes the marked cardAC-2
ScenarioSetupValidatesAC
Delete → scancreate item+card, delete item, detailsForno 500; marked deleted; name preserved; provenance = delete author/timeAC-1/3
Pre-existing deletionseed a card with retired = false whose item is already retired (no fresh event)read still marks it correctly (coalesce); single read heals the cacheAC-3/4
  • After release, optionally audit kanban_card for item_reference_retired = true vs cards still showing stale false to gauge heal coverage (expected to trend to zero as cards are read). A per-tenant API “refresh” (re-save cards) can warm the cache if ever needed — not required for correctness.

Front-end rendering of the marker; procurement/order-read behavior; new-order prevention for deleted items; N1/N3/N4 nice-to-haves.


Copyright: (c) Arda Systems 2025-2026, All rights reserved