Persist PageTree expand state via control-plane view-state and align chevron/DOM with restored expansion; keep Sidex-style shallow page-tree scan and drop the unused recursive scanner that only added cargo noise. Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi into a module package, and retire Hermes/ACP/OpenHub recycle + root harness evidence from the index while gitignoring recycle and local diag dumps. Archive superseded design/bugs docs under old/, point architecture at ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor regressions so the working tree can stay clean.
21 KiB
7-48 [reference] Paperless-ngx Reference: Resource Ingestion / Job Ledger / Evidence Index v1
创建时间:2026-06-05
当前状态:
REFERENCE2026-06-07 状态治理:本文只作为 Paperless-ngx 对 LightRAG source registry、job ledger、索引可重建性和历史 evidence.sqlite 设计的参考材料,不再作为 active evidence.sqlite / LiteParse 主线任务源。 文内未勾选项均为历史候选,不作为当前
process任务。Owner:07-ai / 03-rust-web / control-plane / 01-tree-first-graph-kernel
参考项目:Paperless-ngx 本地副本已在 2026-07 清理;本文保留其已提炼的设计结论,不依赖本地源码。
参考版本:
f56f29111CodeGraph:历史取证时曾单独建立索引;本地参考副本已清理。
上位依据:
/mnt/Data1T/mnote/ARCHITECTURE.md/mnt/Data1T/mnote/CURRENT_ARCHITECTURE.md/mnt/Data1T/mnote/design/07-ai/reference/7-46-document-evidence-retrieval-kernel-v1.md/mnt/Data1T/mnote/design/03-rust-web/done/3-25-local-folder-mineru-ocr-sidecar-v1.md/mnt/Data1T/mnote/design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md
1. 第一结论
Paperless-ngx 对 MNote 最有价值的不是 Django / Angular / Celery 技术栈,而是三套工程结构:
PaperlessTask:所有后台任务有统一账本,能记录来源、状态、耗时、输入、结果和用户是否已确认。consume_fileplugin pipeline:资源导入、预检、解析、OCR、存储、索引、通知按阶段推进,失败可以定位到阶段。Tantivy search backend + sanity checker:索引有 schema 版本、权限字段、锁、延迟补偿和可重建性;文件与派生物有一致性检查。
MNote 应把这些模式收口成自己的 Resource Work Kernel:
- local file / attachment / OCR source / parsed artifact
-> ResourceWorkJob control-plane ledger
-> ResourceIngestionPipeline stage runner
-> sidecar artifact + source-map
-> evidence.sqlite / local search projection
-> realtime job events
-> sanity check / recovery job
这不是要引入 paperless-ngx 的运行时。MNote 的数据真相仍然是 local Markdown、附件原文件、resource tree 和 control-plane;索引、OCR Markdown、parse Markdown、source-map 都是可删除重建的派生物。
2. Paperless-ngx 可借鉴点
2.1 统一后台任务账本
参考路径:
src/documents/models.py:PaperlessTasksrc/documents/signals/handlers.py:Celery task publish / prerun / postrun / failure handlerssrc-ui/src/app/components/admin/tasks/tasks.component.ts
可借鉴点:
- 任务不是只存在于内存事件或前端状态,而是落库。
- 每个任务有
task_type、trigger_source、status、date_created、date_started、date_done、duration_seconds、wait_time_seconds、input_data、result_data、acknowledged。 - 系统任务和用户触发任务统一展示,但保留来源差异。
MNote 映射:
- 当前
JobTicket、OCR job、index refresh、agent run、OnlyOffice bridge 任务、recovery job 不应继续分散。 - 新增 control-plane 表
resource_work_jobs,先覆盖 local OCR / evidence parse / index refresh,后续再纳入 agent run 和 recovery job。
2.2 阶段化资源导入管线
参考路径:
src/documents/tasks.py:consume_filesrc/documents/plugins/base.py:ConsumeTaskPluginsrc/documents/consumer.py:解析、OCR、存储、索引、progresssrc/documents/plugins/helpers.py:ProgressManager
可借鉴点:
- 导入任务按插件链执行,每个 stage 有
setup / run / cleanup。 - 阶段状态通过 websocket 通知。
- 文件写入、数据库更新、索引更新分层处理,失败时能说明卡在预检、解析、写 sidecar 还是索引。
MNote 映射:
- 不引入插件框架泛化;先定义窄的
ResourceIngestionPipeline。 - stage 只覆盖当前真实需要:
preflight、parse_text、ocr、write_artifact、write_source_map、refresh_evidence_index、broadcast_done。 - 当前
local_ocr里的 stage 字符串和 sidecar 写入逻辑可以作为第一批迁移对象。
2.3 索引生命周期和自愈
参考路径:
src/documents/search/_backend.pysrc/documents/search/_schema.pysrc/documents/search/_query.pysrc/documents/tasks.py:index_document、remove_document_from_index
可借鉴点:
- schema version sentinel 决定是否重建。
- 写索引用 file lock 和 retry,锁耗尽后排延迟任务,而不是让前台操作失败。
- 查询层有权限过滤、autocomplete、highlight、CJK bigram、simple search。
MNote 映射:
.mnote/index/evidence.sqlite当前已存在,但需要更明确的 schema sentinel 和 rebuild reason。query_evidence_sqlite_results继续作为默认 evidence path;后续补充 autocomplete / CJK / highlight 时仍以EvidenceLocator为返回真相。- 索引写失败不能悄悄丢失,应写入
resource_work_jobs的 retry-scheduled 状态。
2.4 权限过滤的实时事件
参考路径:
src/paperless/consumers.pysrc/documents/plugins/helpers.py
可借鉴点:
- websocket payload 带 owner / visible users / visible groups。
- server-side websocket consumer 根据当前用户过滤。
MNote 映射:
- local-only 阶段可以先只带
workspaceId、actorId、rootUri、targetDocumentId、grantId。 - 一旦进入 share / team workspace,OCR / index / agent job event 不能只按广播频道粗暴推送。
AiAccessScope和 share grants 应能映射成 job event 可见性字段。
2.5 Sanity checker
参考路径:
src/documents/sanity_checker.pysrc/documents/management/commands/document_sanity_checker.py
可借鉴点:
- 独立检查原文件、派生文件、checksum、孤儿文件和空 OCR 内容。
- 输出按 error / warning / info 分级,既能 CLI 显示,也能作为后台任务结果。
MNote 映射:
- 新增
workspace_sanity_check,先检查 local-folder resource evidence:- Markdown owner 是否存在。
- 附件路径是否存在且在 allowed root 内。
{pageStem}.ocr/sidecar 是否能回到 owner Markdown。*.source-map.json是否能解析为mnote.resource_source_map.v1。evidence.sqlite是否能由 sidecar 重建。- evidence locator 的
openAction是否能落回 document / resource tab。
3. 目标架构
3.1 ResourceWorkJob
新增 control-plane job ledger,不替代 domain event,也不替代 agent run receipt。
pub struct ResourceWorkJob {
pub job_id: String,
pub job_type: ResourceWorkJobType,
pub trigger_source: ResourceWorkTriggerSource,
pub status: ResourceWorkJobStatus,
pub stage: Option<String>,
pub progress_current: Option<u32>,
pub progress_total: Option<u32>,
pub stage_label: Option<String>,
pub workspace_id: String,
pub root_uri: String,
pub actor_id: Option<String>,
pub target_document_id: Option<String>,
pub source_root_relative_path: Option<String>,
pub artifact_root_relative_path: Option<String>,
pub source_map_root_relative_path: Option<String>,
pub input_json: serde_json::Value,
pub result_json: Option<serde_json::Value>,
pub error_code: Option<String>,
pub error_message: Option<String>,
pub created_at_ms: u128,
pub started_at_ms: Option<u128>,
pub finished_at_ms: Option<u128>,
pub acknowledged_at_ms: Option<u128>,
}
首批枚举:
job_type:
- local_ocr
- resource_parse
- evidence_index_refresh
- evidence_index_rebuild
- workspace_sanity_check
trigger_source:
- web_ui
- api
- watcher
- agent_tool
- system
- recovery
status:
- pending
- running
- succeeded
- failed
- retry_scheduled
- canceled
约束:
job_id由 MNote 生成,不复用外部 provider task id。- MinerU task id、LiteParse request id、Reasonix run id 只进入
input_json / result_json。 result_json必须能存 evidence id、artifact path、source-map path、locator count、index row count。stage / progress_current / progress_total / stage_label是前台任务中心的稳定合同;不能只塞在 provider 私有 payload 里。- job ledger 是 control-plane 事实,不是用户正文真相。
3.2 ResourceIngestionPipeline
先实现窄接口,不做任意插件市场:
preflight
-> detect_resource_kind
-> choose_provider
-> parse_or_ocr
-> write_artifact
-> write_source_map
-> refresh_evidence_index
-> broadcast_job_event
Provider 策略:
- 文本型 PDF / Office parse 优先走
LiteParseProvider或当前轻量 parser。 - 扫描件 / 图片走
MinerUProvider。 - mock provider 仅用于 smoke,不得在真实功能报告中冒充成功链路。
阶段状态:
queued
preflight
parsing
ocr_uploading
ocr_processing
writing_artifact
writing_source_map
indexing
done
failed
retry_scheduled
stale
当前 local_ocr.job.updated 可兼容保留,但 payload 应逐步包含 jobId / jobType / stage / workspaceId / rootUri / sourceRootRelativePath / targetDocumentId / artifactPath / sourceMapPath。
3.3 Evidence Index Lifecycle
当前 .mnote/index/evidence.sqlite 继续作为默认 evidence index。升级点:
- 增加
schema_version和build_settingssentinel。 - 每次 write / refresh 记录
job_id。 - 索引 row 必须能通过
source_map_root_relative_path回到 canonical artifact。 - lock 失败进入
retry_scheduledjob,不直接吞掉。 - watcher 增量刷新失败时,不做前端轮询补偿;排 recovery job 并广播一次明确事件。
建议最小表:
CREATE TABLE IF NOT EXISTS evidence_index_meta (
key TEXT PRIMARY KEY,
value_json TEXT NOT NULL,
updated_at_ms INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS evidence_index_jobs (
job_id TEXT PRIMARY KEY,
last_error_code TEXT,
last_error_message TEXT,
updated_at_ms INTEGER NOT NULL
);
如果 control-plane 已存 job,这里只保存 index-local rebuild metadata,不重复完整 job 账本。
3.4 Workspace Sanity Check
新增只读检查,不自动删除、不自动修复。
检查项:
| 级别 | 检查 | 说明 |
|---|---|---|
| error | owner Markdown 不存在 | evidence locator 无法打开 |
| error | resource file 不存在 | 附件或 OCR source 丢失 |
| error | source-map JSON 无法解析 | 定位真相损坏 |
| error | evidence.sqlite schema 不匹配 | 需要 rebuild |
| warning | sidecar 孤儿文件 | 有 OCR/parse artifact 但找不到 owner link |
| warning | source hash / mtime stale | 可重建,但不阻断 |
| info | parse/OCR 内容为空 | 允许,但要可见 |
输出既可作为 API:
POST /api/workspaces/sanity/check
GET /api/work/jobs/:jobId
也可作为 smoke / CLI helper 的 JSON 结果。
3.5 Unified Task Foreground UI
统一后台任务必须有统一前台可见入口。否则用户仍然只能在 OCR 设置、索引设置、toast、局部状态灯之间猜系统是否还在运行。
当前已有的 mnote-local-ocr-task-dock 是迁移起点,不是长期终点。它应升级为全局 Work Task Center:
Topbar task button
-> badge: active / failed / needs attention count
-> drawer: current jobs + recent completed + failed
-> row: icon + title + stage label + progress bar + actions
-> detail: input/result/error + related document/resource + trace/run receipt
入口位置:
- 顶栏保留一个任务中心图标,建议使用
progress_activity或pending_actions。 - OCR 和索引设置按钮仍可保留,但它们不再各自承载任务列表。
- 任务中心抽屉优先靠右打开,保持轻量;不做全屏管理台作为第一阶段。
任务行最小字段:
| 字段 | 来源 | UI 用途 |
|---|---|---|
jobId |
job ledger | 稳定 row key 和详情查询 |
jobType |
job ledger | 图标、分类、筛选 |
status |
job ledger | 颜色、分组、是否需要确认 |
stageLabel |
job event | 当前阶段文案 |
progressCurrent / progressTotal |
job event | 确定性进度条 |
createdAtMs / startedAtMs / finishedAtMs |
job ledger | 排队/耗时/最近完成 |
targetDocumentId |
job ledger | 打开 owner 文档 |
sourceRootRelativePath |
job ledger | 打开附件或定位资源 |
artifactRootRelativePath / sourceMapRootRelativePath |
result | 打开 OCR/parse/source-map |
errorCode / errorMessage |
job ledger | 失败摘要和复现证据 |
进度规则:
- 有
progressCurrent / progressTotal时显示确定性进度条。 - 没有总量但 status 为 running 时显示细条 indeterminate,不伪造百分比。
queued / retry_scheduled显示排队态,不显示假进度。failed和retry_scheduled必须进入“需要处理”计数。succeeded默认保留在最近完成列表,可由用户清除/ack。
任务动作:
| 状态 | 动作 |
|---|---|
| running | 打开目标、查看详情 |
| succeeded | 打开结果、打开目标、清除 |
| failed | 查看错误、重试、打开目标、复制错误摘要 |
| retry_scheduled | 查看重试原因、立即重试、取消重试 |
| stale | 重新生成、打开旧结果 |
事件与数据流:
GET /api/work/jobs?scope=currentWorkspace&active=true
GET /api/work/jobs?scope=currentWorkspace&recent=true
GET /api/work/jobs/:jobId
POST /api/work/jobs/:jobId/ack
POST /api/work/jobs/:jobId/retry
SSE/WS event: resource_work.job.updated
前端运行时:
- 新增
browser/work-task-center-runtime.js,由layout.rs统一注入。 - 现有
document-resource-tab-runtime.js中 OCR task dock 的 state/render 逻辑迁入该 runtime。 mnote:local-ocr-job-updated作为兼容事件继续转发为resource_work.job.updated,直到后端统一 payload 完成。- 前端只在启动时拉一次 active/recent snapshot;后续靠 WS/SSE 事件更新,不新增周期轮询。
视觉约束:
- 顶栏只显示一个任务中心 badge,避免 OCR/索引/AI 各自占顶栏状态位。
- 抽屉行要密集、可扫描,不能做大卡片堆叠。
- 每行必须有可见进度或阶段文本,长路径截断但 title 保留完整路径 tooltip。
- 移动端抽屉宽度占满可用宽度,任务行按钮折到第二行,避免文字溢出。
4. 不做什么
- 不把 paperless-ngx 的 Django model / Angular UI / Celery worker 引入 MNote。
- 不把 OCR Markdown、parse Markdown 或 evidence.sqlite 变成正文真相。
- 不新增前端轮询来弥补 job 状态;状态更新走现有 WS / SSE / watcher event。
- 不把任务 UI 继续拆成 OCR 一套、索引一套、AI 一套;这些只能是任务中心里的分类或过滤。
- 不让 workflow UI 先行。先做 event-triggered job 和少量内置动作,再考虑可视化配置。
- 不在本轮实现向量 RAG。Paperless 的 FAISS append-only 方案只作为反例和参考,不作为 MNote 默认路径。
5. 实施分期
Phase A:设计冻结和接口补齐
Owner:07-ai / 03-rust-web
任务:
- 在
core-protocol增加ResourceWorkJob/ResourceWorkJobStatus/ResourceWorkTriggerSource合同。 - 在 job 合同中加入
stage / stageLabel / progressCurrent / progressTotal,作为前台任务中心稳定字段。 - 明确
local_ocr.job.updated与新resource_work.job.updated的兼容关系。 - 本设计已作为
7-46历史 job / index lifecycle 参考,不再作为 active 执行补充。
验收:
- Rust unit 覆盖 job status 序列化。
- Rust unit 覆盖 progress 字段缺省、确定性进度和 indeterminate 语义。
- 不改变现有 OCR smoke 行为。
Phase B:OCR job ledger 收口
Owner:03-rust-web / control-plane
任务:
- 给
local_ocr::create_job创建 control-plane job 记录。 - 每次 stage advance 同步 job ledger。
done / failed写入finished_at_ms / result_json / error_code。- 增加
GET /api/work/jobs/:jobId。 - 增加
GET /api/work/jobs?active=true&recent=true,供任务中心首屏 snapshot 使用。
验收:
- 现有 OCR sidecar smoke 仍通过。
- 新增 smoke 验证 OCR job 可查询、失败错误脱敏、done 后 result 包含 sidecar 和 source-map。
- 新增 smoke 验证 active snapshot 包含运行中 OCR job,完成后转入 recent。
Phase B2:统一任务中心 UI
Owner:03-rust-web / 05-editor-mainline
任务:
- 新增
browser/work-task-center-runtime.js。 - 将
mnote-local-ocr-task-dock的状态聚合和 drawer 渲染迁移到 work task center。 - 顶栏新增统一任务中心按钮和 badge,OCR 按钮回归 OCR 设置入口或合并进设置面板。
- 支持 active / needs attention / recent 三个分组。
- 任务行支持确定性 progress bar、indeterminate running bar、失败重试、打开目标、打开结果、ack。
- 兼容接收
mnote:local-ocr-job-updated,并在后端统一事件上线后接resource_work.job.updated。
验收:
- 浏览器 smoke:触发 OCR 后顶栏 badge 从 0 变 1,抽屉显示阶段和进度。
- 浏览器 smoke:OCR 完成后任务移入 recent,能打开 OCR sidecar。
- 浏览器 smoke:mock 失败任务进入 needs attention,能查看错误和重试。
- 截图验证桌面和移动端抽屉不溢出、不遮挡主编辑区关键内容。
Phase C:Evidence index lifecycle
Owner:07-ai / 03-rust-web
任务:
- 给 evidence.sqlite 增加 schema/settings sentinel。
- index refresh 写入 job id 或 index-local job metadata。
- lock / write / parse source-map 失败时进入 retry-scheduled 或 failed job。
mnote.index.status返回 schema、last build、last job、pending retry。- index refresh / rebuild 通过任务中心显示阶段和结果摘要。
验收:
task528-document-evidence-liteparse-agent-smoke.js继续通过。- 新增 index stale / rebuild smoke,验证删除 evidence.sqlite 后可通过 job 重建。
- 浏览器 smoke 验证 index rebuild 任务出现在任务中心。
Phase D:Workspace sanity check
Owner:03-rust-web / 04-tree-domain
任务:
- 实现只读
workspace_sanity_checkjob。 - 检查 owner Markdown、resource file、sidecar、source-map、evidence.sqlite。
- 结果按 error / warning / info 分级。
- 从任务中心和 index/OCR 设置 surface 都能启动检查;结果统一进入任务中心详情。
验收:
- smoke 构造缺失 source-map、孤儿 sidecar、损坏 sqlite,能得到稳定 JSON。
- 不删除、不移动任何用户文件。
- 浏览器 smoke 验证 sanity check 运行时有任务行,完成后详情展示 error / warning / info 摘要。
Phase E:内置 workflow actions
Owner:01-tree-first-graph-kernel / 07-ai
任务:
- 定义内置事件:
resource.created、resource.changed、ocr.done、evidence.index.failed。 - 定义内置动作:
parse_resource、run_ocr、refresh_evidence_index、schedule_sanity_check。 - 先用静态配置或 settings 控制,不做复杂 UI。
验收:
- 新增附件后能按 settings 自动排 parse/OCR/index job。
- 失败不会循环重试;必须有 retry budget 和可见错误。
6. 设计验收基线
本设计完成后,MNote 应具备以下能力:
- 用户能看到后台 OCR / parse / index / sanity job 的真实状态,而不是只看到散落的 toast 或局部状态灯。
- 顶栏任务中心能显示 active / failed / recent 任务,且所有任务进度来自 job ledger 或 job event,不伪造百分比。
- agent 调用 evidence 工具时,MNote 能把本次证据索引状态、evidence ids、source-map path 写入 run receipt。
- 删除或损坏 evidence.sqlite 不会让系统静默退化;会显示 rebuild required 或排 rebuild job。
- OCR sidecar、parse artifact、source-map 和 evidence locator 可以被 sanity check 证明互相可追溯。
- 多人 / share 场景下,job event 有明确可见性字段,不把本地单用户广播模型固化成长期事实。
7. 参考代码索引
Paperless-ngx:
src/documents/models.py:PaperlessTask、workflow model、document version fields。src/documents/tasks.py:consume_file、index deferred tasks、bulk update。src/documents/consumer.py:resource consume main path。src/documents/plugins/base.py:plugin lifecycle contract。src/documents/plugins/helpers.py:progress websocket payload。src/documents/search/_backend.py:Tantivy backend、lock retry、autocomplete、highlight。src/documents/search/_schema.py:schema version sentinel。src/documents/search/_query.py:permission filter、date rewrite、CJK/simple query。src/documents/sanity_checker.py:archive consistency checker。src/paperless/consumers.py:permission-aware websocket consumer。
MNote 当前落点:
rust/crates/core-protocol/src/governance.rsrust/crates/core-protocol/src/evidence.rsrust/crates/core-protocol/src/tool.rsrust/crates/mnote-web/src/routes/local_ocr.rsrust/crates/mnote-web/src/routes/local_search_index.rsrust/crates/mnote-web/src/routes/evidence.rsrust/crates/mnote-web/src/routes/local_folder_events.rsrust/crates/mnote-web/src/routes/ws.rsscripts/task528-document-evidence-liteparse-agent-smoke.js