Skip to content

list_tasks in the registry does a sequential ID scan; deleted/non-existent IDs silently shrink pages without warning #40

Description

@cybermax4200

Labels: type: bug, type: api-design, difficulty: intermediate, area: task-registry

Why this matters now

list_tasks is the primary API for backend indexers to page through all tasks (Phase 4 backend integration). If any task ID is missing from storage (expired TTL — see Issue 3, or a future delete path), the page returned has fewer items than limit without indicating whether there are more tasks after the gap. An indexer has no way to distinguish "end of list" from "gap in IDs" from a short page.

Problem / What

list_tasks in contracts/task-registry/src/registry.rs:

while current < count && remaining > 0 {
    if let Some(task) = storage::read_task(&e, current) {
        tasks.push_back(task);
    }
    current += 1;
    remaining -= 1;
}

remaining is decremented whether or not a task was found at current. So a page of limit=10 starting at cursor=0 where IDs 3 and 7 are missing returns only 8 tasks, not 10. The caller has no signal that the short page was due to gaps rather than the end of the list. A backend using page.len() < limit as "stop paginating" will silently drop all tasks after the first gap.

Key Challenges

  • The fix is to decrement remaining only when a task is successfully pushed: if let Some(task) = ... { tasks.push_back(task); remaining -= 1; }. This is a one-line fix in the loop body — but requires updating the existing pagination tests to verify the corrected behaviour.
  • The cursor semantics change slightly: cursor is now "lowest task id to start from" and the page is filled until limit tasks are found or count is exhausted. This must be documented.
  • A new test with a gap in task IDs (e.g., cancel a task and check it can still be listed, or simulate a missing ID) is needed.

Acceptance Criteria

  • list_tasks decrements remaining only when a task is found, not on every iteration.
  • list_tasks returns exactly limit tasks (or fewer only at end-of-list, not at gaps) when tasks are missing at intermediate IDs.
  • Existing pagination tests (test_list_tasks_pagination, test_list_tasks_full_scan) pass.
  • New test test_list_tasks_with_gaps creates 5 tasks, simulates 2 missing IDs, and asserts the page still returns the correct number of found tasks.
  • Docstring on list_tasks updated to describe the gap-skipping behaviour.

Relevant files / functions

File Symbol
contracts/task-registry/src/registry.rs list_tasks

Out of scope

  • Task deletion (tasks are never deleted, only status-changed)
  • Changes to get_tasks_by_creator_paged

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions