The project's README from github.com/MaxHu-xuan/task-state-guard. As one markdown file: README.md.
中文 · English · 运行合成演示 · Technical reference
服务重启后,running 任务和 pending 投递很容易继续留在界面里,却没人能确认它们
是否真的还在工作。TaskStateGuard 是面向 AI 智能体、后台 worker 和本地工作流的
嵌入式 SQLite 状态对账护栏。它把任务终态与投递终态分开,按明确的截止时间和
宽限期收敛遗留状态;你可以先看只含汇总数字的预览,再决定是否应用,doctor
健康检查同样不会列出任务明细。
它解决的是任务状态对账(task state reconciliation)和重启后卡住任务的状态收敛 (stuck task recovery)。它不是队列、执行器或计算续跑工具,也不承诺恰好一次;系统没有 观测到成功或投递回执时,TaskStateGuard 不会把未知结果猜成成功或已送达。
| 你需要解决的问题 | 选择 |
|---|---|
| 重启后核对卡住任务、超时与待投递状态,不把未知结果猜成成功 | TaskStateGuard(任务状态守护),当前项目 |
| 分享或迁移聊天导出前,本地检查疑似秘密、个人信息形态、格式、SQLite 与扫描盲区 | ChatArchiveGuard(聊天归档守护) |
| 确认最终 PPTX 及随附验收证据仍匹配结构检查后生成的 HMAC 签名收据 | ArtifactProof(PPTX 交付物验真) |
三个项目都可以独立使用;它们分别守护状态、聊天归档和 PPTX 交付物,不会互相读取数据。
| 你遇到的问题 | TaskStateGuard 给出的结果 |
|---|---|
服务重启后,一批任务一直显示 running |
超过截止时间或活跃宽限期的任务转为 timed_out;仍然新鲜的任务保持不变 |
| 任务已经结束,但用户是否收到结果并不确定 | 投递状态继续保持 pending,直到真实回执或投递宽限期到期 |
| 内部子任务完成后不应单独向用户投递 | 终态内部任务在宽限期后转为 not_applicable,不会伪装成已送达 |
| 收敛遗留状态前需要知道会改动多少记录 | reconcile --dry-run 返回同一时刻将发生的聚合计数,不修改任务、事件或数据库 |
| 运维需要判断工作流账本是否可信 | doctor 检查数据库、schema、状态、时间戳和事件链,只返回聚合计数 |
| Linux、macOS 和 Windows 需要共用一套状态语义 | 状态机一致;文件保护按 POSIX 权限或外部管理的 Windows DACL 分别处理 |
适合以下场景:
任务状态:
queued ──> running
│ │
└───────────┴──> succeeded | failed | timed_out | cancelled
任务可以从 queued 或 running 进入终态。终态不可改写;重复写入相同终态是幂等的,
写入冲突终态会被拒绝。
投递状态:
pending ──> delivered | failed | not_applicable
投递状态只能在任务进入终态后关闭。外部投递任务只有收到真实传输回执后才能标记为
delivered;无需直接投递的内部任务只能标记为 not_applicable。
这两套状态机回答不同问题:
对于已经发布到 PyPI 的版本,普通用户建议在虚拟环境中安装 TaskStateGuard。
Linux 或 macOS:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install task-state-guard
Windows PowerShell:
py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install task-state-guard
如需审阅尚未发布的修改或参与开发,请先确认源码来源和目标提交,再在已核对的源码仓库
根目录创建独立虚拟环境,并将最后一条安装命令改为 python -m pip install .
(Windows 使用 .\.venv\Scripts\python.exe -m pip install .)。
支持 CPython 3.11 至 3.14。运行时代码只使用 Python 标准库,不包含网络请求或遥测。
演示脚本随源码仓库提供,不包含在 wheel 中。请从仓库根目录运行;它只使用固定的公开
样例状态,在系统临时目录创建 SQLite,调用真实 CLI 完成
preview、apply、幂等复查和 doctor,结束后自动删除临时数据库。它不读取本机任务数据,
也不包含提示词、消息、任务正文或真实标识。
Linux 或 macOS:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src python3 examples/restart_reconciliation_demo.py
Windows PowerShell:
$env:PYTHONDONTWRITEBYTECODE = "1"
$env:PYTHONPATH = "src"
py -3 .\examples\restart_reconciliation_demo.py --allow-external-acl
真实演示中的前三个聚合结果如下;<temporary-demo> 是刻意使用的、不包含本机信息的
路径占位符:
[preview] output: {"ok":true,"reconcile":{"active_examined":2,"applied":false,"deliveries_failed":1,"deliveries_not_applicable":1,"dry_run":true,"fresh_active_retained":1,"pending_delivery_examined":3,"pending_delivery_retained":1,"tasks_timed_out":1}}
[apply] output: {"ok":true,"reconcile":{"active_examined":2,"applied":true,"deliveries_failed":1,"deliveries_not_applicable":1,"dry_run":false,"fresh_active_retained":1,"pending_delivery_examined":3,"pending_delivery_retained":1,"tasks_timed_out":1}}
[idempotent-recheck] output: {"ok":true,"reconcile":{"active_examined":1,"applied":true,"deliveries_failed":0,"deliveries_not_applicable":0,"dry_run":false,"fresh_active_retained":1,"pending_delivery_examined":1,"pending_delivery_retained":1,"tasks_timed_out":0}}
第三次没有再次改写已经闭合的记录;仍保留的 queued 和刚刚进入 timed_out 的投递解释了
两个 retained 计数。完整命令与只含汇总数字的 doctor 输出由脚本直接打印,并在测试中
连续运行两次验证一致性。
from task_state_guard import Ledger
ledger = Ledger("./private-state/tasks.sqlite")
task = ledger.create_task(timeout_seconds=900)
ledger.start_task(task.id)
# 外部 worker 确认工作成功后再写入任务终态。
ledger.close_task(task.id, "succeeded", code="worker_completed")
# 传输层收到真实回执后再写入投递终态。
ledger.set_delivery(
task.id,
"delivered",
code="transport_acknowledged",
)
在 Windows 上,数据库父目录必须提前存在,并由管理员或部署系统设置为仅服务账户可访问的
私有 DACL。只有确认该边界已经建立后,才传入 allow_external_acl=True。
task-state-guard --db ./private-state/tasks.sqlite reconcile \
--active-grace-seconds 600 \
--delivery-grace-seconds 600 \
--dry-run
task-state-guard --db ./private-state/tasks.sqlite reconcile \
--active-grace-seconds 600 \
--delivery-grace-seconds 600
task-state-guard --db ./private-state/tasks.sqlite doctor
--dry-run 使用与实际执行相同的判断规则,但不会修改任务、事件或数据库。时间会继续前进,
并发 worker 也可能更新状态,因此预览只代表预览时刻;执行前应核对返回的 dry_run 和
applied 字段。
CLI 的 dry-run 会以只读模式打开一个已经存在、已经采用 WAL 且权限边界合格的账本;它
不会创建数据库、收紧文件权限或迁移 schema。安全的 schema v1 可以直接预览并保持为
v1;普通执行仍按原有默认行为迁移到 v2。嵌入 Python 时,如果连构造阶段也必须只读,请
使用 Ledger(path, read_only=True),并只调用读取操作或 dry_run=True 的收敛。
数据库已干净关闭且没有 WAL/SHM 时,TaskStateGuard 会对不超过 64 MiB 的主库做两次 身份、时间和 SHA-256 一致性读取,再把私有副本载入内存;不会用 SQLite 的 immutable 模式直接读取可能并发变化的源文件,也不会创建临时副本。读取期间出现 writer 或 checkpoint、内存反序列化不可用、或超过上限时都会安全失败。已有完整 WAL/SHM 时仍使用 SQLite 的正常只读锁语义。
Windows CLI 需要把 DACL 确认参数放在子命令之前:
.\.venv\Scripts\task-state-guard.exe `
--db .\private-state\tasks.sqlite `
--allow-external-acl `
reconcile --active-grace-seconds 600 --delivery-grace-seconds 600 --dry-run
--allow-external-acl 只确认调用方已配置私有 DACL,不会创建、检查或认证 DACL。
| 平台 | 源码与虚拟环境命令 | 文件保护与 CLI 注意事项 |
|---|---|---|
| Linux | 通常使用 python3、. .venv/bin/activate、PYTHONPATH=src 和 / 路径 |
项目检查并收紧 POSIX 文件模式;数据库必须在本地文件系统 |
| macOS | 与 Linux 使用相同的 shell 写法和 / 路径 |
同样执行 POSIX 模式与链接检查;数据库必须在本地文件系统 |
| Windows PowerShell | 使用 py -3、$env:PYTHONPATH = "src" 和 \ 路径 |
先由外部配置私有 DACL,并把 --allow-external-acl 放在子命令之前 |
三个平台的任务与投递状态语义相同;差异只在命令写法和本地文件保护边界。在 Windows
上,合成演示只有收到显式参数后才会加入 DACL 确认,但仍不会替你配置或验证 DACL。
运行前请先确认临时目录的 DACL 边界,并确认上述 python3 或 py -3 实际选择的是
受支持的 CPython 3.11 至 3.14。
reconcile 返回本次将收敛或已收敛的聚合计数,不返回任务 ID。输出中的
dry_run=true, applied=false 表示只读预览;默认执行返回
dry_run=false, applied=true。doctor 检查:
quick_check、外键和精确 schema;doctor 健康时退出码为 0。只要当前数据库中真实存在结构、状态、时间戳或事件链
不一致,它就返回 1;已闭合且一致的历史终态不会仅因仍被保留而失败。它也不会因为
报告能正常生成就把不健康账本当作成功。普通状态命令会返回操作所需的 UUID、状态和
时间戳,因此这些输出仍应像数据库一样受到保护。
数据库没有提示词、消息正文、任务正文、路径或自由文本异常字段。可选的 SHA-256 payload fingerprint 是可关联的假名化元数据,不是匿名数据;低熵内容可能被离线猜测时, 应省略它或在 TaskStateGuard 之外使用带密钥的指纹方案。
reconcile 时发生;项目没有后台 watcher。--dry-run 不是锁或事务预约;预览与随后执行之间,时间和并发状态可能变化。0600。不能。它保存并收敛任务生命周期元数据,不保存计算检查点,也不恢复计算。超过截止时间
或活跃宽限期的 running 任务会转为 timed_out;是否新建重试任务由外部运行时决定。
不能。相同终态的重复写入是幂等的,冲突终态会被拒绝,但 TaskStateGuard 无法观察外部
副作用。只有传输系统记录了真实回执后,调用方才能把投递状态更新为 delivered。
不是。它只保存本地 SQLite 状态账本,不负责入队、调度、执行、取消、重试或发送任务。 它可以放在 agent runtime、worker pool 或工作流系统旁边,但当前仓库不提供框架专用 适配器。
任务可能已经成功,但结果仍在等待投递;内部子任务也可能不需要直接面向用户投递。 拆分状态机可以避免把任务完成误判为结果已经送达。
只有在使用同一时刻且期间没有并发状态变化时才相同。--dry-run 不写数据库,也不锁定
未来执行;它用于审阅当前判断,不是对后续执行的预约。
不会,这些值没有受支持的数据库字段或 CLI 参数。可选的 SHA-256 fingerprint 仍可能被 关联或猜测,因此只应在理解其隐私边界时使用。
所有平台都必须使用支持 SQLite 锁的本地文件系统。Linux 和 macOS 使用所有者/模式检查
并把数据库及 sidecar 收紧为 0600。Windows 要求预先创建带私有 DACL 的服务账户目录,
再显式传入 allow_external_acl=True;项目不能替你验证 DACL。
After a service restarts, running tasks and pending deliveries can remain
visible even when no one can confirm that they are still active. TaskStateGuard
is an embedded SQLite reconciliation guardrail for AI agents, background workers,
and local workflow runtimes. It keeps task terminal state separate from delivery
terminal state and reconciles stale state using explicit deadlines and grace
periods. You can preview aggregate changes before applying them, then use a
counts-only doctor to check the ledger.
It provides task-state reconciliation and stuck-task recovery for metadata. It is not a queue, executor, or computation-resumption tool and makes no exactly-once guarantee. When the surrounding system has not observed success or a delivery acknowledgement, TaskStateGuard does not guess that the work succeeded or was delivered.
| Problem | Result |
|---|---|
A service restarts while tasks remain running |
Tasks past their deadline or active grace become timed_out; fresh tasks remain unchanged |
| Work is terminal but user-facing delivery is uncertain | The delivery state stays pending until a real acknowledgement or the delivery grace expires |
| An internal child task should not create its own delivery obligation | The terminal internal task becomes not_applicable after grace instead of pretending it was delivered |
| You need to see the impact before applying reconciliation | reconcile --dry-run returns the aggregate changes for that moment without updating tasks, events, or the database |
| Operations needs to decide whether the ledger is trustworthy | doctor checks the database, schema, state, timestamps, and event chains and emits aggregate counts |
| Linux, macOS, and Windows need one state contract | The state model is portable; storage protection uses POSIX modes or a caller-managed Windows DACL |
TaskStateGuard fits runtimes that need:
| Problem to solve | Choose |
|---|---|
| After a restart, reconcile stuck tasks, timeouts, and pending delivery without guessing success | TaskStateGuard, this project |
| Before sharing or migrating a chat export, locally audit potential secrets, personal-data patterns, format or SQLite issues, and scan gaps | ChatArchiveGuard |
| Verify that a final PPTX and its supplied QA evidence still match the HMAC-signed receipt created after structural checks | ArtifactProof |
Each project works independently. They protect state, chat archives, and PPTX artifacts respectively, without reading one another's data.
Task states:
queued ──> running
│ │
└───────────┴──> succeeded | failed | timed_out | cancelled
A task can close from queued or running. Terminal states are immutable.
Repeating the same close is idempotent; a conflicting close is rejected.
Delivery states:
pending ──> delivered | failed | not_applicable
Delivery can close only after the task is terminal. A delivery-required task
becomes delivered only after a real transport acknowledgement. An internal
task that has no direct delivery obligation can become only not_applicable.
The two state machines answer different questions:
After a release is published to PyPI, most users should install TaskStateGuard in a virtual environment.
Linux or macOS:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install task-state-guard
Windows PowerShell:
py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install task-state-guard
To review unreleased changes or contribute, first verify the source and target
commit. Then create an isolated environment from that checkout's repository root
and replace the final command with python -m pip install . (on Windows, use
.\.venv\Scripts\python.exe -m pip install .).
TaskStateGuard supports CPython 3.11 through 3.14. Runtime code uses only the Python standard library and has no network or telemetry path.
The demo script is provided in the source repository and is not included in the
wheel. Run it from the repository root. It creates a SQLite database under the
system temporary directory from fixed public sample states, invokes the real CLI
for preview, apply, an idempotent recheck, and doctor, then removes the
database. It does not read local task data or contain prompts, messages, task
bodies, or real identifiers.
Linux or macOS:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src python3 examples/restart_reconciliation_demo.py
Windows PowerShell:
$env:PYTHONDONTWRITEBYTECODE = "1"
$env:PYTHONPATH = "src"
py -3 .\examples\restart_reconciliation_demo.py --allow-external-acl
The first three aggregate resul