get_task(task_id) returns the full TaskInfo, and today every client (dashboard, indexer, orchestrator UI) re-derives a task's status from a mix of fields: completed, spent vs plan_cost, and — for staleness — created_at versus the current ledger time plus the configurable stale threshold. That inference is duplicated everywhere and easy to get subtly wrong. The riskiest part is the "is this task force-completable right now?" question, because it must exactly match the on-chain rule the contract enforces.
Goal
Expose a single authoritative view that computes status on-chain: get_task_status(env, task_id) -> Option<TaskStatus>, where TaskStatus is a #[contracttype] enum with at least Active, Stale, and Completed.
Requirements & constraints
- The
Active vs Stale boundary must be identical to force_complete_stale_task's eligibility rule at lib.rs:774 (currently elapsed > threshold, using get_stale_threshold). If the two ever disagree, a UI would offer a "force complete" button that the contract then rejects — so this equivalence is the crux of the issue, not an afterthought. Consider factoring the eligibility comparison into a shared internal helper that both force_complete_stale_task and get_task_status call, so they cannot drift.
Completed is returned for any finalized task (completed, cancelled, or force-completed) regardless of elapsed time.
- Unknown task IDs return
None.
Edge cases to consider
- A task exactly at the threshold boundary (
elapsed == threshold) — must land on the same side as the contract's force-complete check.
- A task that is both past-threshold and already completed —
Completed wins.
- The stale threshold having been changed by the admin after the task was created.
Acceptance criteria
Notes for contributors
If you'd like to work on this, comment below so we can assign it to you.
Questions welcome — see CONTRIBUTING.md for setup
and workflow.
get_task(task_id)returns the fullTaskInfo, and today every client (dashboard, indexer, orchestrator UI) re-derives a task's status from a mix of fields:completed,spentvsplan_cost, and — for staleness —created_atversus the current ledger time plus the configurable stale threshold. That inference is duplicated everywhere and easy to get subtly wrong. The riskiest part is the "is this task force-completable right now?" question, because it must exactly match the on-chain rule the contract enforces.Goal
Expose a single authoritative view that computes status on-chain:
get_task_status(env, task_id) -> Option<TaskStatus>, whereTaskStatusis a#[contracttype]enum with at leastActive,Stale, andCompleted.Requirements & constraints
ActivevsStaleboundary must be identical toforce_complete_stale_task's eligibility rule atlib.rs:774(currentlyelapsed > threshold, usingget_stale_threshold). If the two ever disagree, a UI would offer a "force complete" button that the contract then rejects — so this equivalence is the crux of the issue, not an afterthought. Consider factoring the eligibility comparison into a shared internal helper that bothforce_complete_stale_taskandget_task_statuscall, so they cannot drift.Completedis returned for any finalized task (completed, cancelled, or force-completed) regardless of elapsed time.None.Edge cases to consider
elapsed == threshold) — must land on the same side as the contract's force-complete check.Completedwins.Acceptance criteria
TaskStatusenum defined with#[contracttype]get_task_statusreturnsNonefor unknown IDs and the correct variant otherwiseActive/Staleboundary provably matchesforce_complete_stale_task(ideally via a shared helper)env.ledger()) to exercise the Active → Stale transition, including the exact boundary, plus the completed case and a post-change thresholdcargo testandcargo clippy --all-targets -- -D warningspassNotes for contributors
If you'd like to work on this, comment below so we can assign it to you.
Questions welcome — see CONTRIBUTING.md for setup
and workflow.