HiddenSteps — 一个本地优先的个人工作流智能平台
这是对 docs/design/02-system-architecture.md 目标模块图的如实、当前状态补充。它说明的是实际已构建的内容、哪些已针对真实后端而非 mock 验证过,以及哪些仍然真正缺失——而不是计划中的内容(那是 docs/roadmap/01-implementation-roadmap.md)。
在仓库根目录运行 cargo build --workspace && cargo test --workspace && cargo clippy --workspace --all-targets -- -D warnings。截至撰写本文时:12 个 crate,193 个测试通过,零 clippy 警告,cargo fmt --check 干净 —— 其中 183 个分布在 11 个不需要显示或外部服务的 crate 中,另有 10 个在 hiddensteps-observation 中,需要活跃的 X11 显示(在有显示的环境已验证;见该行)。有四个测试按设计被 #[ignore](见下文),不计为失败,也不计入这 193 个。
| Crate | 实现内容 | 验证方式 |
|---|---|---|
hiddensteps-domain | 核心类型:PrivacyLevel/PrivacyState、EventSummary/SignalType、Pattern、Recommendation、AuditEntry 以及 CapturedSignal —— 一种在结构上无法被持久化的类型(没有 Serialize),在类型层面强制实施 ADR-0006 的原始数据规则 | 单元测试:层级往返/排序、Deep 模式 TTL 门控 |
hiddensteps-security | SecretStore(ADR-0008):真实的 OS 凭证库(KeyringSecretStore)+ 内存(测试)实现;CSPRNG 主密钥生成(通过 zeroize::Zeroizing 包装器返回,使密钥在 drop 时被擦除,而不是残留在已释放的内存中);针对 Portable 模式的 Argon2id 口令派生(PassphraseKey 在 drop 时对其派生密钥进行零化,保留非机密的盐)。hiddensteps-event-store 同样将含密钥的 PRAGMA key/rekey SQL 文本保存在 Zeroizing 中 | 针对内存存储和 KDF 的单元测试;真实凭证库往返测试被 #[ignore](见下文) |
hiddensteps-event-store | SqlCipherEventStore(ADR-0003):来自 docs/design/07-database-schema.md 的完整模式,针对隐私状态、事件、审计日志、模式、模式↔事件链接、模式嵌入(见下方说明)、推荐、LLM 提供方配置和通用设置的 CRUD,外加 delete_all_data(事务性的;还会 rekey,以实现一种重启后依然生效的"删除所有数据")/export_data/count_rows(诊断)/delete_expired_events(Deep 模式 TTL 清理,由 apps/desktop/src-tauri 的周期性推荐循环调用——ttl_expires_at 自 v0.1.0 起就被持久化,但此前从未有任何代码删除过超过该时间的行);外键强制(PRAGMA foreign_keys = ON),使 schema.sql 中模式↔事件链接上的 ON DELETE CASCADE 真正生效 | 针对真实 SQLCipher 文件的 33 个测试:错误密钥无法打开,相同密钥可正确重新打开,delete-all 清空包括最新表在内的所有表,rekey 往返正常,TTL 清理不会动未过期的事件,级联删除不留下孤立的模式↔事件链接 |
hiddensteps-redaction | 脱敏引擎(docs/design/05-privacy-model.md 第 4 节):针对 API 密钥/令牌/PEM 密钥/电子邮件/SSN/信用卡的 regex+Luhn 检测器、基于熵的模糊机密检测器,以及不确定即丢弃策略 | 30 个测试,包括刻意构造的对抗性输入(嵌入散文中的机密、近似命中但并非机密的字符串(如 git SHA)、无连字符/带空格的 SSN、填充数字的卡号、全大写或全小写的高熵令牌) |
hiddensteps-pipeline | 事件流水线(ADR-0006):分类 → 脱敏 → 摘要,按信号类型进行隐私级别门控,Deep 模式 TTL 分配 | 8 个测试,涵盖脱敏触发的丢弃、级别门控丢弃和成功摘要 |
另外,在 crates/ 之外(不属于根工作区——原因见下文):
hiddensteps-event-store 中,余弦相似度在 Rust 中计算,代替 ADR-0007 的 sqlite-vec 虚拟表(见 event-store/src/schema.sql 顶部的注释)——在此环境中无法验证加载原生 SQLite 扩展,而且 ADR-0007 本身也指出,在现实的单用户数据量下,sqlite-vec 自身的行为就是暴力精确搜索。语义相同,无原生扩展风险。hiddensteps-observation 的 macOS 和 Windows 模块(src/macos/、src/windows/)是针对长期稳定的平台 API(CGWindowListCopyWindowInfo;GetForegroundWindow/GetWindowTextW/QueryFullProcessImageNameW)编写的真实、完整源码,在无 macOS/Windows 工具链可用的情况下完成——现在两者都编译干净,由 .github/workflows/ci.yml 的 core 任务矩阵验证。macOS 模块首先需要一个真实修复( 的无类型默认泛型参数不能满足 对 键的 trait 约束——通过显式将其类型化为 修复);Windows 首次尝试即编译干净。crates/ 之外crates/* 是根 Cargo.toml 工作区,在此 Linux 开发环境中完全可构建/可测试,除 cargo 获取的内容外零系统依赖。apps/desktop/src-tauri 甚至编译都需要 webkit2gtk-4.1(Linux),而此环境无法安装(没有无密码 sudo,没有可用的 nix/包管理器途径——已通过直接尝试确认)。将其排除在工作区之外意味着 cargo build --workspace 在此保持 100% 绿色,而不是因为一个在此沙箱中无人能修复的 crate 而永久红色。出于同样的原因,它还需要在自己的 Cargo.toml 中有一个空的 [workspace] 表——否则 Cargo 仍会尝试将其挂到这个祖先工作区上,并以 "current package believes it's in a workspace when it's not." 报错。apps/desktop/ui 没有此类约束,并采用与 Rust 核心相同的方式验证。两个部分仍然端到端验证——只是由 CI 而非此沙箱完成。
#[ignore] 的测试的说明hiddensteps-security::keyring_store::tests::set_get_delete_round_trip_against_the_real_vault —— 需要真实的 OS 凭证库/桌面会话。hiddensteps-observation::linux::shortcuts::tests::grabs_and_ungrabs_a_real_shortcut —— 执行真实的会话级 XGrabKey,在共享环境中自动运行会产生干扰。hiddensteps-llm-provider 的 tests/ollama_live.rs(2 个测试)——需要真实运行的 Ollama 实例。这两个测试在开发期间确实针对真实的本地 qwen3:0.6b(0.6B 参数混合推理模型)实例运行过,合计约 2 秒即通过;如需不同设置,可通过 HIDDENSTEPS_TEST_OLLAMA_MODEL/HIDDENSTEPS_TEST_OLLAMA_URL 环境变量覆盖模型/URL。这四个都是真实测试,而非遗留的装饰性测试——docs/roadmap/03-testing-strategy.md 第 2 节所划定的正是这条界线:在 CI 中应放在 mock 后面的逻辑,与属于刻意的、人工验证的 OS/会话/外部服务集成。在合适的机器上,可用 cargo test -p <crate> -- --ignored 运行其中任何一个(添加 <test name> 可只运行一个)。
hiddensteps-observation | ObservationSource(ADR-0005) + Linux:ActiveWindowSource(X11 GetInputFocus)、FileOperationSource(通过 notify 使用 inotify)、ClipboardMetadataSource(X11 选择,仅元数据)、GlobalShortcutSource(X11 XGrabKey)。外加 macOS/Windows 源文件(见下文) | 本环境中 11 个测试中有 10 个针对真实后端运行——真实的 X11 显示(WSLg 的 DISPLAY=:0)和真实的 inotify,而非 mock。1 个测试(GlobalShortcutSource 的真实按键捕获)按设计被 #[ignore] |
hiddensteps-llm-provider | LlmProvider(ADR-0004):Ollama 客户端(带 think: Option<bool> 请求字段,用于混合推理模型)、与 OpenAI wire 协议兼容的客户端(覆盖 OpenAI/Azure/OpenRouter/Together/Groq/DeepSeek/LocalAI)、Anthropic Messages 客户端,以及本地运行时自动检测。每个客户端都设置请求超时(build_http_client),因此挂起的远程服务无法永远阻塞调用;Ollama 将 max_tokens 转发为其嵌套的 options.num_predict | 19 个针对 wiremock mock 服务器的测试(包括真实的超时触发检查和 Ollama 确实发送 num_predict 的验证),外加 2 个真实 Ollama 集成测试(tests/ollama_live.rs,被 #[ignore]——见下文),这些测试发现并修复了一个真实问题:在 think 保持默认值的情况下,同一提示词在真实的本地混合思考模型上耗时超过两分钟,而设置 think: Some(false) 后只需几秒 |
hiddensteps-patterns | 模式检测(滑动窗口 n-gram 序列匹配)+ 工作流图(带边权重的转移图)——ADR-0010 的第 1 层 | 16 个测试,包括 PROMPT.md 自身"观察到 31 次"示例的直接类比,以及一个回归测试,验证连续重复上的重叠窗口不会被重复计数 |
hiddensteps-recommendations | 推荐引擎的第 2 层(ADR-0010):LLM 综合,带结构化 JSON 提示词契约、叙述矛盾校验器和重试循环——关键是,数字字段(estimated_time_saved_minutes)从不从 LLM 的输出中解析,仅根据第 1 层计算 | 23 个测试,包括畸形 JSON 重试、叙述矛盾重试(覆盖拼写出来的数字和所有 LLM 控制的字段,而不仅仅是 why)、字符串感知的 JSON 提取,均针对脚本化的测试提供方 |
hiddensteps-privacy-engine | 云端分发门控(docs/design/03-data-flow-diagrams.md 第 5 节)和同意版本管理(docs/design/05-privacy-model.md 第 5 节);PrivacyGatedProvider 包装任何 LlmProvider,使门控无法通过正常调用路径绕过 | 13 个测试,包括即使在所有同意均已授予时 Level-4 内容仍被阻止 |
hiddensteps-plugin-host | WASM 插件宿主(ADR-0009):封闭的能力枚举、清单验证、基于 wasmtime 的沙箱,只链接已授予能力的主机函数,外加燃料计量和一个内存 ResourceLimiter,无论插件实例持有哪些能力,都限制其 CPU/内存——这正是 docs/research/06-threat-model.md 的拒绝服务章节指出的两个仅靠能力强制无法解决的维度(一个不含任何能力的模块仍可能永远循环或无限增长内存)。instantiate_from_manifest 是安全入口:它强制进行清单验证(截屏需 Level-4 的规则),并在任何能力到达链接器之前拒绝授予清单未声明的内容——而普通的 instantiate 能力切片与清单完全没有任何关联 | 20 个测试,包括真实的能力逃逸尝试:测试时手工编写并编译的 WAT 模块,证明未授予能力的导入确实无法解析(实例化失败),而不仅仅是用不到;外加真实的无限循环模块和无界 memory.grow 模块,它们会触发 trap 而不是挂起/耗尽内存 |
hiddensteps-enterprise-policy | 策略模式(docs/design/05-privacy-model.md 第 6 节),恰好只有两个旋钮(隐私级别下限、提供方白名单)——不存在任何其他策略可能想约束的字段。如果存在,则从应用数据目录中的 enterprise-policy.json 文件加载(这是一个真实的、虽是临时的机制——docs/design/08-plugin-architecture.md 所描述的完整 PolicyLoader 插件连接器尚未构建),通过 hiddensteps-event-store 的 enterprise_policy 表持久化,并实际在 apps/desktop/src-tauri 的 set_privacy_level/set_ai_provider 命令中强制执行——这是层级/提供方选择被写入的两个变更点 | 6 个测试,包括解析一个最大对抗性的策略文件,其中包含五个按设计被排除的多余键,并确认它们没有一个在解析后存活 |
| 位置 | 实现内容 | 验证方式 |
|---|
../apps/desktop/ui | React/TypeScript UI:OnboardingWizard(全部 8 个屏幕,docs/ux/02)、PrivacyDashboard(docs/ux/03)、RecommendationCard(docs/ux/04)、SettingsPage、DiagnosticsPage,在 App.tsx 中组装——仅通过类型化的 tauriBridge.ts 与核心通信 | 通过 vitest + @testing-library/react 针对真实 jsdom 渲染进行 50 个测试,包括引导向导的步骤门控(未成功完成检查就无法越过验证步骤,未勾选同意就无法开始观察)、每个变更调用点上的错误呈现、重新同意横幅、推荐证据链,以及 axe-core 可访问性门禁;tsc -b 类型检查干净 |
../apps/desktop/src-tauri | Tauri 外壳:约 21 个 IPC 命令(docs/design/09-api-specification.md)将上面所有 crate 连接在一起,外加一个真实的 capture→pipeline→store→UI 事件后台循环(Linux) | 在 CI 中于 Linux、macOS 和 Windows 上干净编译——不是在这个开发沙箱中(原因见 ../apps/desktop/README.md),但以前这里的"未验证"警告已不存在:CI 的第一次真实运行发现并修复了 3 个真实 bug(一个缺失的 Serialize derive、一个缺失的 Cargo feature 标志、一个缺失的生成图标文件),这些 bug 无论多少本地审查都无法发现 |
CFDictionaryfind&CFStringCFDictionary<CFString, CFType>hiddensteps-observation/src/lib.rs 文档注释中明确指出的缺口——前者需要一个本仓库不包含的独立浏览器扩展产物;后者(GlobalShortcutSource)已实现但从未自动启动,因为在共享开发沙箱中会话级抢占一个按键组合会造成相当大的干扰。LlmProvider 综合定性判断;它已针对脚本化的替代提供方进行了测试(对重试/验证逻辑有真实断言)。hiddensteps-llm-provider 本身现在有真实 Ollama 覆盖(见下文);将综合器自身的提示词契约端到端地对着真实模型运行(而不是底层 HTTP 客户端)是下一个自然的步骤,尚未完成。get_diagnostics(Tauri 外壳)报告真实的事件/模式/推荐/审计日志计数和真实的磁盘文件大小,但不报告 GPU/CPU/内存使用情况、观察的 OS 权限状态或更新状态——也就是 PROMPT.md 所要求的完整自诊断清单。每个渲染此内容的 UI 组件都明确说明这一点,而不是显示一个虚构的"OK"。