mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-22 02:23:46 +00:00
0bf710eea5
* fix(store): hide item_links pointing to soft-deleted items (BUG-734)
Item-link queries that JOIN against `items` now also filter on
`deleted_at IS NULL` for both source and target. This prevents
`pad item related`, the lineage breadcrumb, and dashboard enrichment
from surfacing dangling endpoints when one side has been archived.
Affected queries in internal/store/items.go:
- GetItemLinks (powers `pad item related`, lineage, dashboard)
- GetItemLink (singular; fixed for consistency)
- GetParentForItem (breadcrumb / lineage; archived parent reads as none)
Other item_links queries already filtered on deleted_at; export.go
deliberately keeps all rows for backup correctness — left unchanged.
The link rows themselves are preserved on disk, so restoring a
soft-deleted item resurrects its relationships automatically.
Tests:
- TestItemLinks_HidesSoftDeletedEndpoints — delete + restore round-trip
on both source-side and target-side
- TestGetParentForItem_HidesSoftDeletedParent — parent breadcrumb path
Manually verified: PLAN + TASK with `implements` link, soft-delete the
TASK, `pad item related <PLAN>` correctly returns no implementers.
* fix(store): address Codex review findings on PR #259 (BUG-734)
Three follow-ups from Codex's review of the soft-delete filter on item-link
queries:
1. MEDIUM — GetParentMap now JOINs items on both sides and filters on
deleted_at IS NULL. handlers_dashboard.go uses this map directly to
detect orphaned tasks (items not present in the map are flagged), so
without the filter a task whose parent had been soft-deleted would
silently fail to appear as orphaned.
2. LOW — Revert the deleted_at filter on getItemLink (lowercase, private).
Its only caller is the post-insert readback in CreateItemLink, which
means filtering buys nothing user-facing and introduces a delete-race
window where a successful INSERT returns nil. SetParentLink's readback
was switched from GetItemLinks to getItemLink for the same reason.
User-facing surfaces still go through GetItemLinks (plural) and
GetParentForItem, both of which retain the filter.
3. LOW — Add an explicit comment in export.go documenting that item_links
are exported in full (including links to soft-deleted items), and why
that intentionally diverges from the user-facing query behavior.
Tests: TestGetParentMap_ExcludesSoftDeletedEndpoints exercises the
dashboard regression path on both source-side and target-side soft-delete,
plus the restore round-trip.
* fix(store): reject soft-deleted parent in ListItems UUID parent filter (BUG-734)
Codex review on 288283b flagged that ListItems(parent=<UUID>) at items.go:534
runs an EXISTS subquery against item_links without checking whether the
target parent is soft-deleted. Slug/ref input rejects deleted parents
upstream via GetItem/GetItemBySlug, but raw-UUID input bypasses that path
and would still return active children of an archived parent.
Fix: JOIN items into the EXISTS subquery and require deleted_at IS NULL on
the parent.
Test: TestListItems_ParentFilter_RespectsSoftDeletedParent — covers the
delete + restore round-trip on the parent.
* fix(store): apply parent-filter in FTS path so search+parent enforces deleted-parent rejection (BUG-734)
Codex's 3rd review pass on PR #259 caught that listItemsFTS does not
re-apply ParentLinkID. Combining `parent=<UUID>&search=<q>` therefore
silently dropped the parent constraint — and, by extension, the
deleted-parent rejection introduced earlier in this PR.
Fix: replay the same EXISTS-with-deleted_at-IS-NULL predicate in the FTS
branch. Test: TestListItems_ParentFilter_FTS_RespectsSoftDeletedParent
covers the delete + restore round-trip on the search path.
The wider FTS filter-bypass (Tags, AssignedUserID, AgentRoleID, Fields,
ParentID are all silently dropped when search is set) is pre-existing
behavior outside BUG-734's scope; tracked as BUG-812.