Issue 1557 — Active-task canonical entity labels

Problem

The facts-backed active-task ledger stores one category=project fact per entity/key. Supersession previously matched only the exact entity string, so case and naming variants such as humanizer / Humanizer or hybrid-memory / Hybrid-Memory PR Queue were treated as separate tasks. When a task was closed under one variant, older active status/next facts under another variant stayed current and kept appearing in the active-task projection.

Implemented fix

This is the implementation PR for the runtime active-task canonicalisation fix, not a docs-only scaffold.

  • Added canonicalLabel(entity) for active-task entities:
    • trims and lower-cases labels
    • strips trailing PR Queue, pull request queue, pr-stewardship, and pull request stewardship suffixes
    • collapses whitespace, underscores, and dashes to a single dash separator
  • Added case-insensitive / separator-tolerant terminal status normalisation via normalizeTaskStatus.
  • Updated project fact grouping to group active-task rows by canonical label, while preserving a human-facing display label from the newest variant.
  • Updated upsertProjectTaskKey to find and supersede previous facts by canonical label + key instead of exact entity + key.
  • Updated terminal status writes (done, completed, cancelled, closed, abandoned, superseded, case-insensitive) to supersede all current facts for the canonical task label, not only exact entity matches.
  • Stored canonical label metadata in provenanceJson for new active-task facts as { activeTask: { canonicalLabel }, canonical_label } so future grouping has an explicit canonical key while legacy rows still fall back to computed canonical labels.
  • Added active-tasks backfill-canonical-labels CLI command for facts ledgers. It backfills canonical label metadata for existing project rows, collapses duplicate key facts by canonical label, and if a terminal status exists, supersedes all other current facts in that canonical group.

Regression coverage

Added tests in extensions/memory-hybrid/tests/task-ledger-facts.test.ts for:

  • canonicalLabel collapsing case, separators, and active-task suffix variants.
  • case-insensitive terminal status mapping (Done, COMPLETED, etc.).
  • groupProjectFactsByEntity collapsing humanizer / Humanizer rows.
  • upserting Humanizer as DONE suppressing legacy humanizer active rows from loadTaskLedgerFromFacts active projection.
  • backfillActiveTaskCanonicalLabels collapsing legacy hybrid-memory, Hybrid-Memory, and Hybrid-memory PR Queue variants.
  • backfill preserving existing provenance metadata and choosing non-superseded keepers when collapsing duplicates.

Verification run

From extensions/memory-hybrid:

npm test -- --run tests/task-ledger-facts.test.ts
# 1 passed, 15 tests passed

npm test -- --run tests/stage-active-task.test.ts tests/active-task-checkpoint.test.ts
# 2 passed, 19 tests passed

npm run format:check -- services/task-ledger-facts.ts cli/active-tasks.ts tests/task-ledger-facts.test.ts
# passed

npm run build
# passed

npm run lint -- services/task-ledger-facts.ts cli/active-tasks.ts tests/task-ledger-facts.test.ts
# exited 0; repository-wide warnings remain pre-existing

Operator note

After merging/deploying, run the maintenance command against the live facts ledger:

openclaw hybrid-mem active-tasks backfill-canonical-labels --dry-run
openclaw hybrid-mem active-tasks backfill-canonical-labels

This command refreshes the rendered ACTIVE-TASKS.md projection after applying changes.


Back to top

OpenClaw Hybrid Memory — durable agent memory

This site uses Just the Docs, a documentation theme for Jekyll.