Digital Sovereignty for everyone

Database schema

Two custom tables, per-site (not centralised โ€” the atomic claim UPDATE joins against the posts table, and post IDs are only unique within a single site’s own posts table on Multisite): {$wpdb->prefix}lh_tasks_status and, as of 1.8.0, {$wpdb->prefix}lh_tasks_dependencies. Everything else lives in WordPress’s own posts, postmeta, terms/term_relationships/term_taxonomy tables (or their per-site equivalents on Multisite).

Post type

lh_tasks-task_post

Post status (native WordPress)

draft / pending / lh_tasks-active / private / future / trash. lh_tasks-active (added 1.6.0, replaces publish for this CPT) is required for a task to be claimable via claim_next_task(), and is registered public => false โ€” front-end read access is gated per-task via map_meta_cap, not blanket-public. create_task() defaults to lh_tasks-active; pass draft or pending for a task that shouldn’t be visible/claimable until reviewed.

Workflow status ({$wpdb->prefix}lh_tasks_status table)

Status key (namespace lh_tasks-) Label Notes
new_task New Initial state; claimable (subject to post_status = lh_tasks-active)
claim_task Claimed Set atomically by claim_next_task(); renew_task() (the renew-claim heartbeat) keeps a task in this status while pushing claimed_at forward
prog_task In Progress For workers that separate “claimed” from “started”; not set automatically
pend_task Pending Manual/human-workflow use. Also set automatically (1.8.0) when a task waiting in New loses a blocked_by prerequisite to Cancelled, the trash or deletion
revw_task Awaiting review Open (1.23.0, #150480). Set when someone other than the task’s author marks an assigned task done. The author or staff then accepts it (Resolved) or sends it back with a note (New, still assigned to the doer). Never claimable, and mark as done is refused while a task is in it.
fail_task Failed Set automatically when retries are exhausted, with the claim released (1.18.0; was block_task, “Blocked”, renamed so it is not confused with a task held back by a blocked_by prerequisite, which stays New and is skipped by claims). Open, for a person to triage; edit-task with status New and reset_retries requeues it with its full retries
res_task Resolved Set by complete_task(); terminal
clos_task Closed Manual final closure after resolution; terminal
cncl_task Cancelled Terminal
defr_task Deferred Parked for later โ€” deliberately worth doing eventually, not urgent now. Not in return_open_statuses() (won’t dilute the dashboard widget/default list) and not terminal (promotable back to New later via reassign_task())

Status key naming (e.g. pend_task not pending_task) is unchanged from when these were post_status values constrained to 20 characters โ€” the table column isn’t constrained the same way, but there was no reason to rename and break continuity with existing data/consumers. The one exception is block_task, renamed to fail_task in 1.18.0 because the old name was actively misleading.

Table columns (as of 1.6.0, table version 2.0): post_id (PK), status, claim_token, assigned_to (BIGINT UNSIGNED NULL โ€” hard requirement), assignee (BIGINT UNSIGNED NULL โ€” current claim holder), claimed_at, retry_count, max_retries, created_at, updated_at.

Table version 2.1 (1.18.0) changes data, not columns: migrate_2_0_to_2_1(), run once per site from maybe_create_table() on init, moves block_task rows to fail_task, and repairs two keys that edit-task stored before 1.17.1 validated statuses: can_task to cncl_task and resolved to res_task. These are plain row updates, so no progress-log entries are written and no status actions fire. The version is in the lh_tasks_status_table_version option.

Dependencies ({$wpdb->prefix}lh_tasks_dependencies table, 1.8.0)

One row per “task waits on blocker” edge, and the only store of blocked_by (it replaced the _lh_tasks-blocked_by post meta, which was migrated and deleted). Columns: task_id, blocker_id (primary key together), created_at; indexed on blocker_id. Version option lh_tasks_dependencies_schema_version. Rows for a permanently deleted task are removed in either column.

Taxonomies

  • lh_tasks_queue โ€” non-hierarchical, non-public. Routing/lane, e.g. email-triage. Admin column shown on the Tasks list screen.
  • lh_tasks_task_type โ€” non-hierarchical, non-public. Classification of the kind of work, e.g. github_triage. Distinct from queue: a queue can carry more than one task type and vice versa. Admin column shown on the Tasks list screen.
  • lh_tasks_priority (1.15.0) โ€” a locked set of four terms (low, normal, high, urgent), ranked by term meta order (10, 20, 30, 40). Each task carries one. Version option lh_tasks_priority_schema_version. See Architecture notes.

Post meta (all keys prefixed _lh_tasks-)

Meta key Type Description
payload JSON string Machine-actionable spec for agent tasks: the ability to run and its params
result JSON string Output on completion (since 1.23.0 the completion note, for a task marked done by a person), or an error object on a retryable failure. A stale error is cleared when the task is later completed
due_date MySQL datetime Optional
source_plugin string Slug of the plugin that created this task, e.g. lh-crm
source_post_id int ID of the originating post in the source plugin
skip_review int (1) Set when the task was created with requires_review false: the assignee’s done resolves it without a review step. 1.23.0
progress_log array of objects Appended to by reassign_task(), update_task(), add_note(), complete_assigned_task(), review_task(), LH_Tasks_dependencies_class::flag_dependents(), LH_Tasks_follow_up_class, renew_task() (only when a note is given), and edit_task(). Each entry: timestamp, event (reassigned, heartbeat, admin_override, prerequisite_dropped, note (1.10.0), marked_done (1.11.0, when the assigned member marks it done from their profile, with their note if they gave one), follow_up and follow_up_escalated (1.16.0, written by the daily follow-up with actor 0, and never counted as activity), submitted_for_review, review_accepted and review_sent_back (1.23.0, the review step, each with to_status), or updated, which also records fields and, if the task was claimed, claimed_by), from_status/to_status (reassigned, admin_override and prerequisite_dropped only), from_assignee/to_assigned_to (reassign only), retries_reset_from (admin_override with reset_retries, 1.18.0: the retry count before it was reset), via (1.23.0: mcp when a completion or review came through an ability rather than the task page), actor (WP user ID), note
follow_up array Where an assigned task is in the follow-up cycle, including when the last follow-up was sent (at); activity after that time starts the cycle again. 1.16.0
claim_started Unix timestamp When the current claim was made, set on lh_tasks_task_claimed; the start of the maximum lease for automatic renewal. 1.17.0
ability_calls int Count of write ability calls made with this task (succeeded or failed, not refused). 1.17.0
last_ability array The last such call: ability, time_gmt, success, renewed. 1.17.0

assignee, assigned_to, claim_token, claimed_at, retry_count, and max_retries live in the status table, not postmeta โ€” see above. The follow-up’s BuddyPress notifications carry two notification meta keys, lh_tasks_blog_id and lh_tasks_link, recording the task’s site and link (1.16.0).

If you like this plugin please consider making a donation to further development

Install LHero.org

lhero.org

Open it straight from your home screen, full screen and quicker to load.

  1. Tap the Share button . If you cannot see it, tap โ€ขโ€ขโ€ข first.
  2. Scroll down and tap Add to Home Screen.
  3. Tap Add.
  1. Tap the browser menu โ‹ฎ.
  2. Tap Install app or Add to Home screen.
  1. In the menu bar, choose File, then Add to Dock.

Use your browser's menu to install this site or add it to your home screen.