feat(infra): add task-completion-routes SQLite registry module#93898
feat(infra): add task-completion-routes SQLite registry module#93898wangmiao0668000666 wants to merge 2 commits into
Conversation
Adds a new 'task_completion_routes' table to the shared state DB and a src/infra/task-completion-route.ts module exposing four idempotent operations: - registerTaskCompletionRoute(input) - called once at task prep / first delivery-target resolution - resolveTaskCompletionRoute(taskId) - called by the deliverer as a fallback when in-memory routing is missing - noteRouteDeliveryAttempt(taskId, status) - debug telemetry - retireTaskCompletionRoute(taskId) - called in finally blocks so unretired rows do not accumulate This is foundation only - no behavior change. No call site is wired to the registry yet. That is the next PR (which will close openclaw#92460, openclaw#92076, and openclaw#93323 by hooking the registry into the cron and subagent completion delivery paths). Schema: - New table task_completion_routes(task_id PK, source, channel, to_target, account_id, thread_id, registered_at, retired_at, delivery_attempts, last_delivery_status, last_delivery_at) - Two partial indexes: active lookup (retired_at IS NULL) and source+age for orphan pruning Module guarantees: - register is idempotent on duplicate task_id - resolve returns null for retired rows - note and retire are idempotent - pruneOrphanedRoutes deletes unretired rows older than threshold Tests: 9 unit tests covering register, resolve, note, retire, prune, durability (rows survive across DB connections), and validation (missing taskId/source throw).
|
@clawsweeper re-review Added the The 2 other CI failures ( |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Closing this PR. It does not close any issue on its own (pure infra foundation), and the 2-PR split does not deliver the intended benefit:
Closing both #93898 and #93899. The hookup logic in #93899 is being repackaged into a single PR that closes the 3 issues directly (no separate foundation PR). |
Summary
Adds a new
task_completion_routestable to the shared state DB and asrc/infra/task-completion-route.tsmodule exposing four idempotent operations:registerTaskCompletionRoute(input)— called once at task prep / first delivery-target resolutionresolveTaskCompletionRoute(taskId)— called by the deliverer as a fallback when in-memory routing is missingnoteRouteDeliveryAttempt(taskId, status)— debug telemetryretireTaskCompletionRoute(taskId)— called in finally blocks so unretired rows do not accumulateThis is foundation only — no behavior change. No call site is wired to the registry yet. That is the next PR (
fix/cron-subagent-announce-fallback, stacked on this branch) which will close #92460, #92076, and #93323 by hooking the registry into the cron and subagent completion delivery paths.Changes
src/state/openclaw-state-schema.sql(+25) — newtask_completion_routestable + 2 partial indexes (active lookup, source+age for orphan pruning)src/state/openclaw-state-db.generated.d.ts(+16) — auto-generated Kysely typessrc/state/openclaw-state-schema.generated.ts(+27) — auto-generated SQL re-exportsrc/infra/task-completion-route.ts(+207, new) —register/resolve/note/retirewith idempotency guards onretired_at IS NULLsrc/infra/task-completion-route.test.ts(+258, new) — 9 unit tests covering register, resolve, note, retire, prune, durability, validationSchema
idx_task_completion_routes_active(lookup active non-retired rows) andidx_task_completion_routes_source(orphan prune by source+age).Module guarantees
registeris idempotent on duplicatetask_id(returns{ registered: false, reason: "duplicate_task_id" })resolvereturnsnullfor retired rows (retired rows are hidden from the fallback path; raw row is kept for audit)noteandretireare idempotentpruneOrphanedRoutes(maxAgeMs)deletes unretired rows older than thresholdReal behavior proof
Behavior addressed: This PR adds a new SQLite-backed registry module (
src/infra/task-completion-route.ts) that the cron and subagent completion deliverers will use as a durable fallback when the in-memorydeliveryContextis lost between task prep and delivery. No existing behavior changes — no call site is wired to the new module in this PR. The next stacked PR (PR 2) hooks the registry into the cron and subagent paths and closes the three related P1 issues.Real environment tested:
Linux 4.19.112-2.el8.x86_64, Nodev22.22.0,pnpm exec vitestagainst the production module + production code paths. The test file useswithTempDirSync+openOpenClawStateDatabaseto spin up a realnode:sqlitedatabase on disk per test, then exercises the module's register/resolve/note/retire/prune API end-to-end (no mocks for the registry, no in-memory fakes).Exact steps or command run after this patch:
Evidence after fix:
9/9 unit tests pass against a real
node:sqlitedatabase (perwithTempDirSync+openOpenClawStateDatabase):The 9 tests cover: register-then-resolve roundtrip, duplicate
taskIdhandling, resolve returnsnullfor unknown, resolve returnsnullafter retire,noteRouteDeliveryAttemptincrements counter and stamps status, retire idempotent,pruneOrphanedRoutesonly deletes threshold-expired rows, durability (rows survive across DB connections — verified by closing the Kysely handle and opening a freshnode:sqliteconnection), and validation (missingtaskId/sourcethrow).Observed result after fix: The new module is usable from any caller that imports it. The 4 idempotent operations work as documented; partial indexes make the active-row lookup and orphan-prune queries efficient; rows survive a database close + reopen (durability proven via a fresh
node:sqliteconnection reading the same SQLite file). The deliverer wiring is intentionally not in this PR — that is the next stacked PR.What was not tested: No user-facing behavior change in this PR. The next stacked PR (PR 2) provides end-to-end live repros (
scripts/repro/issue-92460-*.mtsandscripts/repro/issue-92076-*.mts) that exercise the full failure cascade against a real SQLite state DB with the registry as the authoritative fallback. The 2 pre-existing CI failures on this PR (check-additional-boundaries-afor prompt snapshot drift,checks-node-core-toolingfor thecore-runtime-infra-miscshard missing from the test plan) are present onmainand are unrelated to this PR — seegit diff main...HEADfor verification.Verification
Pre-existing CI failures (unrelated to this PR)
Two CI checks fail on this PR but are not caused by it:
check-additional-boundaries-afails onprompt:snapshots:check(6 prompt snapshot files differ from generated output). These files are not modified by this PR; the drift is pre-existing onmain. Confirmed bygit diff main...HEAD -- test/fixtures/agents/prompt-snapshots/.checks-node-core-toolingfails on thecore-runtime-infra-miscshard being absent fromtest/scripts/ci-node-test-plan.test.ts. The shard list is not modified by this PR; the test plan staleness is pre-existing onmain. Confirmed bygit diff main...HEAD -- test/scripts/ci-node-test-plan*.Both will resolve once the main repo's drift is fixed (or via a one-line follow-up PR to the test plan / prompt snapshot generator).
Branch
feat/task-completion-routes-modulebased on latestorigin/main.