Files
memind/docs/architecture/memind-2-runtime-execution-assessment-20260702.md

1078 lines
44 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Memind 2.0 Streaming Agent Runtime 执行评估
> 历史快照说明:本文是 2026-07-02 的执行评估,其中 MindSpace 路径按当时生产数据边界记录。103 当前生产 MindSpace Service 根目录以 [103 runtime topology](../103-runtime-topology.md) 为准,即 `/Users/john/MindSpace`;不要把本文里的旧 monolith 路径当作当前服务根目录。
日期: 2026-07-02
评估对象:
- 生产运行目录: `/Users/john/Project/Memind`
- 干净同步分支: `/Users/john/Project/memind-clean-main-20260702`
- 分支: `memind-streaming-runtime-20260702`
- 最新提交: 本文档所在分支 HEAD,提交信息 `fix: harden auth middleware against transient db errors`
- 最新 release: `20260702-104734-212e163` 后续改造以生产备份和分支提交方式推进。
- 当前公网入口: `https://mm.tkmind.cn`
## 总体结论
当前架构已从原先的 single portal + multi-goosed 形态,推进到 streaming-first runtime 的第一阶段:
- Edge Gateway 已切到 `mm.tkmind.cn -> local nginx -> Portal :8081`
- Stream Controller 已具备 SSE headers、abort propagation、backpressure pipeline。
- Redis Router 已启用,承担 worker runtime state。
- Goose Worker Pool 已从固定单点走向四 worker 可观测调度。
- Aider/OpenHands 已从普通聊天默认能力中剥离,进入 code mode 和后端灰度门禁;P6.3 已把 code run 从 goosed session extension 外移到 `agent-run-v1` Tool Gateway 协议,P6.4 已完成 Aider 真实执行 canaryP6.5 已完成 OpenHands 真实执行 canaryP6.6 已完成 external worker 精确接管 code-run canaryP6.7 已加入 Tool Gateway 产物校验与输出审计,P6.8 已安装 external worker LaunchAgentP6.9 已完成带 validation 的 external worker 灰度 canaryP6.10 已加入 external worker 只读观测脚本,P6.11 已加入放量策略门禁,P6.12 已开启全用户长期灰度并通过普通测试用户真实路径,P6.13 已安装自动暂停 guardP6.15 已让 H5 code-run 自动补 receipt validation 并恢复后端 required validationP6.17/P6.18 已把 external worker 并发 2 通过 canary 并固化为当前 all-user gray 策略,P6.19 已加入 task-level artifact validationP8.1 已加入 stale running recoveryP8.2 已完成 Portal DB/Auth 瞬时错误兜底,P8.3 已加入 worker lease heartbeatP8.4 已把 heartbeat 纳入 runtime SLO 报告并安装小时级 soak 快照。
- PG 和 MindSpace 仍保持生产数据边界,SLO 报告只做统计读取;P6.3-P6.15 不新增 schema migration,不删除或修改既有用户数据。
整体执行评分: 9.95 / 10。
可以支撑当前 H5 streaming 稳定性改造的基础目标。自动采样、worker sidecar heartbeat、SLO 只读快照、SLO 日报定时器、SLO 日报保留策略、first-token EWMA、first-token p50/p95 窗口趋势、Tool Gateway Queue v0、外部 worker 接管入口、真实 worker canary、后端 code-mode canary、code-run 用户级灰度 gate、H5 页面编辑 UI canary、P6.3 Tool Gateway 协议化、P6.4 Aider 真实 canary、P6.5 OpenHands 真实 canary、P6.6 external worker code-run canary、P6.7 Tool Gateway guardrails、P6.8 worker LaunchAgent、P6.9 validated external worker canary、P6.10 worker observability、P6.11 rollout policy gates、P6.12 all-user gray、P6.13 auto-pause guard、P6.15 H5 validation metadata、P6.17/P6.18 worker concurrency 2、P6.19 task-level artifact validation、P8.1 stale running recovery、P8.2 Portal DB/Auth transient hardening、P8.3 worker lease heartbeat 和 P8.4 heartbeat SLO/hourly soak 已经落地。P6.22 用户可见进度/失败说明按用户决策暂不开发,后续剩余差距转为 24h soak 结果复盘和更细任务类型的校验策略扩展。
## 实测结果
### P6.3 Tool Gateway Protocol
结果: 通过,默认关闭发布,dry-run canary 通过并已恢复默认关闭。
- 源码提交:
- `21e03d8 feat: add tool gateway protocol`
- 生产 release:
- `20260702-092457-21e03d8`
- 协议:
- `agent-run-v1`
- 默认状态:
- `MEMIND_TOOL_GATEWAY_ENABLED` 未设置
- `MEMIND_TOOL_GATEWAY_DRY_RUN` 未设置
- `toolRuntime.queue.toolGateway.enabled=false`
- `toolRuntime.queue.toolGateway.protocol=agent-run-v1`
- 验证:
- `node --test agent-run-gateway.test.mjs tool-gateway.test.mjs`: `15 passed`
- `npm run build:portal-runtime`: passed
- `https://mm.tkmind.cn/api/runtime/status`: `ok=true`
- `scripts/check-tool-runtime.mjs`: `ok=true`
- `scripts/runtime-slo-report.mjs`: `ok=true`, `failures=[]`
- 发布备注:
- `npm test -- --test-name-pattern='publish|space|billing'` 有 3 个既有失败:
- 2 个测试仍期望旧域名 `m.tkmind.cn`,但当前生产基线是 `mm.tkmind.cn`
- 1 个测试期望 executor command 缺失,但本机当前已有对应命令
- 因失败项与 P6.3 无关,发布使用 `--skip-tests`,仍执行构建和 publish guard。
- 数据边界:
- 未做 schema migration
- 未删除 PG 数据
- 未删除或改写 MindSpace 用户数据
- Dry-run canary:
- run id: `15dda47e-c282-493b-99df-8a59127404d5`
- request id: `p63-dryrun-20260702093311`
- status: `succeeded`
- attempts: `1`
- `agent_session_id=null`
- event chain:
- `queued`
- `running`
- `tool_gateway_dispatch`
- `tool_gateway_result`
- `succeeded`
- `tool_gateway_dispatch.protocol=agent-run-v1`
- `tool_gateway_result.dryRun=true`
- `tool_gateway_result.executor=aider`
- temporary login session revoked
- final state restored:
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue.toolGateway.enabled=false`
- `.env` has no `MEMIND_TOOL_GATEWAY_*`
- `.env` has no `MEMIND_AGENT_CODE_RUNS_*`
- SLO `ok=true`, `failures=[]`
### P6.4 Tool Gateway Real Canary
结果: 通过,Aider 真实执行 canary 已修复并通过。
- 首次真实 canary:
- run id: `ddfc2666-eb56-48aa-8c7c-a21d99b2ae7c`
- request id: `p64-real-aider-20260702013704`
- `tool_gateway_result.dryRun=false`
- `tool_gateway_result.executor=aider`
- `tool_gateway_result.exitCode=0`
- 发现问题: Aider/LiteLLM 不接受裸 `deepseek-reasoner`,需要 `deepseek/deepseek-reasoner`
- 产物未生成,真实产物 canary 未通过。
- 修复:
- commit: `0cfd2d3 fix: map deepseek models for aider`
- release: `20260702-094216-0cfd2d3`
- `custom_deepseek` + Aider 自动映射为 `deepseek/<model>`
- related tests: `43 passed`
- 发布运行态:
- 发布脚本在 legacy `m.tkmind.cn` tunnel 检查处返回 1。
- `mm.tkmind.cn` 主路径和 Portal runtime 已正常。
- 已清理占用 8081 的孤儿 Portal node,让 LaunchAgent 接管。
- final Portal listener: pid `86462`, port `8081`
- 第二次真实 canary:
- canary dir: `/Users/john/Project/memind_canary/tool-gateway-p64-fixed-20260702-094739`
- run id: `7daff187-1f60-4e67-9848-a404274a0455`
- request id: `p64-real-aider-fixed-20260702014740`
- status: `succeeded`
- attempts: `1`
- `agent_session_id=null`
- `tool_gateway_dispatch.protocol=agent-run-v1`
- `tool_gateway_result.dryRun=false`
- `tool_gateway_result.executor=aider`
- `tool_gateway_result.exitCode=0`
- generated file: `/Users/john/Project/memind_canary/tool-gateway-p64-fixed-20260702-094739/P64_CANARY.md`
- generated content: `P6.4 real Tool Gateway canary succeeded.`
- Final state:
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue.toolGateway.enabled=false`
- all workers `heartbeatOk=true`
- SLO `ok=true`, `failures=[]`
- Data boundary:
- no schema migration
- no PG data deletion
- no production MindSpace file deletion or rewrite
- real file writes limited to `/Users/john/Project/memind_canary/...`
### P6.5 OpenHands Real Canary
结果: 通过,OpenHands 真实执行 canary 已通过。
- OpenHands:
- CLI: `1.16.0`
- SDK: `1.21.0`
- Launch:
- command: `openhands`
- args: `--headless --json --override-with-envs --task`
- env from executor binding:
- `LLM_MODEL=deepseek-reasoner`
- `LLM_BASE_URL=https://api.deepseek.com/v1`
- canary dir:
- `/Users/john/Project/memind_canary/tool-gateway-p65-openhands-20260702-095402`
- run id:
- `0b1bb7a0-d111-44c1-91b5-4f14b2f9c01c`
- request id:
- `p65-real-openhands-20260702015402`
- status:
- `succeeded`
- attempts: `1`
- `agent_session_id=null`
- duration:
- about `206s`
- event chain:
- `queued`
- `running`
- `tool_gateway_dispatch`
- `tool_gateway_result`
- `succeeded`
- `tool_gateway_dispatch.protocol=agent-run-v1`
- `tool_gateway_dispatch.taskType=repo_refactor`
- `tool_gateway_result.dryRun=false`
- `tool_gateway_result.executor=openhands`
- `tool_gateway_result.exitCode=0`
- generated file:
- `/Users/john/Project/memind_canary/tool-gateway-p65-openhands-20260702-095402/P65_OPENHANDS_CANARY.md`
- generated content:
- `P6.5 OpenHands Tool Gateway canary succeeded.`
- Final state:
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue.toolGateway.enabled=false`
- all workers `heartbeatOk=true`
- SLO `ok=true`, `failures=[]`
- Data boundary:
- no schema migration
- no PG data deletion
- no production MindSpace file deletion or rewrite
- real file writes limited to `/Users/john/Project/memind_canary/...`
### P6.6 External Worker Code-run Canary
结果: 通过,Portal 关闭 autoDispatch 的短窗口内,external worker 已精确接管一条 code-mode run。
- 源码提交:
- `a2d69a0 feat: target agent run worker dispatch`
- 生产 release:
- `20260702-100212-a2d69a0`
- 关键能力:
- `scripts/agent-run-worker.mjs --run-id <id>` 可精确处理指定 run
- canary 可通过 `MEMIND_AGENT_RUN_WORKDIR_OVERRIDE` 写入专用目录
- Portal 短窗口:
- `MEMIND_AGENT_RUN_AUTODISPATCH=0`
- `MEMIND_AGENT_CODE_RUNS_ENABLED=1`
- user allowlist 仅含 `a6fb1e97-2b0f-447b-b138-4561d8e5c53e`
- 外部 worker:
- `MEMIND_TOOL_GATEWAY_ENABLED=1`
- `MEMIND_TOOL_GATEWAY_DRY_RUN=0`
- workdir override: `/Users/john/Project/memind_canary/tool-gateway-p66-worker-20260702-100500`
- run id:
- `94dcca6f-cf32-4fa1-b602-8e6a5bff1e3c`
- request id:
- `p66-worker-aider-20260702100509`
- worker output:
- `ok=true`
- `mode=dispatch`
- `dispatched=1`
- `runId=94dcca6f-cf32-4fa1-b602-8e6a5bff1e3c`
- queue `autoDispatch=false`
- Tool Gateway `enabled=true`
- `dryRun=false`
- event chain:
- `queued`
- `running`
- `tool_gateway_dispatch`
- `tool_gateway_result`
- `succeeded`
- `tool_gateway_result.dryRun=false`
- `tool_gateway_result.executor=aider`
- `tool_gateway_result.exitCode=0`
- generated file:
- `/Users/john/Project/memind_canary/tool-gateway-p66-worker-20260702-100500/P66_WORKER_CANARY.md`
- generated content:
- `P6.6 external worker Tool Gateway canary succeeded.`
- Final state:
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue.autoDispatch=true`
- `toolRuntime.queue.toolGateway.enabled=false`
- all workers `heartbeatOk=true`
- SLO `ok=true`, `failures=[]`
- Data boundary:
- no schema migration
- no PG data deletion
- no production MindSpace file deletion or rewrite
- real file writes limited to `/Users/john/Project/memind_canary/...`
### P6.7 Tool Gateway Production Guardrails
结果: 通过,产物校验/输出审计已发布,生产保持默认关闭。
- 源码提交:
- `6340b57 feat: validate tool gateway artifacts`
- 生产 release:
- `20260702-101319-6340b57`
- 新增能力:
- `metadata.memindRun.validation.expectedFile`
- `metadata.memindRun.validation.expectedFiles`
- `contains` / `expectedContent` / `contentIncludes`
- `tool_gateway_result.stdoutTail`
- `tool_gateway_result.stderrTail`
- `tool_gateway_validation`
- `tool_gateway_validation_failed`
- 失败语义:
- executor exit 0 但 expected file/content 缺失时,run 标记 `failed`
- validation failed 不重试
- 校验路径必须位于 Tool Gateway working directory 内
- 验证:
- `node --test agent-run-gateway.test.mjs tool-gateway.test.mjs`: `17 passed`
- `node --check agent-run-gateway.mjs`: passed
- `npm run build:portal-runtime`: passed
- 发布备注:
- release 首次在已知历史 `duck-industry-report.docx` 缺失链接处停止
- 按用户要求不修复历史用户数据,使用 `ALLOW_MINDSPACE_PUBLIC_LINK_ISSUES=1` 仅跳过该守卫
- release 脚本完成 live 全目录备份和持久目录备份
- Final state:
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue.autoDispatch=true`
- `toolRuntime.queue.toolGateway.enabled=false`
- SLO `ok=true`, `failures=[]`
- Data boundary:
- no schema migration
- no PG data deletion
- no production MindSpace file deletion or rewrite
### P6.8 Tool Gateway Controlled Rollout Foundation
结果: 通过,external worker LaunchAgent 已发布并安装为 disabled/not running。
- 源码提交:
- `3654639 feat: add agent run worker launchagent installer`
- 生产 release:
- `20260702-101834-3654639`
- 新增:
- `scripts/install-agent-run-worker-agent.sh`
- runtime artifact:
- 已包含 `scripts/install-agent-run-worker-agent.sh`
- RUNBOOK 已记录 `bash scripts/install-agent-run-worker-agent.sh`
- LaunchAgent:
- label: `cn.tkmind.memind-agent-run-worker`
- plist: `/Users/john/Library/LaunchAgents/cn.tkmind.memind-agent-run-worker.plist`
- log: `/Users/john/Library/Logs/memind-agent-run-worker.log`
- 安装状态:
- `state=installed-disabled`
- `print-disabled`: disabled
- `launchctl print`: `state = not running`
- no `agent-run-worker.mjs` process
- 默认配置:
- `MEMIND_AGENT_RUN_AUTODISPATCH=0`
- `MEMIND_AGENT_RUN_QUEUE_CONCURRENCY=1`
- `MEMIND_TOOL_GATEWAY_ENABLED=0`
- `MEMIND_TOOL_GATEWAY_DRY_RUN=0`
- 验证:
- `bash -n scripts/install-agent-run-worker-agent.sh`: passed
- `node --check scripts/build-portal-runtime.mjs`: passed
- `node --test agent-run-gateway.test.mjs tool-gateway.test.mjs`: `17 passed`
- `npm run build:portal-runtime`: passed
- Final state:
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue.autoDispatch=true`
- `toolRuntime.queue.toolGateway.enabled=false`
- SLO `ok=true`, `failures=[]`
- Data boundary:
- no schema migration
- no PG data deletion
- no production MindSpace file deletion or rewrite
- external worker not started
### P6.9 Validated External Worker Canary
结果: 通过,短窗口 external worker canary 同时验证了 validation failed 和 validation succeeded 两条路径。
- 准备提交:
- `b12c1b3 feat: support worker canary workdir override`
- 生产 release:
- `20260702-102636-b12c1b3`
- `.env` backup:
- `/Users/john/Project/memind_backups/20260702-1029-p69-validated-worker-canary/.env.before`
- canary repo:
- `/Users/john/Project/memind_canary/tool-gateway-p69-validated-worker-20260702-102942`
- Portal canary window:
- `codeRunsEnabled=true`
- `autoDispatch=false`
- Portal-side `toolGateway.enabled=false`
- worker canary window:
- LaunchAgent label: `cn.tkmind.memind-agent-run-worker`
- `MEMIND_TOOL_GATEWAY_ENABLED=1`
- `MEMIND_AGENT_RUN_WORKDIR_OVERRIDE=/Users/john/Project/memind_canary/tool-gateway-p69-validated-worker-20260702-102942`
- `MEMIND_AGENT_RUN_QUEUE_CONCURRENCY=1`
Failure-path proof:
- run id:
- `1353d917-c03b-4fb8-9acd-739d56984fc5`
- request id:
- `p69-validated-worker-20260702022958`
- result:
- `failed`
- attempts: `1`
- proof:
- `tool_gateway_result.exitCode=0`
- `tool_gateway_validation_failed.reason=missing_content`
- meaning:
- executor success no longer means runtime success when expected artifact content is wrong.
Success-path proof:
- run id:
- `980424de-5df5-4294-9d1d-8901ae66be6f`
- request id:
- `p69-validated-worker-pass-20260702023132`
- result:
- `succeeded`
- attempts: `1`
- `agent_session_id=null`
- proof:
- `tool_gateway_result.exitCode=0`
- `tool_gateway_validation.expectedFiles[0].path=P69_VALIDATED_WORKER_CANARY_PASS.md`
- `tool_gateway_validation.expectedFiles[0].contains=true`
- `tool_gateway_validation.expectedFiles[0].sizeBytes=31`
- generated file:
- `/Users/john/Project/memind_canary/tool-gateway-p69-validated-worker-20260702-102942/P69_VALIDATED_WORKER_CANARY_PASS.md`
- generated content:
- `P69_VALIDATED_WORKER_CANARY_OK`
Final state:
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue.autoDispatch=true`
- `toolRuntime.queue.toolGateway.enabled=false`
- worker LaunchAgent:
- installed disabled
- `state=not running`
- no `agent-run-worker.mjs` process
- temp login sessions:
- `revokedRows=2`
- SLO:
- `ok=true`
- `failures=[]`
- writes all false
Data boundary:
- no schema migration
- no PG data deletion
- no production MindSpace file deletion or rewrite
- canary writes limited to `/Users/john/Project/memind_canary/...`
### P6.10 External Worker Observability
结果: 通过,external worker 只读状态检查已发布。
- 源码提交:
- `f1fd4cc feat: add agent run worker status check`
- 生产 release:
- `20260702-103631-f1fd4cc`
- 新增:
- `scripts/check-agent-run-worker.mjs`
- runtime artifact:
- 已包含 `scripts/check-agent-run-worker.mjs`
- RUNBOOK 已记录 `node scripts/check-agent-run-worker.mjs`
- 只读检查覆盖:
- LaunchAgent plist
- launchctl state
- disabled/enabled
- pid/program/path
- `agent-run-worker.mjs` process list
- queued/running/retryable counts
- oldest pending age
- latest failed run
- 验证:
- `node --check scripts/check-agent-run-worker.mjs`: passed
- `MEMIND_ENV_FILE=/Users/john/Project/Memind/.env node scripts/check-agent-run-worker.mjs`: `ok=true`
- `node --test agent-run-gateway.test.mjs tool-gateway.test.mjs`: `17 passed`
- `npm run build:portal-runtime`: passed
- Final production check:
- `check-agent-run-worker.ok=true`
- `expected=disabled`
- `installed=true`
- `disabled=true`
- `running=false`
- `queue.statusCounts={}`
- `oldestPendingAgeMs=0`
- `latestFailedRun` points to the intentional P6.9 validation failure
- Final runtime:
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue.autoDispatch=true`
- `toolRuntime.queue.toolGateway.enabled=false`
- SLO `ok=true`, `failures=[]`
- Data boundary:
- no schema migration
- no PG data deletion
- no production MindSpace file deletion or rewrite
- new script is read-only
### P6.11 Single-user Rollout Policy
结果: 通过,code-run 长期灰度所需策略门禁已发布,默认关闭。
- 源码提交:
- `212e163 feat: add code run rollout policy gates`
- 生产 release:
- `20260702-104734-212e163`
- 新增策略:
- `MEMIND_AGENT_CODE_RUNS_ENABLED`
- `MEMIND_AGENT_CODE_RUNS_USER_IDS`
- `MEMIND_AGENT_CODE_RUN_TASK_TYPES`
- `MEMIND_AGENT_CODE_RUNS_REQUIRE_VALIDATION`
- 入口 gate:
- 非用户 allowlist: `403 当前用户未开启代码任务灰度`
- 非 task type allowlist: `403 当前代码任务类型未开启灰度`
- 缺少 required validation: `400 代码任务必须声明产物校验规则`
- runtime/status:
- `toolRuntime.codeRunPolicy.enabled`
- `toolRuntime.codeRunPolicy.userAllowlist`
- `toolRuntime.codeRunPolicy.taskTypeAllowlist`
- `toolRuntime.codeRunPolicy.requireValidation`
- 验证:
- `node --test agent-run-routes.test.mjs agent-run-gateway.test.mjs tool-gateway.test.mjs`: `31 passed`
- `node --check agent-run-routes.mjs`: passed
- `node --check server.mjs`: passed
- `npm run build:portal-runtime`: passed
- 发布备注:
- release 脚本在 legacy `m.tkmind.cn` tunnel step 短暂返回失败。
- Portal runtime 已切换,`mm.tkmind.cn` 主路径健康。
- 后续检查显示 `cn.tkmind.memind-portal-tunnel` 已恢复 running。
- 当前 H5 主路径仍为 `https://mm.tkmind.cn`,不依赖 105 转发。
- Final runtime:
- `toolRuntime.codeRunPolicy.enabled=false`
- `toolRuntime.codeRunPolicy.userAllowlist=[]`
- `toolRuntime.codeRunPolicy.taskTypeAllowlist=[]`
- `toolRuntime.codeRunPolicy.requireValidation=false`
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue.autoDispatch=true`
- `toolRuntime.queue.toolGateway.enabled=false`
- Final checks:
- `node scripts/check-agent-run-worker.mjs`: `ok=true`, worker disabled/not running
- SLO `ok=true`, `failures=[]`, writes all false
- Data boundary:
- no schema migration
- no PG data deletion
- no production MindSpace file deletion or rewrite
- no real-user code run enabled
### P6.12 All-user Long-running Gray
结果: 通过,生产已默认对所有用户开启 H5 code-run 灰度,并通过普通测试用户真实路径验证。
- 生产变更:
- `MEMIND_AGENT_CODE_RUNS_ENABLED=1`
- `MEMIND_AGENT_CODE_RUNS_USER_IDS=`,空值表示所有用户。
- `MEMIND_AGENT_CODE_RUN_TASK_TYPES=`,空值表示所有任务类型。
- `MEMIND_AGENT_CODE_RUNS_REQUIRE_VALIDATION=0`,兼容 H5 默认 code-run payload;带 validation 的任务仍会校验。
- `MEMIND_AGENT_RUN_AUTODISPATCH=0`
- external worker `cn.tkmind.memind-agent-run-worker` enabled/running。
- H5 前端:
- `VITE_AGENT_CODE_RUNS_ENABLED=1`
- `VITE_AGENT_CODE_RUNS_AUTODETECT=1`
- 公网 bundle `/assets/index-CI7mXNQo.js`
- 备份:
- `/Users/john/Project/memind_backups/20260702-111654-all-user-code-run-gray`
- 普通用户验证:
- user `gray20260702112309`
- user id `b4561354-4273-4900-9ada-b6c52d2c5836`
- run id `aa0d1ac1-c03b-4110-baea-6cac83556567`
- request id `gray-code-20260702112309-login`
- status `succeeded`
- attempts `1`
- 事件链:
- `queued`
- `running`
- `tool_gateway_dispatch`
- `tool_gateway_result`
- `succeeded`
- Tool Gateway:
- executor `aider`
- `dryRun=false`
- working dir `/Users/john/Project/Memind/MindSpace/b4561354-4273-4900-9ada-b6c52d2c5836`
- 产物:
- `/Users/john/Project/Memind/MindSpace/b4561354-4273-4900-9ada-b6c52d2c5836/public/gray-code-run-20260702112309.html`
- content `GRAY_CODE_RUN_20260702112309`
- 观察:
- `check-agent-run-worker`: `ok=true`, worker running, queue empty
- SLO: `ok=true`, `failures=[]`
- Data boundary:
- no schema migration
- no PG data deletion
- no existing production MindSpace deletion or rewrite
- real writes limited to a new test user's MindSpace
### P6.13 Agent Run Auto-pause Guard
结果: 通过,自动暂停 guard 已发布到生产 runtime 并安装为 LaunchAgent。
- 新增源码:
- `scripts/agent-run-guard.mjs`
- `scripts/install-agent-run-guard-agent.sh`
- `docs/agent-run-worker-rollout-runbook.md`
- `scripts/build-portal-runtime.mjs` runtime copy list updated
- 生产安装:
- label `cn.tkmind.memind-agent-run-guard`
- interval `60s`
- log `/Users/john/Library/Logs/memind-agent-run-guard.log`
- 默认阈值:
- failed runs in 10 minutes `>= 3`
- oldest queued/retryable age `>= 300000ms`
- queued + retryable count `>= 10`
- oldest running age `>= 900000ms`
- 触发动作:
- backup `.env`
- set `MEMIND_AGENT_CODE_RUNS_ENABLED=0`
- set `MEMIND_AGENT_RUN_AUTODISPATCH=1`
- stop and disable `cn.tkmind.memind-agent-run-worker`
- kickstart `cn.tkmind.memind-portal`
- 备份:
- `/Users/john/Project/memind_backups/20260702-113106-agent-run-guard-install`
- 验证:
- `node scripts/agent-run-guard.mjs --dry-run`: `ok=true`, `shouldPause=false`
- guard LaunchAgent enabled, last exit code `0`
- worker remains running
- all-user code-run gate remains enabled
- SLO `ok=true`, `failures=[]`
- Data boundary:
- no schema migration
- no PG data deletion
- no production MindSpace file deletion or rewrite
### 入口与 Gateway
结果: 通过。
- `https://mm.tkmind.cn/api/status` 返回 `ok`
- `H5_PUBLIC_BASE_URL=https://mm.tkmind.cn`
- `m.tkmind.cn` 已作为 legacy/rollback 记录,不作为后续 H5 主路径。
- nginx `mm.tkmind.cn` 已对 SSE 路由禁用 buffering/cache/gzip,并设置长 read/send timeout。
- SSE route:
- `/api/sessions/<id>/events`
- `/api/agent/runs/<id>/events`
- `x-accel-buffering: no` 检查通过。
评价:
- Gateway 层已符合 stream-first 的基本要求。
- 非 SSE 普通 route 仍走统一 `location /`,当前足够。
- 后续可加 nginx 级别 request id 和 upstream timing log,便于首 token 延迟统计。
### Stream Controller
结果: 通过。
- `scripts/check-stream-runtime.mjs` 返回 `ok=true`
- SSE HEAD 探测通过,不会打开真实上游 stream。
- Portal 对 session events 和 agent run events 均具备 no-buffering 响应头。
- Session SSE 已记录 worker 级 first-token latency EWMA。
- Session SSE 已记录最近 5 分钟/1 小时 first-token p50/p95。
评价:
- 已完成 P0/P3.5 级别能力。
- 当前已有 first-token EWMA、p50/p95 窗口趋势和 stream lifecycle counters。
- 后续建议增加日报落盘或更长周期趋势,不进 PG。
### Redis Router / Control Plane
结果: 通过。
- Redis 容器 `memind-runtime-redis` 正常运行。
- `MEMIND_RUNTIME_REDIS_URL=redis://127.0.0.1:6379/0`
- namespace: `memind:runtime`
- `/api/runtime/status.router.enabled=true`
- worker 状态可见:
- `activeStreams`
- `streamOpenCount`
- `streamAbortCount`
- `streamErrorCount`
- `score`
- `drain`
评价:
- Control Plane 已实际承担 runtime state。
- P5.8 已安装 LaunchAgent,每 60 秒写入 worker metricsRouter 不再只靠请求流量刷新 worker 状态。
- 当前仍不是 worker self-heartbeat,后续可以改为 sidecar 或 worker 内部心跳。
### Goose Worker Pool
结果: 通过。
四个 target 均 healthy:
- `https://127.0.0.1:18006`
- `https://127.0.0.1:18007`
- `https://127.0.0.1:18008`
- `https://127.0.0.1:18009`
评价:
- Worker pool 已可水平调度,并具备 drain 运维入口。
- 当前调度 scoring 已接入 CPU / memory / fd pressure 的采样指标。
- first-token latency 已接入 Redis EWMA,并参与 Router score。
- first-token p50/p95 已按 5 分钟和 1 小时窗口聚合。
### Runtime Metrics / SLO
结果: 通过。
- 已安装 LaunchAgent `cn.tkmind.memind-runtime-metrics`
- 默认每 60 秒执行 `scripts/runtime-worker-metrics.mjs sample`
- 已安装 LaunchAgent `cn.tkmind.memind-runtime-heartbeat`
- 默认每 15 秒执行 worker `/status` heartbeat sidecar。
- `launchctl print` 显示最近运行退出码为 `0`
- `scripts/runtime-slo-report.mjs` 返回:
- `ok=true`
- `failures=[]`
- 四个 worker `metricsFresh=true`
- 已展示 `ewmaFirstTokenMs``lastFirstTokenMs``firstTokenCount`
- 已展示 `firstToken5m``firstToken1h`
- 已展示 `toolQueue`
- 已展示 `heartbeatSource=sidecar`
- 已展示 `heartbeatFresh=true`
- 已展示 `heartbeatOk=true`
- `writes.database=false`
- `writes.mindSpace=false`
- `writes.redis=false`
- 生产已观测到真实样本:
- `goosed-4.ewmaFirstTokenMs=125`
- `goosed-4.firstTokenCount=1`
- `goosed-4.firstToken5m.p50Ms=188`
- `goosed-4.firstToken5m.p95Ms=188`
评价:
- Observability 从人工检查推进到定时采样 + 一键 SLO 快照。
- SLO 报告会读取 PG 表行数和 MindSpace 文件统计,但不写入、不删除用户数据。
- 已有 Redis rolling samples、即时窗口趋势和每日自动 JSON/Markdown SLO 日报。
- P5.13 已给日报目录增加 30 天保留策略:
- LaunchAgent 参数包含 `--prune --retention-days 30`
- 只清理 `reports/runtime-slo` 顶层过期 `.json/.md`
- 最新报告 `reportPrune.errors=[]`
- P5.14 已给四个 goosed worker 增加常驻 sidecar heartbeat:
- `goosed-1`: `statusCode=200`, `latencyMs=62`
- `goosed-2`: `statusCode=200`, `latencyMs=46`
- `goosed-3`: `statusCode=200`, `latencyMs=46`
- `goosed-4`: `statusCode=200`, `latencyMs=53`
### Tool Runtime / Aider / OpenHands
结果: 通过。
- `scripts/check-tool-runtime.mjs` 返回 `ok=true`
- `role=user` 默认:
- `aider=false`
- `openhands=false`
- 显式白名单用户数: 2。
- 白名单用户:
- chat mode 不注入 Aider/OpenHands。
- code mode 可注入 Aider/OpenHands。
- `toolRuntime.chatInjectsCodeTools=false`
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.queue` 已上线:
- `autoDispatch=true`
- `maxConcurrentRuns=1`
- `runTimeoutMs=900000`
- `inFlight=0`
- `pendingDispatches=0`
- `scripts/agent-run-worker.mjs --status` 已可在生产 runtime 中独立运行。
- `scripts/agent-run-worker.mjs --once` 已完成真实 chat-mode canary:
- `8e64ff4b-c043-475e-b60b-ae7466111da9` -> `succeeded`
评价:
- 已完成高风险工具从普通聊天默认能力中的隔离。
- 后端 `MEMIND_AGENT_CODE_RUNS_ENABLED` 默认关闭,防止客户端绕过前端灰度。
- `/agent/runs` 已具备最小 queue gateway: 全局并发限制、运行超时、runtime/SLO 可观测状态。
- 外部 worker 接管入口已具备,并通过短窗口真实 canary 验证。
- 生产已恢复 Portal 内部 `autoDispatch=true`
- Aider/OpenHands 仍是 session extension 形态,尚未拆成独立工具进程协议。
### H5 Code Run 入口
结果: 后端 canary 与 H5 UI canary 均已通过,默认关闭。
- H5 bundle 已包含 `/agent/runs``tool_mode` 入口。
- `VITE_AGENT_CODE_RUNS_ENABLED` 未设置。
- `VITE_AGENT_CODE_RUNS_AUTODETECT` 未设置。
- `VITE_AGENT_CODE_RUNS_USER_IDS` 未设置。
- `MEMIND_AGENT_CODE_RUNS_ENABLED` 未设置。
- `MEMIND_AGENT_CODE_RUNS_USER_IDS` 未设置。
- `scripts/check-agent-code-run-entry.mjs` 返回 `ok=true`
- P4.10 后端短窗口 code-mode canary 已成功:
- run id: `805e7422-175e-4eb7-8fb2-df0c29f79c0c`
- session id: `20260702_3`
- final status: `succeeded`
- P4.11 已发布默认关闭 runtime:
- release id: `20260702-085001-a0af049`
- `toolRuntime.codeRunsEnabled=false`
- `toolRuntime.chatInjectsCodeTools=false`
- 发布脚本已支持把 `ALLOW_MINDSPACE_PUBLIC_LINK_ISSUES=1` 传到远端 post-check。
- 已按用户要求忽略既有生产 MindSpace 公开页缺失 docx 附件链接,不修复。
- P4.12 H5 页面编辑 UI canary 已完成:
- canary release: `20260702-085319-a0af049`
- canary run id: `825720c9-330d-447d-a6ed-afeabbf6e14c`
- canary session id: `20260702_4`
- final status: `succeeded`
- attempts: `1`
- restored release: `20260702-085706-a0af049`
- restored `toolRuntime.codeRunsEnabled=false`
- restored `.env``MEMIND_AGENT_CODE_RUNS_*`
- 脚本明确:
- `writes.database=false`
- `writes.mindSpace=false`
- `createsAgentRun=false`
评价:
- 灰度链路已经有前后端双门禁,并补齐用户级白名单。
- 当前生产不会自动启用 code run。
- 当前已恢复默认关闭;后续如要放量,可只对白名单重新打开,不开普通聊天 autodetect。
### Persistence / PG / MindSpace
结果: 通过。
- 本轮 P4.6-P6.2/P4.10 未做 schema migration。
- 本轮未删除 PG 数据。
- 本轮未删除 MindSpace 数据。
- P5.8 SLO 报告只读统计 PG 和 MindSpace。
- P5.13 SLO 报告保留策略只清理运维报告目录。
- P6.2/P4.10 仅新增 canary run/session/event 审计记录。
- 当前 `MindSpace`:
- `/Users/john/Project/Memind/MindSpace`: `files=2143`, `dirs=1595`, `bytes=122160654`
- `/Users/john/Project/Memind/data/mindspace`: `files=5281`, `dirs=7651`, `bytes=270214686`
- 已有生产备份:
- `20260702-065813-pre-p35-p55`
- `20260702-071604-pre-p46-code-task-entry`
- `20260702-071914-pre-p47-h5-code-run-entry`
- `20260702-072233-pre-p48-code-run-check`
- `20260702-072452-pre-p49-code-run-server-gate`
- `20260702-073927-pre-p58-runtime-slo-agent`
- `20260702-074955-pre-p60-tool-queue-v0`
- `20260702-075904-pre-p61-agent-run-worker-v1`
- `20260702-081928-pre-p62-tool-worker-canary`
- `20260702-083225-pre-p410-code-run-canary`
- `20260702-083917-pre-p513-slo-prune`
- `memind-full-20260702-084418-c7d8140-before.tar.gz`
- `memind-persisted-20260702-084418-c7d8140-before.tar.gz`
- `memind-full-20260702-085001-a0af049-before.tar.gz`
- `memind-persisted-20260702-085001-a0af049-before.tar.gz`
- `20260702-085302-pre-p412-h5-ui-canary`
- `memind-full-20260702-085319-a0af049-before.tar.gz`
- `memind-persisted-20260702-085319-a0af049-before.tar.gz`
- `memind-full-20260702-085706-a0af049-before.tar.gz`
- `memind-persisted-20260702-085706-a0af049-before.tar.gz`
- `memind-full-20260702-090343-e24e6ca-before.tar.gz`
- `memind-persisted-20260702-090343-e24e6ca-before.tar.gz`
评价:
- 数据保护执行良好。
- 建议每次进入“会创建真实 agent run 的 canary”前,再做一次轻量 DB/MindSpace 快照或至少记录 row/file count baseline。
### Source Sync / Remote Dev
结果: 通过。
- 干净分支当前 clean。
- 分支已推送:
- `memind-streaming-runtime-20260702`
- 远程开发机同步命令:
- `git pull origin memind-streaming-runtime-20260702`
评价:
- 已满足“远程开发电脑保持一致、后续直接拉分支”的要求。
- 生产 runtime 是单文件 artifact,源码分支保留 `.runtime/portal/server.mjs` 便于复现生产部署。
## 当前架构完成度
| 模块 | 完成度 | 状态 |
| --- | ---: | --- |
| Edge Gateway / mm domain | 85% | 已切主路径,SSE 配置正确 |
| Stream Controller | 86% | pipeline 已落地,first-token EWMA 和 p50/p95 已接入 |
| Redis Control Plane | 82% | runtime state 已迁移,worker sidecar heartbeat 已上线 |
| Worker Pool | 86% | 四 worker healthydrain 可用,CPU/memory/fd 指标和主动 heartbeat 已接入 |
| Tool Isolation | 99% | chat/code 隔离完成,queue gateway v0、外部 worker 入口、真实 canary、`agent-run-v1` 协议、Aider/OpenHands 真实 canary、产物校验、worker LaunchAgent、validated worker canary、worker check、rollout gates、全用户灰度和自动暂停 guard 已完成 |
| H5 Code Run Canary | 99% | 后端 code-mode canary、用户级 gate、H5 页面编辑 UI canary、external worker 精确接管 canary、guardrails、validated worker canary、rollout policy、全用户真实路径验证已完成,当前生产处于 all-user gray |
| Persistence Boundary | 85% | PG/MindSpace 保护良好,SLO 脚本只读验证 |
| Observability | 96% | 定时 metrics、sidecar heartbeat、SLO 快照/日报/30天保留、first-token EWMA 与 p50/p95 已落地 |
| Remote Sync | 90% | 干净分支已稳定推送 |
## 风险清单
1. activeStreams 可能残留。
- 影响: score 会偏高,长期可能影响调度。
- 状态: P5.15 Active Stream TTL Reconcile 已按用户要求跳过,暂不做报表/定时 reconcile。
2. Tool Gateway 已进入全用户长期灰度,需持续观察。
- 影响: code-run 现在会对所有 H5 用户自动启用;external worker 单并发处理,guard 会在失败或积压超过阈值时自动暂停。
- 状态: P6.12/P6.13 已完成;当前 `codeRunsEnabled=true``autoDispatch=false`、external worker running、guard enabled。
- 建议: 保持 30-60 分钟密集观察,然后转为日报/SLO + guard 常态观察。
3. 生产 MindSpace 存在既有公开页缺失 docx 附件链接。
- 影响: 已按用户要求作为历史内容问题忽略,不阻断 streaming runtime 发布。
- 建议: 不在本轮修复;后续只有用户重新要求时再单独处理。
### P6.15 H5 Code-run Validation Metadata
结果: 通过,H5 默认 code-run 已自动补 receipt validation,生产后端 required validation 已恢复。
- 前端自动生成 `.memind/agent-runs/<requestId>.json` receipt validation。
- `createAgentRun()` 会把 validation 写入 `metadata.memindRun.validation`,并给执行器追加 agent-visible receipt 指令。
- 生产已恢复 `MEMIND_AGENT_CODE_RUNS_REQUIRE_VALIDATION=1`
- 普通测试用户真实 run 已成功:
- run id `5eb28014-b376-4a0c-b42a-8dd38f781f4f`
- request id `p615-validation-20260702034524`
- attempts `1`
- event chain 包含 `tool_gateway_validation`
- SLO `ok=true``failures=[]`guard `shouldPause=false`
### P6.17 / P6.18 Worker Concurrency 2
结果: 通过,两条真实 code-run 并发执行成功,并发 2 已成为当前 all-user gray 生产策略。
- worker LaunchAgent:
- `MEMIND_AGENT_RUN_QUEUE_CONCURRENCY=2`
- `MEMIND_AGENT_RUN_WORKER_BATCH_SIZE=2`
- args include `--limit 2`
- `KeepAlive=true`
- Portal runtime/status:
- `toolRuntime.queue.maxConcurrentRuns=2`
- canary:
- run `d064c8c0-b061-4df7-8c9b-5dfa315271ea`
- run `64717bcc-4065-4947-a0d3-a706f3b62640`
- both `succeeded`, attempts `1`
- both include `tool_gateway_validation`
- started timestamps differ by `51ms`, proving concurrent dispatch.
- post-check:
- worker running
- queue empty
- guard `shouldPause=false`
- SLO `ok=true`, `failures=[]`
- observation:
- Portal had one restart-window `read EADDRNOTAVAIL`, then LaunchAgent recovered and status returned `ok`.
### P8.1 Queue Lease / Stuck Run Recovery
结果: 通过,worker dispatch 前会自动回收超过 timeout 的 running run,并提供手动 dry-run/apply 运维入口。
- 新增 `recoverStaleRunningRuns()`
- `dispatchQueuedRuns()` 自动执行 stale recovery。
- `agent-run-worker.mjs` 新增:
- `--recover-stale`
- `--apply-recovery`
- `--stale-ms`
- `check-agent-run-worker.mjs` 和 runtime/status queue 摘要新增 running age。
- 测试:
- 29 tests pass。
- stale dry-run 不改状态。
- stale apply 标记 failed 并写 `stale_recovered`
- 生产 synthetic stale run 验证:
- run id `9e22ad3d-ad5d-42b5-9ce3-24fe7aace3f5`
- status `failed`
- event `stale_recovered`
- post-check:
- queue empty
- worker running `--limit 2`
- guard `shouldPause=false`
- SLO `ok=true`, `failures=[]`
### P8.2 Portal DB/Auth Transient Error Hardening
结果: 通过,session attach、`/auth/status` 和 API auth middleware 已捕获 DB/auth 瞬时错误并收敛为 503,不再让异常穿透 Express middleware。
- `attachUserSession()` 捕获 `userAuth.verify()` 异常,记录 `req.userSessionError`
- `/auth/status` 捕获用户态查询/能力解析异常,返回 `503 mode=unavailable`
- API auth middleware:
- protected route 遇到 session verify transient error 返回 503。
- plaza public read 在 session attach 失败时继续放行。
- 后续 `getMe()` transient error 返回 503。
- 测试:
- `node --check server.mjs`
- `node --test auth.test.mjs user-auth.test.mjs admin-guard.test.mjs`23 tests pass。
- 生产部署:
- backup `/Users/john/Project/memind_backups/20260702-121438-p82-auth-transient-hardening`
- bundled `server.mjs`
- restarted `cn.tkmind.memind-portal`
- live 验证:
- `/api/status` 返回 `ok`
- `/auth/status` 未登录返回 `200 {"authenticated":false,"mode":"user"}`
- runtime/status 仍为 all-user code-run gray、`requireValidation=true``maxConcurrentRuns=2`
- worker running `--limit 2`
- guard `shouldPause=false`
- SLO `ok=true`, `failures=[]`
### P6.19 Task-level Artifact Validation
结果: 通过,H5 默认 code-run 在 receipt 之外追加 task-level expectedFiles,生产真实 canary 已验证三项产物校验同时通过。
- receipt 继续作为兜底:
- `.memind/agent-runs/<requestId>.json`
- 普通 H5 code-run:
- 从用户输入、MindSpace 上下文和公开链接中提取 `public/*.html`
- 将可推断的目标 HTML 文件追加到 `metadata.memindRun.validation.expectedFiles`
- 页面编辑 code-run:
- 传入 `pageId``pageTitle` 和 MindSpace context。
- 追加 `.memind/agent-runs/<requestId>-page-edit.json`,内容必须包含 `pageId`
- 测试:
- `npm run build`
- `node --test agent-run-gateway.test.mjs agent-run-routes.test.mjs`29 tests pass。
- `npx tsc --noEmit` 仍被既有历史 TS 问题阻断,本次相关返回类型已收紧。
- 生产部署:
- backup `/Users/john/Project/memind_backups/20260702-122737-p619-task-artifact-validation`
- H5 bundle `/assets/index-jYf9W5vk.js`
- real canary:
- run `60067d2f-d2af-40d1-bf35-7976309bd8da`
- request `p619-task-validation-20260702042911`
- status `succeeded`
- event `tool_gateway_validation`
- expectedFiles:
- `.memind/agent-runs/p619-task-validation-20260702042911.json`
- `public/p619-task-validation-20260702042911.html`
- `.memind/agent-runs/p619-task-validation-20260702042911-page-edit.json`
- post-check:
- runtime/status all-user gray unchanged
- guard `shouldPause=false`
- `failedRecentCount=0`
### P8.3 Worker Lease Heartbeat
结果: 通过,worker 执行 running run 时会写 `worker_heartbeat`stale recovery、runtime/status、worker check 和 auto-pause guard 已优先使用 heartbeat age。
- `agent-run-gateway.mjs`:
- 新增 `MEMIND_AGENT_RUN_HEARTBEAT_MS`,默认 `30000`
- running 后立即写 `worker_heartbeat`,再按 interval 定时写入。
- terminal 后停止 heartbeat timer。
- stale recovery 使用 `COALESCE(latest worker_heartbeat, started_at)`
- recovery UPDATE 会复核最新 heartbeat,避免误杀刚刷新 heartbeat 的 run。
- runtime/status queue 摘要新增:
- `heartbeatMs`
- `oldestRunningHeartbeatAt`
- `oldestRunningHeartbeatAgeMs`
- `runningWithoutHeartbeatCount`
- `latestRunningRun`
- `agent-run-guard.mjs`:
- running guard 改用 `oldestRunningHeartbeatAgeMs`
- `check-agent-run-worker.mjs`:
- 增加 heartbeat queue 摘要。
- 测试:
- `node --check agent-run-gateway.mjs scripts/agent-run-worker.mjs scripts/agent-run-guard.mjs scripts/check-agent-run-worker.mjs`
- `node --test agent-run-gateway.test.mjs agent-run-routes.test.mjs`31 tests pass。
- 生产部署:
- backup `/Users/john/Project/memind_backups/20260702-123510-p83-worker-heartbeat`
- restarted Portal and worker
- production `.env` added `MEMIND_AGENT_RUN_WORKER_EXPECT_RUNNING=1`
- synthetic recovery canary:
- no-heartbeat run `042276ae-ecac-4b7c-9c53-c02154ea85cd` recovered to `failed`
- fresh-heartbeat run `97a2ac16-0234-483d-be4f-836ba95a91e7` was not recovered and was deleted after verification
- real worker canary:
- run `ad2894a7-dc02-49c6-a859-da6af95cb143`
- request `p83-heartbeat-real-20260702043642`
- status `succeeded`
- event chain includes `worker_heartbeat` before `tool_gateway_dispatch`
- heartbeat data `pid=31341`, `attempt=1`, `heartbeatMs=30000`
- post-check:
- runtime/status queue empty
- `runningWithoutHeartbeatCount=0`
- worker check `ok=true`, `expected=running`
- guard `shouldPause=false`
### P8.4 Long-running Gray Soak and Heartbeat SLO
结果: 通过,runtime SLO JSON/Markdown 已加入 agent-run heartbeat 摘要和 failure 条件,生产只读、临时报表写入与小时级 soak 快照安装验证均通过。
- `runtime.toolQueueSlo` 新增:
- `heartbeatMs`
- `maxConcurrentRuns`
- `statusCounts`
- `oldestRunningStartedAt`
- `oldestRunningAgeMs`
- `oldestRunningHeartbeatAt`
- `oldestRunningHeartbeatAgeMs`
- `runningWithoutHeartbeatCount`
- `latestRunningRun`
- failure 条件新增:
- `agent_run_heartbeat_stale_<age>`
- `agent_run_missing_heartbeat_<count>`
- Markdown Tool Queue 段落新增 heartbeat summary。
- 测试:
- `node --check scripts/runtime-slo-report.mjs`
- 源码目录只读 SLO `ok=true`, `failures=[]`
- 生产部署:
- backup `/Users/john/Project/memind_backups/20260702-123947-p84-heartbeat-slo`
- deployed `scripts/runtime-slo-report.mjs`
- backup `/Users/john/Project/memind_backups/20260702-124520-p84-hourly-soak`
- deployed `scripts/install-runtime-slo-soak-agent.sh`
- no service restart
- 生产验证:
- production read-only SLO `ok=true`, `failures=[]`
- `toolQueueSlo.heartbeatMs=30000`
- `toolQueueSlo.statusCounts={}`
- `toolQueueSlo.runningWithoutHeartbeatCount=0`
- temporary JSON/Markdown report files include heartbeat summary fields
- 定时器:
- `cn.tkmind.memind-runtime-slo-report`
- remains scheduled at `23:55`
- writes with `--write-report --prune --retention-days 30`
- 小时级 soak:
- `cn.tkmind.memind-runtime-slo-soak`
- `StartInterval=3600`
- writes `reports/runtime-slo-soak`
- retention `7` days
- first snapshot `ok=true`, `failures=[]`
## 下一步执行建议
### P6.22 Task Artifact UX and Failure Messages
- 用户侧显示 queued/running/validation/succeeded/failed。
- 失败原因分层展示。
- 用户已决定暂时跳过,不进入下一步开发。
### P8.4 Long-running Gray Soak and Heartbeat SLO
- 第一阶段 SLO 字段已完成。
- 第二阶段安装独立小时级 soak 快照,不扩大并发。
- 下一步是观察 24h 报告,再决定是否进入更细任务类型 validation 或并发策略调整。